// cremation-permit-system — 固有ロジック（共通coreへ委譲される per-system handle）
import { join } from "node:path";

// ── 火葬・埋葬の手数料／火葬場使用料の算定（標準仕様書: 許可手数料＋使用料。額は条例パラメータ）──
//   許可手数料は無料の自治体が多い。使用料は市内/市外×区分（大人/小人/死産児等）で異なる（例示）。
const CREMATION_PERMIT_FEE = { "死体火葬許可": 0, "死体埋葬許可": 0, "死胎火葬許可": 0, "死胎埋葬許可": 0 };
const CREMATORIUM_USE_FEE = {
  resident: { "大人": 0, "小人": 0, "死産児": 0 },       // 市内
  nonResident: { "大人": 50000, "小人": 30000, "死産児": 10000 }, // 市外
};
function computeCremationFee({ permitType = "死体火葬許可", residency = "resident", bodyClass = "大人", useCrematorium = true }) {
  const permitFee = CREMATION_PERMIT_FEE[permitType];
  if (permitFee === undefined) return { error: "unknown permitType", permitType };
  const table = residency === "nonResident" ? CREMATORIUM_USE_FEE.nonResident : CREMATORIUM_USE_FEE.resident;
  const useFee = useCrematorium ? (table[bodyClass] ?? 0) : 0;
  return { permitType, permitFee, residency, bodyClass, crematoriumUseFee: useFee, total: permitFee + useFee };
}

