import express, { type Router } from "express";
import path from "node:path";
import { fileURLToPath } from "node:url";
import type { AppConfig } from "../config/types.js";
import {
  loadLdapConfig,
  loadPagesConfig,
  loadUserScenariosConfig,
} from "../config/loadConfig.js";
import { requireAuth } from "../middleware/requireAuth.js";
import {
  buildScenarioImageMapForUser,
  loadHomeCatalogForUser,
  resolveScenarioForUser,
  scenarioToSlides,
} from "../services/scenarioLookup.js";
import {
  createUserScenario,
  deleteUserScenario,
  STAYTIME_MAX,
  STAYTIME_MIN,
} from "../services/userScenarios.js";
import { verifyCredentials } from "../utils/ldapAuth.js";
import { resolveBoardUrl } from "../utils/resolveBoardUrl.js";
import { loadWarehousePlansGroupedByDate } from "../services/warehouseCalendarData.js";
import {
  coerceParamsFromRequest,
  executeDataSourceById,
} from "../services/sqlDataSource.js";
import { buildMonthRows, formatDateKey } from "../utils/calendarMonth.js";

const __dirname = path.dirname(fileURLToPath(import.meta.url));

function shiftMonth(
  year: number,
  month: number,
  delta: number
): { year: number; month: number } {
  const d = new Date(year, month - 1 + delta, 1);
  return { year: d.getFullYear(), month: d.getMonth() + 1 };
}

type ScenarioViewSlide = {
  id: string;
  title: string;
  staytime: number;
  boardUrl: string;
};

type ScenarioViewSlidesData = {
  documentTitle: string;
  scenarioId: string;
  scenarioName: string;
  scenarioImage: string;
  slides: ScenarioViewSlide[];
  slidesJson: string;
  startIndex: number;
};

function buildScenarioViewSlides(
  appCfg: AppConfig,
  username: string,
  scenarioId: string,
  pageIdHint: string
): ScenarioViewSlidesData | null {
  const resolved = resolveScenarioForUser(username, scenarioId);
  if (!resolved) {
    return null;
  }
  const sc = resolved.scenario;
  const rawSlides = scenarioToSlides(sc);
  const slides = rawSlides.map((p) => ({
    id: p.id,
    title: p.title,
    staytime:
      typeof p.staytime === "number" && p.staytime > 0 ? p.staytime : 30,
    boardUrl: resolveBoardUrl(appCfg.dataEasePublicBase, p.boardUrl),
  }));
  let startIndex = 0;
  if (pageIdHint) {
    const idx = slides.findIndex((p) => p.id === pageIdHint);
    if (idx >= 0) {
      startIndex = idx;
    }
  }
  return {
    documentTitle: sc.name,
    scenarioId: sc.id,
    scenarioName: sc.name,
    scenarioImage: sc.image,
    slides,
    slidesJson: JSON.stringify(slides),
    startIndex,
  };
}

