import { env } from "./lib/env";
import { Server, Cookie } from "rjweb-server";
import { Runtime } from "@rjweb/runtime-node";
import { existsSync } from "node:fs";
import { join } from "path";
import { prisma } from "./lib/db";
import { logger } from "./lib/logger";
import { corsMiddleware } from "./lib/middlewares/cors";
import { mainMiddleware } from "./lib/middlewares/main";
import { authResolutionMiddleware, authEnforcementMiddleware } from "./lib/middlewares/auth";
import { makeResponse } from "./lib/response";
import { ERROR_MESSAGES } from "./lib/errors";
import { startJobWorker } from "./workers/jobWorker";
import { startCronWorkers } from "./workers/cronWorker";
import { getAdminSettings } from "./lib/adminSettings";
import { runOrphanCleanup } from "./services/orphanCleanup";
import { runWebhookReconciliation } from "./services/webhookReconcile";

import { loginHandler, logoutHandler, meHandler, setupStatusHandler, firstUserHandler } from "./routes/auth";
import { webhookHandler } from "./routes/webhook";
import { getUserSettings, updateUsername, updatePassword, updateGitea, updateAws, getWebhookSecret, regenerateWebhookSecret } from "./routes/api/user";
import { listRepos, saveRepoConfig, toggleRepoEnabled, getRepoConfig } from "./routes/api/repos";
import { listPreviews, getPreview, stopPreviewRoute, rebuildPreviewRoute, previewLogsWs, previewAppLogsWs } from "./routes/api/previews";
import { getStats } from "./routes/api/stats";
import { listUsers, createUser, updateUser, deleteUser, getSettings, updateSettings, adminListPreviews, adminStopPreview } from "./routes/api/admin";

const uiBuildPath = join(__dirname, "../../frontend/dist");
const uiIndexPath = join(uiBuildPath, "index.html");
const hasUiBuild = existsSync(uiBuildPath);

export const server = new Server(
  Runtime,
  {
    port: env.PORT,
    bind: "0.0.0.0",
    version: false,
    performance: { lastModified: false, eTag: false },
    logging: { warn: true, debug: false, error: true },
  },
  [
    corsMiddleware.use({}),
    mainMiddleware.use({}),
    authResolutionMiddleware.use({}),
    authEnforcementMiddleware.use({}),
  ],
);

// Auth
server.path("/", (path) => path
  .http("POST", "/api/auth/login", (http) => http.onRequest(loginHandler))
  .http("POST", "/api/auth/logout", (http) => http.onRequest(logoutHandler))
  .http("GET", "/api/auth/me", (http) => http.onRequest(meHandler))
  .http("GET", "/api/auth/setup-status", (http) => http.onRequest(setupStatusHandler))
  .http("POST", "/api/auth/first-user", (http) => http.onRequest(firstUserHandler))
);

// Webhook
server.path("/", (path) => path
  .http("POST", "/webhook/{userId}", (http) => http.onRequest(webhookHandler))
);

// User settings
server.path("/", (path) => path
  .http("GET", "/api/user/settings", (http) => http.onRequest(getUserSettings))
  .http("PATCH", "/api/user/username", (http) => http.onRequest(updateUsername))
  .http("PATCH", "/api/user/password", (http) => http.onRequest(updatePassword))
  .http("PUT", "/api/user/gitea", (http) => http.onRequest(updateGitea))
  .http("PUT", "/api/user/aws", (http) => http.onRequest(updateAws))
  .http("GET", "/api/user/webhook-secret", (http) => http.onRequest(getWebhookSecret))
  .http("POST", "/api/user/webhook-secret/regenerate", (http) => http.onRequest(regenerateWebhookSecret))
);

// Repos
server.path("/", (path) => path
  .http("GET", "/api/repos", (http) => http.onRequest(listRepos))
  .http("POST", "/api/repos/config", (http) => http.onRequest(saveRepoConfig))
  .http("POST", "/api/repos/toggle", (http) => http.onRequest(toggleRepoEnabled))
  .http("GET", "/api/repos/{owner}/{repo}/config", (http) => http.onRequest(getRepoConfig))
);

// Stats (instance-wide Overview)
server.path("/", (path) => path
  .http("GET", "/api/stats", (http) => http.onRequest(getStats))
);

// Previews
server.path("/", (path) => path
  .http("GET", "/api/previews", (http) => http.onRequest(listPreviews))
  .http("GET", "/api/previews/{id}", (http) => http.onRequest(getPreview))
  .http("POST", "/api/previews/{id}/stop", (http) => http.onRequest(stopPreviewRoute))
  .http("POST", "/api/previews/{id}/rebuild", (http) => http.onRequest(rebuildPreviewRoute))
  .ws("/api/previews/{id}/logs", (ws) => ws
    .onOpen(previewLogsWs)
    .onMessage(async () => {})
    .onClose(async () => {})
  )
  .ws("/api/previews/{id}/applogs", (ws) => ws
    .onOpen(previewAppLogsWs)
    .onMessage(async () => {})
    .onClose(async () => {})
  )
);

// Admin
server.path("/", (path) => path
  .http("GET", "/api/admin/users", (http) => http.onRequest(listUsers))
  .http("POST", "/api/admin/users", (http) => http.onRequest(createUser))
  .http("PATCH", "/api/admin/users/{id}", (http) => http.onRequest(updateUser))
  .http("DELETE", "/api/admin/users/{id}", (http) => http.onRequest(deleteUser))
  .http("GET", "/api/admin/settings", (http) => http.onRequest(getSettings))
  .http("PUT", "/api/admin/settings", (http) => http.onRequest(updateSettings))
  .http("GET", "/api/admin/previews", (http) => http.onRequest(adminListPreviews))
  .http("POST", "/api/admin/previews/{id}/stop", (http) => http.onRequest(adminStopPreview))
);

// Static UI — must come AFTER API routes
if (hasUiBuild) {
  server.path("/", (path) => path.static(uiBuildPath));
}

server.notFound(async (ctr) => {
  const STATIC_EXT = /\.(js|mjs|css|png|jpg|jpeg|gif|svg|ico|woff2?|ttf|eot|map|json|txt|xml|webp|avif)(\?.*)?$/i;
  if (!ctr.url.path.startsWith("/api") && !ctr.url.path.startsWith("/webhook") && !STATIC_EXT.test(ctr.url.path) && existsSync(uiIndexPath)) {
    return ctr.status(200).printFile(uiIndexPath, { addTypes: true });
  }
  return makeResponse({ ctr, content: { code: ERROR_MESSAGES.NOT_FOUND.code, message: ERROR_MESSAGES.NOT_FOUND.message } });
});

server.error("httpRequest", async (ctr, error) => {
  logger.error(error, "Unhandled HTTP request error");
  return makeResponse({ ctr, content: { code: ERROR_MESSAGES.INTERNAL_SERVER_ERROR.code } });
});

server
  .start()
  .then(async (port) => {
    await prisma.$connect();
    logger.info({ port }, "PP backend running");
    await getAdminSettings();
    startJobWorker();
    startCronWorkers();
    runOrphanCleanup().catch(e => logger.warn(e, "Orphan cleanup error"));
    runWebhookReconciliation().catch(e => logger.warn(e, "Webhook reconciliation error"));
    logger.info("All workers started");
  })
  .catch((err) => logger.error(err, "Server failed to start"));