export async function handle(ctx) {
  const { req, res, pathname, searchParams, actor, send, sendRaw, parseBody, readJson, writeJson, audit,
    maskRecord, validateRecord, requireRole, hasRole, dataRoot, restrictionsFile, crypto,
    dbSelect, dbSelectOne, dbInsert, dbUpdate, dbDelete } = ctx;
  const reply = (...a) => { send(...a); return true; };
  const cremationPermitsFile = join(dataRoot, "cremation-permits.json");


    // ── 火葬許可 (Cremation Permits) ─────────────────────────────────────

    if (pathname === "/api/cremation-permits" && req.method === "GET") {
      requireRole(actor, "CLERK");
      const { status } = Object.fromEntries(new URL(req.url, "http://x").searchParams);
      const filter = status ? { status } : {};
      const permits = (await dbSelect("cremation_permit.cremation_permit", filter)) ?? await readJson(cremationPermitsFile, []).then((all) => status ? all.filter((p) => p.status === status) : all);
      return reply(res, 200, permits);
    }

    if (pathname === "/api/cremation-permits" && req.method === "POST") {
      requireRole(actor, "CLERK");
      const body = await parseBody(req);
      if (!body.deceasedName || !body.deceasedBirthDate || !body.deathDate || !body.applicantName) return reply(res, 400, { error: "deceasedName, deceasedBirthDate, deathDate, applicantName required" });
      const permitNumber = "KS-" + Date.now().toString(36).toUpperCase();
      const permit = { id: crypto.randomUUID(), permitNumber, deceasedName: body.deceasedName, deceasedBirthDate: body.deceasedBirthDate, deathDate: body.deathDate, deathPlace: body.deathPlace ?? "", applicantName: body.applicantName, relationship: body.relationship ?? "", cremationFacility: body.cremationFacility ?? "", status: "applied", issuedAt: null, issuedBy: null, createdBy: actor.id, createdAt: new Date().toISOString() };
      const inserted = await dbInsert("cremation_permit.cremation_permit", permit);
      if (inserted === null) {
        const permits = await readJson(cremationPermitsFile, []);
        permits.push(permit);
        await writeJson(cremationPermitsFile, permits);
      }
      await audit(req, "cremation-permit.apply", { id: permit.id, permitNumber });
      return reply(res, 201, inserted ?? permit);
    }

    const permitMatch = pathname.match(/^\/api\/cremation-permits\/([^/]+)$/);
    if (permitMatch && (req.method === "GET" || req.method === "PUT")) {
      requireRole(actor, "CLERK");
      const id = permitMatch[1];
      const permit = (await dbSelectOne("cremation_permit.cremation_permit", id))
        ?? (await readJson(cremationPermitsFile, [])).find((p) => p.id === id);
      if (!permit) return reply(res, 404, { error: "permit not found" });
      if (req.method === "GET") return reply(res, 200, permit);

      const body = await parseBody(req);
      const editable = ["deceasedName", "deceasedBirthDate", "deathDate", "deathPlace", "applicantName", "relationship", "cremationFacility"];
      const patch = {};
      for (const k of editable) if (body[k] !== undefined) patch[k] = body[k];
      patch.updatedAt = new Date().toISOString();
      patch.updatedBy = actor.id;
      const updated = await dbUpdate("cremation_permit.cremation_permit", id, patch);
      let result = updated;
      if (updated === null) {
        const permits = await readJson(cremationPermitsFile, []);
        const idx = permits.findIndex((p) => p.id === id);
        permits[idx] = { ...permits[idx], ...patch };
        await writeJson(cremationPermitsFile, permits);
        result = permits[idx];
      }
      await audit(req, "cremation-permit.update", { id, fields: Object.keys(patch).filter((k) => k !== "updatedAt" && k !== "updatedBy") });
      return reply(res, 200, result);
    }

    const permitIssueMatch = pathname.match(/^\/api\/cremation-permits\/([^/]+)\/issue$/);
    if (permitIssueMatch && req.method === "POST") {
      requireRole(actor, "CLERK");
      const id = permitIssueMatch[1];
      const existingPermit = (await dbSelectOne("cremation_permit.cremation_permit", id))
        ?? (await readJson(cremationPermitsFile, [])).find((p) => p.id === id);
      if (!existingPermit) return reply(res, 404, { error: "permit not found" });
      if (existingPermit.status !== "applied") return reply(res, 409, { error: "permit is not in applied status" });
      const issuedAt = new Date().toISOString();
      const issuedBy = actor.id;
      const updated = await dbUpdate("cremation_permit.cremation_permit", id, { status: "issued", issuedAt, issuedBy });
      let result;
      if (updated === null) {
        const permits = await readJson(cremationPermitsFile, []);
        const permit = permits.find((p) => p.id === id);
        permit.status = "issued";
        permit.issuedAt = issuedAt;
        permit.issuedBy = issuedBy;
        await writeJson(cremationPermitsFile, permits);
        result = permit;
      } else {
        result = updated;
      }
      await audit(req, "cremation-permit.issue", { id: result.id, permitNumber: result.permitNumber });
      return reply(res, 200, result);
    }


    const permitDeleteMatch = pathname.match(/^\/api\/cremation-permits\/([^\/]+)$/);
    if (permitDeleteMatch && req.method === "DELETE") {
      requireRole(actor, "ADMIN");
      const delId = permitDeleteMatch[1];
      const deleted = await dbDelete("cremation_permit.cremation_permit", delId);
      if (deleted === null) {
        const _items = await readJson(cremationPermitsFile, []);
        const _idx = _items.findIndex((x) => x.id === delId);
        if (_idx === -1) return reply(res, 404, { error: "permit not found" });
        _items.splice(_idx, 1);
        await writeJson(cremationPermitsFile, _items);
      }
      await audit(req, "cremation-permit.delete", { id: delId });
      return reply(res, 204, {});
    }


    // ── 火葬・埋葬の手数料／火葬場使用料の算定（標準仕様書準拠）──
    if (pathname === "/api/cremation/fee" && req.method === "POST") {
      requireRole(actor, "CLERK");
      const body = await parseBody(req);
      const result = computeCremationFee(body || {});
      if (result.error) return reply(res, 400, result);
      await audit(req, "cremation.fee-calc", { permitType: result.permitType, total: result.total });
      return reply(res, 200, result);
    }

    // ════ 文字要件・管掌者自動切替・交付簿・公印/発行番号印字（標準仕様書）════

    const cremationKanshoushaFile = join(dataRoot, "cremation-kanshousha.json");
    const cremationSealFile = join(dataRoot, "cremation-seal.json");

    // ── 文字要件（標準仕様書）準拠チェック ──
    if (pathname === "/api/cremation/charset/validate" && req.method === "POST") {
      requireRole(actor, "CLERK");
      const b = await parseBody(req);
      const text = String(b.text ?? "");
      if (!text) return reply(res, 400, { error: "text required" });
      // 文字要件: JIS X 0213の範囲内（サロゲートペア=範囲外文字、私用領域=外字を検出）
      const issues = [];
      for (const ch of text) {
        const cp = ch.codePointAt(0);
        if (cp >= 0xE000 && cp <= 0xF8FF) issues.push({ char: ch, issue: "私用領域（外字）" });
        else if (cp > 0xFFFF) issues.push({ char: ch, issue: "JIS X 0213範囲外（サロゲート）" });
      }
      return reply(res, 200, { standard: "標準仕様書（文字要件）準拠チェック", ok: issues.length === 0, issues });
    }

    // ── 管掌者の登録（職務開始日・終了日／適用開始日・終了日で自動切替）と出力時選択 ──
    if (pathname === "/api/cremation/kanshousha" && req.method === "POST") {
      requireRole(actor, "ADMIN");
      const b = await parseBody(req);
      if (!b.name || !b.title || !b.startDate) return reply(res, 400, { error: "name, title, startDate required" });
      const list = await readJson(cremationKanshoushaFile, []);
      const item = { id: crypto.randomUUID(), name: String(b.name), title: String(b.title),
        startDate: String(b.startDate), endDate: b.endDate ? String(b.endDate) : null,
        registeredAt: new Date().toISOString(), by: actor.id };
      list.unshift(item);
      await writeJson(cremationKanshoushaFile, list);
      await audit(req, "cremation-kanshousha.register", { name: item.name });
      return reply(res, 201, item);
    }
    if (pathname === "/api/cremation/kanshousha" && req.method === "GET") {
      requireRole(actor, "CLERK");
      const asOf = searchParams.get("asOf") ?? new Date().toISOString().slice(0, 10);
      const list = await readJson(cremationKanshoushaFile, []);
      const active = list.filter((k) => k.startDate <= asOf && (!k.endDate || asOf <= k.endDate));
      return reply(res, 200, { asOf, autoSelected: active[0] ?? null, selectable: list,
        note: "職務開始日・終了日／適用開始日・終了日で自動切替。出力時に選択も可能" });
    }

    // ── 電子公印／「(公印省略)」の設定 ──
    if (pathname === "/api/cremation/seal" && (req.method === "GET" || req.method === "PUT")) {
      requireRole(actor, req.method === "PUT" ? "ADMIN" : "CLERK");
      const defaults = { mode: "電子公印", note: "" };
      let conf = { ...defaults, ...(await readJson(cremationSealFile, {})) };
      if (req.method === "PUT") {
        const b = await parseBody(req);
        const modes = ["電子公印", "公印省略"];
        if (b.mode !== undefined && !modes.includes(b.mode)) return reply(res, 400, { error: `mode(${modes.join("/")})` });
        conf = { ...conf, ...b, updatedAt: new Date().toISOString(), updatedBy: actor.id };
        await writeJson(cremationSealFile, conf);
        await audit(req, "cremation-seal.update", { mode: conf.mode });
      }
      return reply(res, 200, { ...conf, printLine: conf.mode === "公印省略" ? "(公印省略)" : "（電子公印印影）" });
    }

    // ── 許可証の印刷（発行年月日・発行番号・管掌者自動選択・公印印字）＋作成→出力画面遷移 ──
    if (pathname === "/api/cremation/permits/print" && req.method === "POST") {
      requireRole(actor, "CLERK");
      const b = await parseBody(req);
      if (!b.permitId) return reply(res, 400, { error: "permitId required" });
      const permits = await readJson(cremationPermitsFile, []);
      const permit = permits.find((p) => p.id === String(b.permitId));
      if (!permit) return reply(res, 404, { error: "permit not found" });
      const asOf = new Date().toISOString().slice(0, 10);
      const kanshousha = (await readJson(cremationKanshoushaFile, []))
        .find((k) => k.startDate <= asOf && (!k.endDate || asOf <= k.endDate)) ?? null;
      const seal = { mode: "電子公印", ...(await readJson(cremationSealFile, {})) };
      const issueNumber = permit.permitNumber ?? `CR-${Date.now().toString(36).toUpperCase()}`;
      await audit(req, "cremation-permits.print", { permitId: b.permitId });
      return reply(res, 200, { permitId: String(b.permitId), 発行年月日: asOf, 発行番号: issueNumber,
        管掌者: kanshousha ? `${kanshousha.title} ${kanshousha.name}` : "（未設定）",
        公印: seal.mode === "公印省略" ? "(公印省略)" : "電子公印",
        nextScreen: "output", note: "作成画面から出力画面へそのまま遷移可能（nextScreenをUIが解釈）" });
    }

    // ── 交付簿の作成 ──
    if (pathname === "/api/cremation/register-book" && req.method === "GET") {
      requireRole(actor, "CLERK");
      const permits = await readJson(cremationPermitsFile, []);
      const rows = permits.map((p) => ({ permitNumber: p.permitNumber ?? p.id, deceasedName: p.deceasedName ?? "",
        permitType: p.permitType ?? "", issuedAt: p.issuedAt ?? p.createdAt ?? "" }));
      await audit(req, "cremation.register-book", { rows: rows.length });
      return reply(res, 200, { document: "火葬・埋葬許可証 交付簿", count: rows.length, rows });
    }

  return false;
}