export function createBoardRouter(appCfg: AppConfig): Router {
  const router = express.Router();

  router.get("/check/", (req, res) => {
    if (req.session.user) {
      res.status(200).type("text/plain").send("ok");
      return;
    }
    res.status(401).type("text/plain").send("unauthorized");
  });

  router.get("/login", (req, res) => {
    if (req.session.user) {
      res.redirect("/board/");
      return;
    }
    res.render("login", {
      basePath: "/board",
      error: null as string | null,
    });
  });

  router.use(express.json());

  router.post("/api/login", async (req, res) => {
    const username = String(req.body?.username ?? "").trim();
    const password = String(req.body?.password ?? "");
    if (!username || !password) {
      res.status(400).json({ ok: false, error: "缺少用户名或密码" });
      return;
    }
    const skipAd = appCfg.skipLdapAuthForDebug === true;
    if (!skipAd) {
      const ldapCfg = loadLdapConfig();
      const result = await verifyCredentials(username, password, ldapCfg);
      if (!result.ok) {
        const status =
          result.reason === "connection_error" ? 503 : 401;
        const msg =
          result.reason === "user_not_found"
            ? "用户不存在"
            : result.reason === "connection_error"
              ? "目录服务不可用"
              : "用户名或密码错误";
        res.status(status).json({ ok: false, error: msg });
        return;
      }
    }
    req.session.user = { username };
    const next = String(req.body?.next ?? "");
    const safeNext =
      next.startsWith("/board/") && !next.includes("//") ? next : "/board/";
    await new Promise<void>((resolve, reject) => {
      req.session.save((err) => (err != null ? reject(err) : resolve()));
    });
    res.json({ ok: true, redirect: safeNext });
  });

  router.post("/api/logout", (req, res) => {
    req.session.destroy((err) => {
      if (err) {
        res.status(500).json({ ok: false, error: "登出失败" });
        return;
      }
      res.clearCookie("board.sid", { path: "/" });
      res.json({ ok: true });
    });
  });

  router.use(
    "/static",
    express.static(path.join(__dirname, "../../public/board"))
  );

  router.get("/", requireAuth, (req, res) => {
    const username = req.session.user!.username;
    const categories = loadHomeCatalogForUser(username);
    res.render("home", {
      basePath: "/board",
      username,
      categories,
      scenarioImagesJson: JSON.stringify(buildScenarioImageMapForUser(username)),
    });
  });

  router.get("/warehouse-calendar", requireAuth, async (req, res) => {
    const now = new Date();
    const qy = Number(req.query.year);
    const qm = Number(req.query.month);
    const year =
      Number.isFinite(qy) && qy >= 2000 && qy <= 2100 ? qy : now.getFullYear();
    const month =
      Number.isFinite(qm) && qm >= 1 && qm <= 12 ? qm : now.getMonth() + 1;

    const monthTitle = new Date(year, month - 1, 1).toLocaleString("zh-CN", {
      year: "numeric",
      month: "long",
    });

    const prev = shiftMonth(year, month, -1);
    const next = shiftMonth(year, month, 1);

    const todayKey = formatDateKey(
      now.getFullYear(),
      now.getMonth() + 1,
      now.getDate()
    );
    const baseLocals = {
      basePath: "/board",
      username: req.session.user!.username,
      year,
      month,
      monthTitle,
      todayKey,
      prevYear: prev.year,
      prevMonth: prev.month,
      nextYear: next.year,
      nextMonth: next.month,
      weekdayLabels: ["一", "二", "三", "四", "五", "六", "日"],
    };

    try {
      const plansByDate = await loadWarehousePlansGroupedByDate(year, month);
      const rows = buildMonthRows(year, month, plansByDate);
      res.render("warehouseCalendar", {
        ...baseLocals,
        rows,
        dataError: null as string | null,
      });
    } catch (err) {
      const msg = err instanceof Error ? err.message : String(err);
      const rows = buildMonthRows(year, month, {});
      res.status(503).render("warehouseCalendar", {
        ...baseLocals,
        rows,
        dataError: `数据加载失败：${msg}`,
      });
    }
  });

  /** 按 datasrc.json 的 id 执行 SQL，参数来自 query（除 id 外均传入存储过程/语句） */
  router.get("/api/datasrc/query", requireAuth, async (req, res) => {
    const id = String(req.query.id ?? "").trim();
    if (!id) {
      res.status(400).json({ ok: false, error: "缺少参数 id" });
      return;
    }
    const raw = { ...req.query } as Record<string, unknown>;
    delete raw.id;
    try {
      const params = coerceParamsFromRequest(raw);
      const data = await executeDataSourceById(id, params);
      res.json({ ok: true, data });
    } catch (e) {
      const msg = e instanceof Error ? e.message : String(e);
      res.status(500).json({ ok: false, error: msg });
    }
  });

  /** 同上，参数来自 JSON body */
  router.post("/api/datasrc/query", requireAuth, async (req, res) => {
    const id = String(req.body?.id ?? "").trim();
    if (!id) {
      res.status(400).json({ ok: false, error: "缺少参数 id" });
      return;
    }
    const raw = { ...(req.body ?? {}) } as Record<string, unknown>;
    delete raw.id;
    try {
      const params = coerceParamsFromRequest(raw);
      const data = await executeDataSourceById(id, params);
      res.json({ ok: true, data });
    } catch (e) {
      const msg = e instanceof Error ? e.message : String(e);
      res.status(500).json({ ok: false, error: msg });
    }
  });

  /** 用户场景管理（仅显示/修改 user 级别） */
  router.get("/config/scenarios", requireAuth, (req, res) => {
    const username = req.session.user!.username;
    const { pages } = loadPagesConfig();
    const userScenarios = loadUserScenariosConfig().users[username]?.scenarios ?? [];
    res.render("userScenarios", {
      basePath: "/board",
      username,
      allPages: pages,
      userScenarios,
      staytimeMin: STAYTIME_MIN,
      staytimeMax: STAYTIME_MAX,
    });
  });

  router.post("/api/user-scenarios/delete", requireAuth, (req, res) => {
    const username = req.session.user!.username;
    const scenarioId = String(req.body?.scenarioId ?? "").trim();
    if (!scenarioId) {
      res.status(400).json({ ok: false, error: "缺少 scenarioId" });
      return;
    }
    const result = deleteUserScenario(username, scenarioId);
    if (!result.ok) {
      res.status(400).json({ ok: false, error: result.error });
      return;
    }
    res.json({ ok: true });
  });

  router.post("/api/user-scenarios/create", requireAuth, (req, res) => {
    const username = req.session.user!.username;
    const name = String(req.body?.name ?? "");
    const pages = Array.isArray(req.body?.pages) ? req.body.pages : [];
    const pageRefs = pages.map((x: any) => ({
      pageId: String(x?.pageId ?? "").trim(),
      staytime: Number(x?.staytime),
    }));
    const result = createUserScenario(username, name, pageRefs);
    if (!result.ok) {
      res.status(400).json({ ok: false, error: result.error });
      return;
    }
    res.json({ ok: true, scenario: result.scenario });
  });

  /** 默认：多 iframe 叠放、按 staytime 切换可见层（不轮改 src） */
  router.get("/view", requireAuth, (req, res) => {
    const scenarioId = String(req.query.scenarioId ?? "");
    const pageIdHint = String(req.query.pageId ?? "");
    const username = req.session.user!.username;
    const data = buildScenarioViewSlides(appCfg, username, scenarioId, pageIdHint);
    if (!data) {
      res.status(404).send("场景不存在");
      return;
    }
    res.render("view", {
      basePath: "/board",
      ...data,
    });
  });

  /** 旧版：单 iframe 定时切换 src */
  router.get("/view-i", requireAuth, (req, res) => {
    const scenarioId = String(req.query.scenarioId ?? "");
    const pageIdHint = String(req.query.pageId ?? "");
    const username = req.session.user!.username;
    const data = buildScenarioViewSlides(appCfg, username, scenarioId, pageIdHint);
    if (!data) {
      res.status(404).send("场景不存在");
      return;
    }
    res.render("view-i", {
      basePath: "/board",
      documentTitle: data.documentTitle,
      scenarioId: data.scenarioId,
      scenarioName: data.scenarioName,
      scenarioImage: data.scenarioImage,
      slidesJson: data.slidesJson,
      startIndex: data.startIndex,
    });
  });

  /** 兼容旧链接，重定向到 /view */
  router.get("/view-tabs", requireAuth, (req, res) => {
    const q = req.url.includes("?")
      ? req.url.slice(req.url.indexOf("?"))
      : "";
    res.redirect(302, "/board/view" + q);
  });

  return router;
}
