import { createHonoServer } from "react-router-hono-server/bun";
import * as fs from "node:fs/promises";
import { Scalar } from "@scalar/hono-api-reference";
import { Hono } from "hono";
import { logger as honoLogger } from "hono/logger";
import { openAPIRouteHandler } from "hono-openapi";
import { getCapabilities } from "./core/capabilities";
import { runDbMigrations } from "./db/db";
import { authController } from "./modules/auth/auth.controller";
import { requireAuth } from "./modules/auth/auth.middleware";
import { driverController } from "./modules/driver/driver.controller";
import { startup } from "./modules/lifecycle/startup";
import { repositoriesController } from "./modules/repositories/repositories.controller";
import { systemController } from "./modules/system/system.controller";
import { volumeController } from "./modules/volumes/volume.controller";
import { backupScheduleController } from "./modules/backups/backups.controller";
import { eventsController } from "./modules/events/events.controller";
import { handleServiceError } from "./utils/errors";
import { logger } from "./utils/logger";
import { shutdown } from "./modules/lifecycle/shutdown";
import { SOCKET_PATH } from "./core/constants";

export const generalDescriptor = (app: Hono) =>
	openAPIRouteHandler(app, {
		documentation: {
			info: {
				title: "Zerobyte API",
				version: "1.0.0",
				description: "API for managing volumes",
			},
			servers: [{ url: "http://192.168.2.42:4096", description: "Development Server" }],
		},
	});

export const scalarDescriptor = Scalar({
	title: "Zerobyte API Docs",
	pageTitle: "Zerobyte API Docs",
	url: "/api/v1/openapi.json",
});

const driver = new Hono().use(honoLogger()).route("/", driverController);
const app = new Hono()
	.use(honoLogger())
	.get("healthcheck", (c) => c.json({ status: "ok" }))
	.route("/api/v1/auth", authController.basePath("/api/v1"))
	.route("/api/v1/volumes", volumeController.use(requireAuth))
	.route("/api/v1/repositories", repositoriesController.use(requireAuth))
	.route("/api/v1/backups", backupScheduleController.use(requireAuth))
	.route("/api/v1/system", systemController.use(requireAuth))
	.route("/api/v1/events", eventsController.use(requireAuth));

app.get("/api/v1/openapi.json", generalDescriptor(app));
app.get("/api/v1/docs", scalarDescriptor);

app.onError((err, c) => {
	logger.error(`${c.req.url}: ${err.message}`);

	if (err.cause instanceof Error) {
		logger.error(err.cause.message);
	}

	const { status, message } = handleServiceError(err);

	return c.json({ message }, status);
});

runDbMigrations();

const { docker } = await getCapabilities();

if (docker) {
	try {
		await fs.mkdir("/run/docker/plugins", { recursive: true });

		Bun.serve({
			unix: SOCKET_PATH,
			fetch: driver.fetch,
		});

		logger.info(`Docker volume plugin server running at ${SOCKET_PATH}`);
	} catch (error) {
		logger.error(`Failed to start Docker volume plugin server: ${error}`);
	}
}

startup();

logger.info(`Server is running at http://localhost:4096`);

export type AppType = typeof app;

process.on("SIGTERM", async () => {
	logger.info("SIGTERM received, starting graceful shutdown...");
	await shutdown();
	process.exit(0);
});

process.on("SIGINT", async () => {
	logger.info("SIGINT received, starting graceful shutdown...");
	await shutdown();
	process.exit(0);
});

export default await createHonoServer({ app, port: 4096 });
