import {
    disable2FASchema,
    enable2FACodeSchema,
    startVerificationSchema,
    verifyCodeSchema,
} from "@pkg/logic/domains/2fa/data";
import {
    getFlowExecCtxForRemoteFuncs,
    unauthorized,
} from "$lib/core/server.utils";
import { getTwofaController } from "@pkg/logic/domains/2fa/controller";
import { command, getRequestEvent, query } from "$app/server";
import { auth } from "@pkg/logic/domains/auth/config.base";
import type { User } from "@pkg/logic/domains/user/data";
import * as v from "valibot";

const tc = getTwofaController();

function buildIpAddress(headers: Headers) {
    return (
        headers.get("x-forwarded-for") ?? headers.get("x-real-ip") ?? "unknown"
    );
}

function buildUserAgent(headers: Headers) {
    return headers.get("user-agent") ?? "unknown";
}

export const setupTwoFactorSC = command(v.object({}), async () => {
    const event = getRequestEvent();
    const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
    const currentUser = event.locals.user;

    if (!fctx.userId || !currentUser) {
        return unauthorized(fctx);
    }

    const res = await tc.setup2FA(fctx, currentUser as User);
    return res.isOk()
        ? { data: res.value, error: null }
        : { data: null, error: res.error };
});

export const verifyAndEnableTwoFactorSC = command(
    enable2FACodeSchema,
    async (payload) => {
        const event = getRequestEvent();
        const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
        const currentUser = event.locals.user;

        if (!fctx.userId || !currentUser) {
            return unauthorized(fctx);
        }

        const res = await tc.verifyAndEnable2FA(
            fctx,
            currentUser as User,
            payload.code,
            event.request.headers,
        );

        return res.isOk()
            ? { data: res.value, error: null }
            : { data: null, error: res.error };
    },
);

export const generateBackupCodesSQ = query(async () => {
    const event = getRequestEvent();
    const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
    const currentUser = event.locals.user;

    if (!fctx.userId || !currentUser) {
        return unauthorized(fctx);
    }

    const res = await tc.generateBackupCodes(fctx, currentUser as User);
    return res.isOk()
        ? { data: res.value, error: null }
        : { data: null, error: res.error };
});

export const disableTwoFactorSC = command(disable2FASchema, async (payload) => {
    const event = getRequestEvent();
    const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
    const currentUser = event.locals.user;

    if (!fctx.userId || !currentUser) {
        return unauthorized(fctx);
    }

    const res = await tc.disable(fctx, currentUser as User, payload.code);
    return res.isOk()
        ? { data: res.value, error: null }
        : { data: null, error: res.error };
});

export const requiresVerificationSQ = query(
    v.object({ sessionId: v.string() }),
    async (input) => {
        const event = getRequestEvent();
        const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
        const currentUser = event.locals.user;

        if (!fctx.userId || !currentUser) {
            return unauthorized(fctx);
        }

        const res = await tc.requiresInitialVerification(
            fctx,
            currentUser as User,
            input.sessionId,
        );

        return res.isOk()
            ? { data: res.value, error: null }
            : { data: null, error: res.error };
    },
);

export const requiresSensitiveActionSQ = query(async () => {
    const event = getRequestEvent();
    const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);
    const currentUser = event.locals.user;

    if (!fctx.userId || !currentUser) {
        return unauthorized(fctx);
    }

    const res = await tc.requiresSensitiveActionVerification(
        fctx,
        currentUser as User,
    );

    return res.isOk()
        ? { data: res.value, error: null }
        : { data: null, error: res.error };
});

export const startVerificationSessionSC = command(
    startVerificationSchema,
    async (payload) => {
        const event = getRequestEvent();
        const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);

        if (!fctx.userId) {
            return unauthorized(fctx);
        }

        const res = await tc.startVerification(fctx, {
            userId: payload.userId,
            sessionId: payload.sessionId,
            ipAddress: buildIpAddress(event.request.headers),
            userAgent: buildUserAgent(event.request.headers),
        });

        return res.isOk()
            ? { data: res.value, error: null }
            : { data: null, error: res.error };
    },
);

export const verifySessionCodeSC = command(
    verifyCodeSchema,
    async (payload) => {
        const event = getRequestEvent();
        const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);

        let currentUser = event.locals.user;

        if (!currentUser) {
            const sess = await auth.api.getSession({
                headers: event.request.headers,
            });
            currentUser = sess?.user as User | undefined;
        }

        const res = await tc.verifyCode(
            fctx,
            {
                verificationSessToken: payload.verificationToken,
                code: payload.code,
            },
            currentUser as User | undefined,
        );

        return res.isOk()
            ? { data: res.value, error: null }
            : { data: null, error: res.error };
    },
);

export const cleanupExpiredSessionsSC = command(v.object({}), async () => {
    const event = getRequestEvent();
    const fctx = await getFlowExecCtxForRemoteFuncs(event.locals);

    if (!fctx.userId) {
        return unauthorized(fctx);
    }

    const res = await tc.cleanupExpiredSessions(fctx);
    return res.isOk()
        ? { data: res.value, error: null }
        : { data: null, error: res.error };
});
