import { routes as templatebuilder, workerPath } from "./packages/templatebuilder/index";
import { routes as extractor }                   from "./packages/extractor/index";
import { routes as summarizer }                  from "./packages/summarizer/index";
import { join } from "node:path";
import { rm } from "node:fs/promises";
import { dataPath } from "./deployPaths";
import {
  addSecurityHeaders,
  getCsrfCookieName,
  isAuthenticated,
  isSafeStem,
  login,
  loginPage,
  logout,
  requireAuth,
  securityHeaders,
  withAuth,
} from "./security";

const SUMMARIZER_OUTPUTS_DIR =
  process.env.BMP_SUMMARIZER_OUTPUTS_DIR ??
  dataPath(join(import.meta.dir, "packages/summarizer/outputs"), "summarizer", "outputs");
const EXTRACTOR_OUTPUTS_DIR =
  process.env.BMP_EXTRACTOR_OUTPUTS_DIR ??
  dataPath(join(import.meta.dir, "packages/extractor/outputs"), "extractor", "outputs");
const ROOT_PAGE_PATH = join(import.meta.dir, "index.html");
const BMP_LOGO_PATH = join(import.meta.dir, "assets/bmp-logo.png");
const PORT = Number(process.env.PORT ?? 3000);

function contentTypeFor(path: string): string {
  if (path.endsWith(".json")) return "application/json; charset=utf-8";
  if (path.endsWith(".xlsx")) return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
  if (path.endsWith(".pdf")) return "application/pdf";
  if (path.endsWith(".html")) return "text/html; charset=utf-8";
  return "application/octet-stream";
}

async function serveSummaryDownload(req: Request, stem: string, file: string): Promise<Response> {
  const blocked = await requireAuth(req);
  if (blocked) return blocked;

  if (
    !isSafeStem(stem) ||
      file !== `${stem}.json` &&
      file !== `${stem}.xlsx` &&
      file !== `${stem}.pdf` &&
      file !== `${stem}.report.html` &&
      file !== `${stem}.questions.pdf` &&
      file !== `${stem}.questions.html`
  ) {
    return new Response("Not found", { status: 404 });
  }

  const path = join(SUMMARIZER_OUTPUTS_DIR, stem, file);
  const output = Bun.file(path);
  if (!(await output.exists())) {
    return new Response("Not found", { status: 404 });
  }

  return new Response(output, {
    headers: {
      ...securityHeaders,
      "Content-Type": contentTypeFor(file),
      "Cache-Control": "private, no-store",
      "Content-Disposition": `attachment; filename="${file.replaceAll('"', "")}"`,
    },
  });
}

