import test from "node:test";
import assert from "node:assert/strict";
import { once } from "node:events";
import { spawn } from "node:child_process";
import {
  ESTIMATORS, estimatorCatalog, runEstimate,
  computeKokuhoPremium, computeCertFee, computeChildAllowance, computeNenkin,
} from "../apps/api/estimators.mjs";

// ── モジュール単体テスト（窓口の標準準拠システムと同一結果になることを確認）──
test("試算カタログは run を含まず、各メニューに fields がある", () => {
  const cat = estimatorCatalog();
  assert.equal(cat.length, ESTIMATORS.length);
  for (const e of cat) {
    assert.ok(e.id && e.name && Array.isArray(e.fields));
    assert.equal(e.run, undefined);
  }
});

test("国保: 200万単身45歳 = 273,259円（KokuhoWebSystemと一致）", () => {
  const r = runEstimate("kokuho", { members: "2000000:45" });
  assert.equal(r.primary.value, 273259);
  assert.equal(r.primary.format, "yen");
  // 軽減判定: 所得0/70歳は7割軽減・介護分0
  const low = computeKokuhoPremium([{ income: 0, age: 70 }]);
  assert.equal(low.reductionRate, 0.7);
  assert.equal(low.breakdown.longterm, 0);
});

test("証明手数料: 戸籍全部事項×2＋郵送 = 1,040円", () => {
  const r = runEstimate("cert-fee", { certType: "戸籍全部事項証明書", copies: "2", postal: "1" });
  assert.equal(r.primary.value, 450 * 2 + 140);
  // 不正な証明種別は error
  assert.ok(computeCertFee({ certType: "存在しない証明", copies: 1 }).error);
});

test("児童手当: 8/5/2歳 = 50,000円/月（第3子30,000含む）", () => {
  const r = runEstimate("child-allowance", { births: "2016-05-01\n2020-09-10\n2023-02-15" });
  assert.equal(r.primary.value, 50000);
  assert.equal(r.primary.format, "yenMonthly");
  // 大学生年代(多子カウント対象)＋高校生＋中学生 → 第3子が30,000
  const counted = computeChildAllowance([
    { birthDate: "2004-05-01" }, { birthDate: "2008-05-01" }, { birthDate: "2012-05-01" },
  ], new Date("2026-06-09"));
  assert.equal(counted.countedChildren, 3);
});

test("国民年金: 所得50万は全額免除→納付0円 / 高所得は全額納付", () => {
  const exempt = runEstimate("nenkin", { year: "2024", income: "500000", dependents: "0", withAdditional: "" });
  assert.equal(exempt.primary.value, 0);
  const full = computeNenkin({ year: 2024, income: 5000000, dependents: 0 });
  assert.equal(full.exemption, "該当なし（全額納付）");
  assert.equal(full.payableMonthly, 16980);
});

test("不明な試算IDは error を返す", () => {
  assert.ok(runEstimate("does-not-exist", {}).error);
});

test("試算→申請の導線: 各メニューに apply(関連手続き)があり、結果に echo が付く", () => {
  // カタログに apply が含まれる
  for (const e of estimatorCatalog()) {
    assert.ok(e.apply && e.apply.kind, `${e.id} に apply がない`);
  }
  // proc 系は typeId、cert 系は kind=cert
  assert.equal(runEstimate("child-allowance", { births: "2023-02-15" }).apply.typeId, "childallowance");
  assert.equal(runEstimate("nenkin", { income: "500000" }).apply.typeId, "nenkin-exempt");
  const cert = runEstimate("cert-fee", { certType: "戸籍全部事項証明書", copies: "2", postal: "1" });
  assert.equal(cert.apply.kind, "cert");
  // echo に生入力が返り、cert のプリフィル（種別名・部数）に使える
  assert.equal(cert.echo.certType, "戸籍全部事項証明書");
  assert.equal(cert.echo.copies, "2");
});

// ── エンドポイント結合テスト ──
async function startServer() {
  const port = 20000 + Math.floor(Math.random() * 20000);
  const child = spawn(process.execPath, ["apps/api/server.mjs"], {
    env: { ...process.env, PORT: String(port) }, stdio: ["ignore", "pipe", "pipe"],
  });
  await Promise.race([
    once(child.stdout, "data"),
    once(child.stderr, "data").then(([c]) => { throw new Error(c.toString()); }),
  ]);
  return { child, base: `http://127.0.0.1:${port}` };
}

test("GET /api/estimate-types と POST /api/estimate はログイン不要で動く", async () => {
  const { child, base } = await startServer();
  try {
    const list = await fetch(`${base}/api/estimate-types`);
    assert.equal(list.status, 200);
    const types = await list.json();
    assert.ok(types.length >= 6);

    const post = await fetch(`${base}/api/estimate`, {
      method: "POST", headers: { "content-type": "application/json" },
      body: JSON.stringify({ id: "kokuho", input: { members: "2000000:45" } }),
    });
    assert.equal(post.status, 200);
    const r = await post.json();
    assert.equal(r.primary.value, 273259);

    const bad = await fetch(`${base}/api/estimate`, {
      method: "POST", headers: { "content-type": "application/json" },
      body: JSON.stringify({ id: "nope", input: {} }),
    });
    assert.equal(bad.status, 400);
  } finally {
    child.kill();
  }
});
