import { count, eq, sql } from 'drizzle-orm';
import { ZodError, type ZodIssue } from 'zod';
import useDatabase from '~/composables/useDatabase';
import { usersDataTable, usersTable } from '~/db/schema';
import { schema } from '~/schemas/registration';
import { checkSession, logSession } from '~/server/utils/user';
import type { UserSession, UserSessionRequired } from '~/types/auth';

interface SuccessHandler
{
    success: true;
    session: UserSession;
}
interface ErrorHandler
{
    success: false;
    error: Error | ZodError<{
        username: string;
        email: string;
        password: string;
    }>;
}
type Return = SuccessHandler | ErrorHandler;

export default defineEventHandler(async (e): Promise<Return> => {
    try
    {
        const session = await getUserSession(e);
        const db = useDatabase();

        const checkedSession = await checkSession(e, session);

        if(checkedSession !== undefined)
            return checkedSession;

        const body = await readValidatedBody(e, schema.safeParse);

        if (!body.success)
        {
            await clearUserSession(e);

            setResponseStatus(e, 406);
            return { success: false, error: body.error };
        }

        const checkUsername = db.select({ count: count() }).from(usersTable).where(eq(usersTable.username, sql.placeholder('username'))).prepare().get({ username: body.data.username });
        const checkEmail = db.select({ count: count() }).from(usersTable).where(eq(usersTable.email, sql.placeholder('email'))).prepare().get({ email: body.data.email });

        const errors: ZodIssue[] = [];
        if(!checkUsername || checkUsername.count !== 0)
            errors.push({ code: 'custom', path: ['username'], message: "Ce nom d'utilisateur est déjà utilisé" });
        if(!checkEmail || checkEmail.count !== 0)
            errors.push({ code: 'custom', path: ['email'], message: "Cette adresse mail est déjà utilisée" });

        if(errors.length > 0)
        {
            setResponseStatus(e, 406);
            return { success: false, error: new ZodError(errors) };
        }
        else
        {
            const hash = await Bun.password.hash(body.data.password);
            db.insert(usersTable).values({ username: sql.placeholder('username'), email: sql.placeholder('email'), hash: sql.placeholder('hash'), state: sql.placeholder('state') }).prepare().run({ username: body.data.username, email: body.data.email, hash, state: 0 });
            const id = db.select({ id: usersTable.id }).from(usersTable).where(eq(usersTable.username, sql.placeholder('username'))).prepare().get({ username: body.data.username });

            if(!id || !id.id)
            {
                setResponseStatus(e, 406);
                return { success: false, error: new Error('Erreur de création de compte') };
            }

            db.insert(usersDataTable).values({ id: sql.placeholder('id') }).prepare().run({ id: id.id });

            logSession(e, await setUserSession(e, { user: { id: id.id, username: body.data.username, email: body.data.email, state: 0, signin: new Date(), permissions: [], lastTimestamp: new Date() } }) as UserSessionRequired);

            const emailId = Bun.hash('register' + id.id + hash, Date.now());
            const timestamp = Date.now() + 1000 * 60 * 60;

            await runTask('validation', {
                payload: {
                    type: 'validation',
                    id: emailId, timestamp,
                }
            });
            await runTask('mail', {
                payload: {
                    type: 'mail',
                    to: [body.data.email],
                    template: 'registration',
                    data: {
                        id: emailId, timestamp,
                        userId: id,
                        username: body.data.username,
                    },
                }
            });

            setResponseStatus(e, 201);
            return { success: true, session };
        }
    }
    catch(err: any)
    {
        console.error(err);
        
        await clearUserSession(e);
        return { success: false, error: err as Error };
    }
});