Bun.serve({
  port: PORT,
  routes: {
    "/login": {
      GET: loginPage,
      POST: login,
    },
    "/healthz": {
      GET: () => Response.json({ ok: true }),
    },
    "/logout": {
      POST: withAuth(async () => logout(), { csrf: true }),
    },
    "/api/auth/session": {
      GET: async (req: Request) => addSecurityHeaders(Response.json({
        authenticated: await isAuthenticated(req),
        csrfCookieName: getCsrfCookieName(),
      })),
    },
    "/": {
      GET: withAuth(async () => new Response(Bun.file(ROOT_PAGE_PATH), {
        headers: { "Content-Type": "text/html; charset=utf-8" },
      })),
    },
    "/assets/bmp-logo.png": {
      GET: withAuth(async () => new Response(Bun.file(BMP_LOGO_PATH), {
        headers: { "Content-Type": "image/png", "Cache-Control": "private, max-age=86400" },
      })),
    },
    "/api/results/:stem": {
      DELETE: withAuth(async (req: Request) => {
        const stem = (req as Request & { params: Record<string, string | undefined> }).params.stem ?? "";
        if (!isSafeStem(stem)) {
          return Response.json({ error: "Invalid result id" }, { status: 400 });
        }

        const targets = [
          join(EXTRACTOR_OUTPUTS_DIR, stem),
          join(SUMMARIZER_OUTPUTS_DIR, stem),
        ];
        await Promise.all(targets.map((target) => rm(target, { recursive: true, force: true })));
        return Response.json({ ok: true, stem, deleted: targets.length });
      }, {
        csrf: true,
        limit: { key: "result-delete", max: 20, windowMs: 60_000 },
      }),
    },
    "/api/pipeline/result/:stem": {
      GET: withAuth(async (req: Request) => {
        const stem = (req as Request & { params: Record<string, string | undefined> }).params.stem ?? "";
        if (!isSafeStem(stem)) {
          return Response.json({ error: "Invalid result id" }, { status: 400 });
        }

        const jsonPath = join(SUMMARIZER_OUTPUTS_DIR, stem, `${stem}.json`);
        const xlsxPath = join(SUMMARIZER_OUTPUTS_DIR, stem, `${stem}.xlsx`);
        const pdfPath = join(SUMMARIZER_OUTPUTS_DIR, stem, `${stem}.pdf`);
        const reportHtmlPath = join(SUMMARIZER_OUTPUTS_DIR, stem, `${stem}.report.html`);
        const questionsPdfPath = join(SUMMARIZER_OUTPUTS_DIR, stem, `${stem}.questions.pdf`);
        const questionsHtmlPath = join(SUMMARIZER_OUTPUTS_DIR, stem, `${stem}.questions.html`);
        const jsonFile = Bun.file(jsonPath);
        if (!(await jsonFile.exists())) {
          return Response.json({ error: "Summary result not found" }, { status: 404 });
        }

        const downloads = [
          {
            label: "JSON herunterladen",
            url: `/downloads/summarizer/${encodeURIComponent(stem)}/${encodeURIComponent(`${stem}.json`)}`,
          },
        ];

        if (await Bun.file(xlsxPath).exists()) {
          downloads.push({
            label: "XLSX herunterladen",
            url: `/downloads/summarizer/${encodeURIComponent(stem)}/${encodeURIComponent(`${stem}.xlsx`)}`,
          });
        }
        if (await Bun.file(pdfPath).exists()) {
          downloads.push({
            label: "PDF herunterladen",
            url: `/downloads/summarizer/${encodeURIComponent(stem)}/${encodeURIComponent(`${stem}.pdf`)}`,
          });
        }
        if (await Bun.file(reportHtmlPath).exists()) {
          downloads.push({
            label: "Report-HTML herunterladen",
            url: `/downloads/summarizer/${encodeURIComponent(stem)}/${encodeURIComponent(`${stem}.report.html`)}`,
          });
        }
        if (await Bun.file(questionsPdfPath).exists()) {
          downloads.push({
            label: "Fragen-PDF herunterladen",
            url: `/downloads/summarizer/${encodeURIComponent(stem)}/${encodeURIComponent(`${stem}.questions.pdf`)}`,
          });
        }
        if (await Bun.file(questionsHtmlPath).exists()) {
          downloads.push({
            label: "Fragen-HTML herunterladen",
            url: `/downloads/summarizer/${encodeURIComponent(stem)}/${encodeURIComponent(`${stem}.questions.html`)}`,
          });
        }

        return Response.json({
          ok: true,
          stem,
          summary: await jsonFile.json(),
          downloads,
        });
      }),
    },
    "/downloads/summarizer/:stem/:file": {
      GET: async (req: Request) => {
        const params = (req as Request & { params: Record<string, string | undefined> }).params;
        const stem = params.stem ?? "";
        const file = params.file ?? "";
        return serveSummaryDownload(req, stem, file);
      },
    },
    ...templatebuilder,
    ...extractor,
    ...summarizer,
  },
  async fetch(req) {
    const { pathname } = new URL(req.url);
    if (pathname === "/pdf.worker.mjs") {
      const blocked = await requireAuth(req);
      if (blocked) return blocked;
      return addSecurityHeaders(new Response(Bun.file(workerPath), {
        headers: { "Content-Type": "application/javascript", "Cache-Control": "private, max-age=3600" },
      }));
    }
    if (pathname === "/login" && req.method === "POST") {
      return login(req);
    }
    if (pathname === "/logout" && req.method === "POST") {
      const blocked = await requireAuth(req, { csrf: true });
      if (blocked) return blocked;
      return logout();
    }
    if (pathname === "/login" && req.method === "GET") {
      return loginPage(req);
    }
    if (pathname === "/healthz" && req.method === "GET") {
      return Response.json({ ok: true });
    }
    if (pathname === "/") {
      const blocked = await requireAuth(req);
      if (blocked) return blocked;
      return addSecurityHeaders(new Response(Bun.file(ROOT_PAGE_PATH), {
        headers: { "Content-Type": "text/html; charset=utf-8" },
      }));
    }
    if (pathname === "/assets/bmp-logo.png") {
      const blocked = await requireAuth(req);
      if (blocked) return blocked;
      return addSecurityHeaders(new Response(Bun.file(BMP_LOGO_PATH), {
        headers: { "Content-Type": "image/png", "Cache-Control": "private, max-age=86400" },
      }));
    }
    if (pathname.startsWith("/_bun/")) {
      const blocked = await requireAuth(req);
      if (blocked) return blocked;
      return new Response("Not found", {
        status: 404,
        headers: securityHeaders,
      });
    }
    if (pathname.startsWith("/downloads/summarizer/")) {
      const [stem = "", file = ""] = pathname
        .slice("/downloads/summarizer/".length)
        .split("/")
        .map((part) => decodeURIComponent(part));
      return serveSummaryDownload(req, stem, file);
    }
    return new Response("Not found", { status: 404, headers: securityHeaders });
  },
  development: { hmr: true, console: true },
});

console.log("bmp-rewrite");
console.log(`  pipeline       → http://localhost:${PORT}/`);
console.log(`  templatebuilder → http://localhost:${PORT}/builder`);
console.log(`  extractor       → http://localhost:${PORT}/extractor`);
console.log(`  summarizer      → http://localhost:${PORT}/summarizer`);
