import { db } from "@backend/db";
import type {
	AuthenticationResponseJSON,
	PublicKeyCredentialCreationOptionsJSON,
	PublicKeyCredentialRequestOptionsJSON,
} from "@simplewebauthn/server";
import {
	generateAuthenticationOptions,
	generateRegistrationOptions,
	verifyAuthenticationResponse,
	verifyRegistrationResponse,
} from "@simplewebauthn/server";
import { newID } from "utils/id";

// import type { itemSchema, paymentSchema } from "@backend/payments/payments.schema";
// import type { PaymentRow } from "@backend/payments/payments.types";

// type Payment = typeof paymentSchema.static;
// type Item = typeof itemSchema.static;

const rpName = "dela";
// todo:check domain name stuff with allowed origins
// const rpID = "rcastellotti.dev";
const rpID = "localhost";
// todo: this should prolly only contain one and should be set based on the env
const expectedOrigins = ["http://localhost:5173", "https://dev.dela.rcastellotti.dev", "https://dela.rcastellotti.dev"];

export async function signup(username: string): Promise<PublicKeyCredentialCreationOptionsJSON> {
	const userID = crypto.getRandomValues(new Uint8Array(32));

	const options = await generateRegistrationOptions({
		rpName: rpName,
		rpID: rpID,
		userID: userID,
		userName: username,
		authenticatorSelection: {
			residentKey: "required",
			requireResidentKey: true,
			userVerification: "required",
		},
		attestationType: "none",
	});

	const insertUser = db.prepare(`
    INSERT INTO users
    (uid, username)
    VALUES (?,?)
    `);
	const us = insertUser.run(newID(12), username);
	console.log(us);
	const usid = Number(us.lastInsertRowid);
	// todo: we need to make this a transaction and insert an user too
	const insertWebAuthnChallenge = db.prepare(`
    INSERT INTO webauthn_challenges
    (userID,challenge,type)
    VALUES (?,?,?)
    `);

	db.transaction(() => {
		const chall = insertWebAuthnChallenge.run(usid, options.challenge, "registration");
	})();
	return options;
}

// we need to link with webauthn_challenges, which is where we will get the stored challenge

export async function signupVerify(response: PublicKeyCredentialCreationOptionsJSON, username: string) {
	const usid = db.prepare("select id from users where username=?");
	const id = usid.get(username);
	console.log("singup id");
	console.log(id.id);
	const expectedChallenge = db
		.prepare(`
    SELECT id, challenge
    FROM webauthn_challenges
    WHERE userID = ? and type='registration'`)
		.get(id.id) as { id: number; challenge: string };

	console.log("expecto");
	console.log(expectedChallenge);

	const result = await verifyRegistrationResponse({
		response: response,
		expectedChallenge: expectedChallenge.challenge,
		expectedOrigin: expectedOrigins,
		expectedRPID: rpID,
	});
	console.log("----result begin-------");
	console.log(result);
	console.log("----result end-------");

	if (result.verified && result.registrationInfo) {
		console.log("siamo qua");
		const { credential } = result.registrationInfo;

		const insertAuthenticator = db.prepare(`
    INSERT INTO authenticators
    (user_webauthnID, credential_id,public_key,counter)
    VALUES (?,?,?,?)
    `);

		const gogo = insertAuthenticator.run(
			id.id,
			Buffer.from(credential.id).toString("base64url"),
			credential.publicKey,
			credential.counter,
		);
		// users.set(body.email, {
		// 	userId: pending.userId,
		// 	credentialID: Buffer.from(credential.id).toString("base64url"),
		// 	publicKey: credential.publicKey,
		// 	counter: credential.counter,
		// });
		console.log(gogo);
	}

	return result.verified;
}

export async function login(username: string): Promise<PublicKeyCredentialRequestOptionsJSON> {
	const usid = db.prepare("select id from users where username=?");
	const id = usid.get(username);
	const options = await generateAuthenticationOptions({
		rpID: rpID,

		userVerification: "required",
	});
	const insertWebAuthnChallenge = db.prepare(`
    INSERT INTO webauthn_challenges
    (userID,challenge,type)
    VALUES (?,?,?)
    `);

	// const insertUser = db.prepare(`
	//    INSERT INTO users
	//    (uid, username)
	//    VALUES (?,?)
	//    `);

	db.transaction(() => {
		const chall = insertWebAuthnChallenge.run(id.id, options.challenge, "login");
		console.log(chall);
	})();

	return options;
}

export async function verifyLogin(response: AuthenticationResponseJSON, username: string): Promise<boolean> {
	// const lognpaoo = db
	// 	.prepare(`
	//  select id from webauthn_challenges where username=? AND type='login'`)
	// 	.get(username) as { id: number };

	// console.log(lognpaoo);
	// console.log(lognpaoo);
	// console.log(lognpaoo);
	// console.log(lognpaoo);
	// console.log(lognpaoo);
	// console.log(lognpaoo);
	console.log("usernome");
	console.log(username);
	const usid = db.prepare("select id from users where username=?");
	const id = usid.get(username);
	console.log("iddio");
	console.log(id);
	const expectedChallenge = db

		.prepare(`

     SELECT id, challenge
     FROM webauthn_challenges
     ORDER BY created_at DESC
     LIMIT 1;
     WHERE userID = ? AND type='login'
     `)
		.get(id.id) as { id: number; challenge: string };

	if (!expectedChallenge) {
		throw new Error("No challenge");
	}

	console.log("expecto patru");
	console.log(expectedChallenge);
	console.log("fine expecto patru");
	const usern = db
		.prepare(`
      SELECT credential_id,public_key, counter
      FROM authenticators
      WHERE user_webauthnID = ?
      ORDER BY created_at DESC
      LIMIT 1;
      `)
		.get(id.id) as { credential_id: Buffer; public_key: Uint8Array<ArrayBuffer>; counter: number };

	const userHandle = response.response.userHandle;

	if (!userHandle) {
		throw new Error("No user handle");
	}

	// const userId = Buffer.from(userHandle, "base64url");

	// const user = [...users.values()].find((u) => Buffer.compare(Buffer.from(u.userId), userId) === 0);
	console.log(usern);
	if (!usern) {
		throw new Error("User not found");
	}

	const result = await verifyAuthenticationResponse({
		response: response,
		expectedChallenge: expectedChallenge.challenge,
		expectedOrigin: expectedOrigins,
		expectedRPID: rpID,
		credential: {
			id: Buffer.from(usern.credential_id).toString("base64url"),
			publicKey: usern.public_key,
			counter: usern.counter,
		},
	});

	console.log("il risultato del professorone");
	console.log(result);
	console.log("fine del risultato del docentone");

	if (result.verified) {
		const query = db.prepare(`
       UPDATE authenticators
       SET counter =?
       WHERE user_webauthnID = ?`);

		const niuresult = query.run(result.authenticationInfo.newCounter, id.id);
		if (niuresult.changes === 0) throw new Error(`no receipt found with asjkdhasjhdjkashjkashd`);

		// user.counter = result.authenticationInfo.newCounter;
	}
	console.log("il dottore entryu opure");
	console.log("stiamo parlando dell' illustrissimo");
	console.log(username);
	console.log("sitcazzi!!!!!");
	return result.verified;
}
