import HttpRouter from "@lib/router.ts";
import { Eta } from "@eta-dev/eta";
import { serveFile } from "jsr:@std/http/file-server";
import rateLimitMiddleware from "@src/middleware/rateLimiter.ts";
import authMiddleware from "@src/middleware/auth.ts";
import loggerMiddleware from "@src/middleware/logger.ts";
import { SchemaValidationError, z } from "@shared/utils/validator.ts";
import { loginApi } from "./api.ts";
import { err, ok } from "@shared/utils/result.ts";
import admin from "@src/lib/admin.ts";
import {
    FailedToParseRequestAsJSON,
    QueryExecutionError,
} from "@src/lib/errors.ts";
import { Context } from "@src/lib/context.ts";

const AUTH_COOKIE_NAME = "token";

const router = new HttpRouter();

const views = Deno.cwd() + "/views/";
const eta = new Eta({ views });

router.use(loggerMiddleware);
router.use(rateLimitMiddleware);
router.use(authMiddleware);

const cache: Map<string, Response> = new Map();

router.get("/public/*", async (c) => {
    const filePath = "." + c.path;

    //const cached = cache.get(filePath);
    //
    //if (cached) {
    //    return cached.clone();
    //}

    const res = await serveFile(c.req, filePath);

    //cache.set(filePath, res.clone());

    return res;
});

router
    .get(["", "/index.html"], (c) => {
        return c.html(eta.render("./index.html", {}));
    })
    .get(["/login", "/login.html"], (c) => {
        const alreadyLoggedIn = c.cookies.get("token").map((token) =>
            admin.sessions.verifyToken(token)
        )
            .toBoolean();

        console.log(alreadyLoggedIn);

        return c.html(eta.render("./login.html", { alreadyLoggedIn }));
    });

admin.setPassword("Vermont5481");

router.api(loginApi, async (c) => {
    const r = await c
        .parseBody()
        .andThenAsync(
            ({ password }) => admin.verifyPassword(password),
        );

    if (r.isErr()) {
        if (r.error.type === "AdminPasswordNotSetError") {
            return c.json400(
                err({
                    type: r.error.type,
                    msg: r.error.message,
                }),
            );
        }
        return handleCommonErrors(c, r.error);
    }

    const isMatch = r.value;

    if (isMatch) {
        return admin.sessions.create()
            .map(({ value, expires }) => {
                c.cookies.set({
                    name: AUTH_COOKIE_NAME,
                    value,
                    expires,
                });
                return ok({ isMatch: true });
            }).match(
                (v) => c.json(v),
                (e) => handleCommonErrors(c, e),
            );
    } else {
        return c.json(ok({
            isMatch: false,
        }));
    }
});

function handleCommonErrors(
    c: Context<any, any, any>,
    error:
        | QueryExecutionError
        | FailedToParseRequestAsJSON
        | SchemaValidationError,
): Response {
    switch (error.type) {
        case "QueryExecutionError":
            return c.json(
                err(new QueryExecutionError("Server failed to execute query")),
                { status: 500 },
            );
        case "FailedToParseRequestAsJSON":
            return c.json(
                err(error),
                { status: 400 },
            );
        case "SchemaValiationError":
            return c.json(
                err({
                    type: "ValidationError",
                    msg: error.msg,
                }),
                { status: 400 },
            );
    }
}

export default {
    async fetch(req, connInfo) {
        return await router.handleRequest(req, connInfo);
    },
} satisfies Deno.ServeDefaultExport;
