import { getLogger } from "@logtape/logtape";
import { Elysia, t } from "elysia";
import { auth } from "./auth";
import { entriesH } from "./controllers/entries";
import { imagesH } from "./controllers/images";
import { historyH } from "./controllers/profiles/history";
import { nextup } from "./controllers/profiles/nextup";
import { watchlistH } from "./controllers/profiles/watchlist";
import { seasonsH } from "./controllers/seasons";
import { seed } from "./controllers/seed";
import { videoLinkH } from "./controllers/seed/video-links";
import { videosWriteH } from "./controllers/seed/videos";
import { collections } from "./controllers/shows/collections";
import { movies } from "./controllers/shows/movies";
import { series } from "./controllers/shows/series";
import { showsH } from "./controllers/shows/shows";
import { staffH } from "./controllers/staff";
import { streamsH } from "./controllers/streams";
import { studiosH } from "./controllers/studios";
import { videosMetadata } from "./controllers/video-metadata";
import { videosReadH } from "./controllers/videos";
import { dbRaw } from "./db";
import { KError } from "./models/error";
import { appWs } from "./websockets";

const logger = getLogger();

export const base = new Elysia({ name: "base" })
	.onError(({ code, error }) => {
		// sometimes elysia as an unknown code when throwing errors
		if (code === "UNKNOWN") {
			try {
				const details = JSON.parse(error.message);
				if (details?.code === "KError") {
					const { code, ...ret } = details;
					return ret;
				}
			} catch {}
		}
		if (code === "VALIDATION") {
			return {
				status: error.status,
				message: `Validation error.`,
				details: error,
			} as KError;
		}
		if (code === "NOT_FOUND") {
			return error;
		}
		logger.error("Request error code={code} error={error}", {
			code: code,
			error: error,
		});
		return {
			status: 500,
			message: "Internal server error",
		} as KError;
	})
	.get("/health", () => ({ status: "healthy" }) as const, {
		detail: { description: "Check if the api is healthy." },
		response: {
			200: t.Union([
				t.Object({
					status: t.Literal("a"),
					a: t.Object({ b: t.Integer() }),
				}),
				t.Object({ status: t.Literal("healthy") }),
			]),
		},
	})
	.get(
		"/ready",
		async ({ status }) => {
			try {
				await dbRaw.execute("select 1");
				return { status: "healthy", database: "healthy" } as const;
			} catch (e) {
				return status(500, {
					status: "unhealthy",
					database: e,
				});
			}
		},
		{
			detail: { description: "Check if the api is healthy." },
			response: {
				200: t.Object({
					status: t.Literal("healthy"),
					database: t.Literal("healthy"),
				}),
				500: t.Object({
					status: t.Literal("unhealthy"),
					database: t.Any(),
				}),
			},
		},
	)
	.as("global");

export const prefix = "/api";
export const handlers = new Elysia({ prefix })
	.use(base)
	.use(appWs)
	.use(auth)
	.guard(
		{
			// Those are not applied for now. See https://github.com/elysiajs/elysia/issues/1139
			detail: {
				security: [{ bearer: ["core.read"] }, { api: ["core.read"] }],
			},
			response: {
				401: { ...KError, description: "" },
				403: { ...KError, description: "" },
			},
			permissions: ["core.read"],
		},
		(app) =>
			app
				.use(streamsH)
				.use(showsH)
				.use(movies)
				.use(series)
				.use(collections)
				.use(entriesH)
				.use(seasonsH)
				.use(studiosH)
				.use(staffH)
				.use(imagesH)
				.use(watchlistH)
				.use(historyH)
				.use(nextup)
				.use(videosReadH)
				.use(videosMetadata),
	)
	.guard(
		{
			detail: {
				security: [{ bearer: ["core.write"] }, { api: ["core.write"] }],
			},
			response: {
				401: { ...KError, description: "" },
				403: { ...KError, description: "" },
			},
			permissions: ["core.write"],
		},
		(app) => app.use(videosWriteH).use(videoLinkH).use(seed),
	);
