// 制度の試算（市民向けクイック見積もり）モジュール
// ねらい: 各標準準拠システム（国保・後期・介護・年金・児童手当・証明手数料）の
//         算定アルゴリズムを市民が来庁・ログイン前でも自分で試算できるよう、ポータルに集約。
// 各バックオフィスシステムの compute 関数と同一ロジック（令和6年度の標準パラメータを定数化）を
// 移植しているため、窓口（職員側のクイック算定UI）と同じ結果になる。
// 金額・率は政令／条例パラメータ＝あくまで標準例示。実際の賦課額は自治体の条例で異なる。

// ── 国民健康保険料（KokuhoWebSystem computeKokuhoPremium と同一）──
const KOKUHO_RATES = {
  basicDeduction: 430000,
  components: {
    medical: { incomeRate: 0.072, perCapita: 29000, perHousehold: 21000, cap: 650000 },
    support: { incomeRate: 0.025, perCapita: 11000, perHousehold: 7000, cap: 240000 },
    longterm: { incomeRate: 0.021, perCapita: 14000, perHousehold: 6000, cap: 170000 },
  },
  reduction: { base: 430000, fivePerPerson: 295000, twoPerPerson: 545000 },
};
export function computeKokuhoPremium(members, rates = KOKUHO_RATES) {
  const ms = (members || []).map((m) => ({ income: Number(m.income) || 0, age: Number(m.age) || 0 }));
  const N = ms.length;
  if (N === 0) return { total: 0, breakdown: { medical: 0, support: 0, longterm: 0 }, insuredCount: 0 };
  const baseTotal = ms.reduce((s, m) => s + Math.max(0, m.income - rates.basicDeduction), 0);
  const Nc = ms.filter((m) => m.age >= 40 && m.age < 65).length;
  const judge = ms.reduce((s, m) => s + Math.max(0, m.income), 0);
  let reductionRate = 0;
  if (judge <= rates.reduction.base) reductionRate = 0.7;
  else if (judge <= rates.reduction.base + rates.reduction.fivePerPerson * N) reductionRate = 0.5;
  else if (judge <= rates.reduction.base + rates.reduction.twoPerPerson * N) reductionRate = 0.2;
  const calcComponent = (key, count) => {
    if (count === 0) return 0;
    const c = rates.components[key];
    const shotoku = Math.floor(baseTotal * c.incomeRate);
    const reduced = Math.floor((c.perCapita * count + c.perHousehold) * (1 - reductionRate));
    return Math.min(c.cap, shotoku + reduced);
  };
  const breakdown = {
    medical: calcComponent("medical", N),
    support: calcComponent("support", N),
    longterm: calcComponent("longterm", Nc),
  };
  return {
    total: breakdown.medical + breakdown.support + breakdown.longterm,
    breakdown, reductionRate, baseTotal, insuredCount: N, longtermInsuredCount: Nc,
  };
}

// ── 後期高齢者医療保険料（ElderlyHealthcareSystem computeKoukiPremium と同一）──
const KOUKI_RATES = {
  basicDeduction: 430000, perCapita: 50000, incomeRate: 0.0954, cap: 800000,
  reduction: { base: 430000, fivePerPerson: 295000, twoPerPerson: 545000 },
};
export function computeKoukiPremium({ income, dependents = 0 }, rates = KOUKI_RATES) {
  const inc = Math.max(0, Number(income) || 0);
  const dep = Math.max(0, Number(dependents) || 0);
  const N = dep + 1;
  const incomeLevy = Math.floor(Math.max(0, inc - rates.basicDeduction) * rates.incomeRate);
  let reductionRate = 0;
  if (inc <= rates.reduction.base) reductionRate = 0.7;
  else if (inc <= rates.reduction.base + rates.reduction.fivePerPerson * N) reductionRate = 0.5;
  else if (inc <= rates.reduction.base + rates.reduction.twoPerPerson * N) reductionRate = 0.2;
  const perCapitaLevy = Math.floor(rates.perCapita * (1 - reductionRate));
  const total = Math.min(rates.cap, incomeLevy + perCapitaLevy);
  return { total, incomeLevy, perCapitaLevy, reductionRate, capped: incomeLevy + perCapitaLevy > rates.cap };
}

// ── 介護保険料（LongTermCareInsuranceSystem_Web と同一の13段階係数）──
const KAIGO_COEFFICIENTS = [0.285, 0.485, 0.685, 0.90, 1.00, 1.20, 1.30, 1.50, 1.70, 1.90, 2.10, 2.30, 2.40];
export function determineKaigoStage({ isWelfareOrOldAgePension = false, householdAllNonTaxable = false, selfTaxable = false, judgeIncome = 0, totalIncome = 0 }) {
  const ji = Number(judgeIncome) || 0;
  const ti = Number(totalIncome) || 0;
  if (!selfTaxable) {
    if (householdAllNonTaxable) {
      if (isWelfareOrOldAgePension || ji <= 800000) return 1;
      if (ji <= 1200000) return 2;
      return 3;
    }
    return ji <= 800000 ? 4 : 5;
  }
  if (ti < 1200000) return 6;
  if (ti < 2100000) return 7;
  if (ti < 3200000) return 8;
  if (ti < 4200000) return 9;
  if (ti < 5200000) return 10;
  if (ti < 6200000) return 11;
  if (ti < 7200000) return 12;
  return 13;
}
export function computeKaigoPremium({ baseAnnual, stage, ...criteria }, coefficients = KAIGO_COEFFICIENTS) {
  const base = Number(baseAnnual) || 0;
  const st = stage != null ? Number(stage) : determineKaigoStage(criteria);
  const idx = Math.min(coefficients.length, Math.max(1, st)) - 1;
  const coefficient = coefficients[idx];
  const annual = Math.floor(base * coefficient);
  return { stage: idx + 1, coefficient, annual, monthly: Math.floor(annual / 12) };
}

// ── 国民年金保険料・免除区分（KokuminNenkinSystem_Web computeNenkin と同一）──
const NENKIN_RATES = {
  monthlyByYear: { 2024: 16980, 2025: 17510 },
  additional: 400,
  exemption: {
    full: (dep) => (dep + 1) * 350000 + 320000,
    threeQuarter: (dep) => 880000 + dep * 380000,
    half: (dep) => 1280000 + dep * 380000,
    quarter: (dep) => 1680000 + dep * 380000,
  },
};
export function determineNenkinExemption(income, dependents = 0, rates = NENKIN_RATES) {
  const inc = Math.max(0, Number(income) || 0);
  const dep = Math.max(0, Number(dependents) || 0);
  if (inc <= rates.exemption.full(dep)) return { category: "全額免除", payRatio: 0 };
  if (inc <= rates.exemption.threeQuarter(dep)) return { category: "4分の3免除", payRatio: 0.25 };
  if (inc <= rates.exemption.half(dep)) return { category: "半額免除", payRatio: 0.5 };
  if (inc <= rates.exemption.quarter(dep)) return { category: "4分の1免除", payRatio: 0.75 };
  return { category: "該当なし（全額納付）", payRatio: 1 };
}
export function computeNenkin({ year = 2024, income, dependents = 0, withAdditional = false }, rates = NENKIN_RATES) {
  const monthly = rates.monthlyByYear[year] ?? rates.monthlyByYear[2024];
  const ex = income === undefined || income === null || income === "" ? { category: "判定なし", payRatio: 1 } : determineNenkinExemption(income, dependents, rates);
  const additional = withAdditional ? rates.additional : 0;
  const payableMonthly = Math.round(monthly * ex.payRatio) + additional;
  return { year, monthlyPremium: monthly, additional, exemption: ex.category, payRatio: ex.payRatio, payableMonthly, payableAnnual: payableMonthly * 12 };
}

// ── 児童手当（ChildAllowanceSystem computeChildAllowance と同一）──
function fiscalYearEndAfterAge(birthDate, age) {
  const b = new Date(birthDate);
  const reachYear = b.getFullYear() + age;
  const yearEnd = b.getMonth() <= 2 ? reachYear : reachYear + 1;
  return new Date(yearEnd, 2, 31, 23, 59, 59, 999);
}
export function computeChildAllowance(children, asOf = new Date()) {
  const now = asOf instanceof Date ? asOf : new Date(asOf);
  const counted = (children || [])
    .filter((c) => c && c.birthDate && now <= fiscalYearEndAfterAge(c.birthDate, 22))
    .map((c) => ({ ...c, _b: new Date(c.birthDate) }))
    .sort((a, b) => a._b - b._b);
  const lines = [];
  let total = 0;
  counted.forEach((c, idx) => {
    const position = idx + 1;
    const payTarget = now <= fiscalYearEndAfterAge(c.birthDate, 18);
    if (!payTarget) {
      lines.push({ position, amount: 0, note: "算定対象のみ（支給対象外）" });
      return;
    }
    const thirdBirthday = new Date(c._b.getFullYear() + 3, c._b.getMonth(), c._b.getDate());
    const under3 = now < thirdBirthday;
    const amount = position >= 3 ? 30000 : under3 ? 15000 : 10000;
    total += amount;
    lines.push({ position, under3, amount });
  });
  return { totalMonthly: total, countedChildren: counted.length, payTargetChildren: lines.filter((l) => l.amount > 0).length, children: lines };
}

// ── 証明書手数料（juki computeJuminCertFee と同一）──
const JUMIN_CERT_FEES = {
  "住民票の写し": 300, "住民票記載事項証明書": 300, "印鑑登録証明書": 300,
  "課税証明書／所得証明書": 300, "戸籍全部事項証明書": 450, "戸籍の附票の写し": 300,
};
const JUMIN_POSTAL_FEE = 140;
export function computeCertFee({ certType, copies = 1, postal = false }) {
  const unit = JUMIN_CERT_FEES[certType];
  if (unit === undefined) return { error: "unknown certType", certType };
  const n = Math.max(1, Number(copies) || 1);
  const subtotal = unit * n;
  const postalFee = postal ? JUMIN_POSTAL_FEE : 0;
  return { certType, unitFee: unit, copies: n, subtotal, postalFee, total: subtotal + postalFee };
}

// ── 試算カタログ（市民向け）──
// fields.type: number | select | members（"所得:年齢"を改行区切り）| dates（生年月日を改行区切り）
// run(input) → { primary:{label,value,format}, details:[[label,value],...] }（format: yen|yenMonthly|text）
const yen = (n) => `${Number(n).toLocaleString("ja-JP")}円`;
const pct = (r) => `${Math.round(r * 100)}%`;

export const ESTIMATORS = [
  {
    id: "kokuho", name: "国民健康保険料", category: "health", categoryLabel: "健康・医療・介護",
    system: "KokuhoWebSystem",
    summary: "世帯の被保険者の所得・年齢から年間保険料の目安を試算します。",
    apply: { kind: "proc", typeId: "kokuho-join", label: "国民健康保険の加入・喪失届へ" },
    fields: [
      { key: "members", label: "世帯の被保険者（「総所得:年齢」を1人1行で）", type: "members",
        placeholder: "2000000:45\n0:70", required: true },
    ],
    run(input) {
      const r = computeKokuhoPremium(input.members);
      return {
        primary: { label: "年間保険料の目安", value: r.total, format: "yen" },
        details: [
          ["医療分", yen(r.breakdown.medical)],
          ["後期高齢者支援金分", yen(r.breakdown.support)],
          ["介護納付金分（40〜64歳）", yen(r.breakdown.longterm)],
          ["軽減割合", r.reductionRate ? pct(r.reductionRate) : "なし"],
          ["被保険者数", `${r.insuredCount}人`],
        ],
      };
    },
  },
  {
    id: "kouki", name: "後期高齢者医療保険料", category: "health", categoryLabel: "健康・医療・介護",
    system: "ElderlyHealthcareSystem",
    summary: "75歳以上の方の総所得から年間保険料の目安を試算します。",
    apply: { kind: "proc", typeId: "elderly-genngaku", label: "限度額適用認定の申請へ" },
    fields: [
      { key: "income", label: "本人の総所得金額（円）", type: "number", placeholder: "1500000", required: true },
      { key: "dependents", label: "同一世帯の後期被保険者数（本人を除く）", type: "number", placeholder: "0" },
    ],
    run(input) {
      const r = computeKoukiPremium({ income: input.income, dependents: input.dependents });
      return {
        primary: { label: "年間保険料の目安", value: r.total, format: "yen" },
        details: [
          ["所得割", yen(r.incomeLevy)],
          ["均等割", yen(r.perCapitaLevy)],
          ["軽減割合", r.reductionRate ? pct(r.reductionRate) : "なし"],
          ["賦課限度額超過", r.capped ? "あり（限度額で頭打ち）" : "なし"],
        ],
      };
    },
  },
  {
    id: "kaigo", name: "介護保険料（第1号）", category: "health", categoryLabel: "健康・医療・介護",
    system: "LongTermCareInsuranceSystem_Web",
    summary: "65歳以上の方の課税状況・所得段階から保険料の目安を試算します。",
    apply: { kind: "proc", typeId: "kaigo-nintei", label: "要介護・要支援認定の申請へ" },
    fields: [
      { key: "household", label: "世帯の課税状況", type: "select", required: true, options: [
        { value: "allNonTaxable", label: "世帯全員が住民税非課税" },
        { value: "selfNonTaxable", label: "本人は非課税・世帯に課税者あり" },
        { value: "selfTaxable", label: "本人が住民税課税" },
      ] },
      { key: "income", label: "本人の合計所得金額（円）", type: "number", placeholder: "1000000" },
      { key: "baseAnnual", label: "お住まいの基準額（年額・任意）", type: "number", placeholder: "75000" },
    ],
    run(input) {
      const baseAnnual = Number(input.baseAnnual) || 75000;
      const inc = Number(input.income) || 0;
      const crit = input.household === "selfTaxable"
        ? { selfTaxable: true, totalIncome: inc, judgeIncome: inc }
        : input.household === "selfNonTaxable"
        ? { selfTaxable: false, householdAllNonTaxable: false, judgeIncome: inc }
        : { selfTaxable: false, householdAllNonTaxable: true, judgeIncome: inc };
      const r = computeKaigoPremium({ baseAnnual, ...crit });
      return {
        primary: { label: "年間保険料の目安", value: r.annual, format: "yen" },
        details: [
          ["所得段階", `第${r.stage}段階 / 全13段階`],
          ["段階別係数", `×${r.coefficient}`],
          ["月額の目安", yen(r.monthly)],
          ["基準額（年額）", yen(baseAnnual)],
        ],
      };
    },
  },
  {
    id: "nenkin", name: "国民年金保険料・免除", category: "tax", categoryLabel: "税・年金",
    system: "KokuminNenkinSystem_Web",
    summary: "前年所得から保険料と申請免除区分（全額〜4分の1）の目安を試算します。",
    apply: { kind: "proc", typeId: "nenkin-exempt", label: "保険料の免除・納付猶予を申請する" },
    fields: [
      { key: "year", label: "年度", type: "select", options: [
        { value: "2024", label: "令和6年度（2024）" }, { value: "2025", label: "令和7年度（2025）" },
      ] },
      { key: "income", label: "前年の所得金額（円）", type: "number", placeholder: "500000" },
      { key: "dependents", label: "扶養親族の人数", type: "number", placeholder: "0" },
      { key: "withAdditional", label: "付加保険料（月400円）", type: "select", options: [
        { value: "", label: "納めない" }, { value: "1", label: "納める" },
      ] },
    ],
    run(input) {
      const r = computeNenkin({
        year: Number(input.year) || 2024,
        income: input.income === "" || input.income == null ? undefined : Number(input.income),
        dependents: Number(input.dependents) || 0,
        withAdditional: !!input.withAdditional,
      });
      return {
        primary: { label: "実際に納める月額の目安", value: r.payableMonthly, format: "yenMonthly" },
        details: [
          ["免除区分", r.exemption],
          ["定額保険料（月額）", yen(r.monthlyPremium)],
          ["納付割合", pct(r.payRatio)],
          ["付加保険料", r.additional ? yen(r.additional) : "なし"],
          ["年額の目安", yen(r.payableAnnual)],
        ],
      };
    },
  },
  {
    id: "child-allowance", name: "児童手当", category: "child", categoryLabel: "子育て・教育",
    system: "ChildAllowanceSystem",
    summary: "お子さまの生年月日から月額の手当の目安を試算します（所得制限は2024年10月廃止）。",
    apply: { kind: "proc", typeId: "childallowance", label: "児童手当を申請する" },
    fields: [
      { key: "births", label: "お子さまの生年月日（1人1行・古い順でなくて可）", type: "dates",
        placeholder: "2016-05-01\n2020-09-10\n2023-02-15", required: true },
    ],
    run(input) {
      const children = (input.births || []).map((birthDate) => ({ birthDate }));
      const r = computeChildAllowance(children);
      return {
        primary: { label: "毎月の手当の目安", value: r.totalMonthly, format: "yenMonthly" },
        details: [
          ["支給対象のお子さま", `${r.payTargetChildren}人`],
          ["多子カウント対象", `${r.countedChildren}人（22歳年度末まで）`],
          ["年額の目安", yen(r.totalMonthly * 12)],
          ...r.children.map((c, i) => [`第${c.position}子`, c.amount ? `${yen(c.amount)}/月${c.under3 ? "（3歳未満）" : ""}` : "支給対象外"]),
        ],
      };
    },
  },
  {
    id: "cert-fee", name: "証明書の手数料", category: "family", categoryLabel: "戸籍・証明",
    system: "juki-web-reference",
    summary: "証明書の種類・部数・受取方法から手数料を試算します。",
    apply: { kind: "cert", label: "この証明書を請求する" },
    fields: [
      { key: "certType", label: "証明書の種類", type: "select", required: true, options: [
        { value: "住民票の写し", label: "住民票の写し" },
        { value: "住民票記載事項証明書", label: "住民票記載事項証明書" },
        { value: "印鑑登録証明書", label: "印鑑登録証明書" },
        { value: "課税証明書／所得証明書", label: "課税証明書／所得証明書" },
        { value: "戸籍全部事項証明書", label: "戸籍全部事項証明書" },
        { value: "戸籍の附票の写し", label: "戸籍の附票の写し" },
      ] },
      { key: "copies", label: "部数", type: "number", placeholder: "1" },
      { key: "postal", label: "受取方法", type: "select", options: [
        { value: "", label: "コンビニ交付・窓口（送料なし）" }, { value: "1", label: "郵送（＋送料140円）" },
      ] },
    ],
    run(input) {
      const r = computeCertFee({ certType: input.certType, copies: input.copies, postal: !!input.postal });
      if (r.error) return { error: "証明書の種類が不正です。" };
      return {
        primary: { label: "手数料の合計", value: r.total, format: "yen" },
        details: [
          ["1部あたり", yen(r.unitFee)],
          ["部数", `${r.copies}部`],
          ["小計", yen(r.subtotal)],
          ["送料", r.postalFee ? yen(r.postalFee) : "なし"],
        ],
      };
    },
  },
];

// 入力の正規化（web からの生の文字列を各 run が期待する型へ）
export function normalizeEstimateInput(estimator, raw) {
  const input = {};
  for (const f of estimator.fields) {
    const v = raw ? raw[f.key] : undefined;
    if (f.type === "members") {
      input[f.key] = String(v || "").split(/\n+/).map((line) => line.trim()).filter(Boolean).map((line) => {
        const [income, age] = line.split(":").map((s) => s.trim());
        return { income: Number(income) || 0, age: Number(age) || 0 };
      });
    } else if (f.type === "dates") {
      input[f.key] = String(v || "").split(/\n+/).map((s) => s.trim()).filter(Boolean);
    } else if (f.type === "number") {
      input[f.key] = v === "" || v == null ? "" : Number(v);
    } else {
      input[f.key] = v == null ? "" : String(v);
    }
  }
  return input;
}

// web へ渡すカタログ（run を除く）
export function estimatorCatalog() {
  return ESTIMATORS.map(({ id, name, category, categoryLabel, summary, system, fields, apply }) => ({
    id, name, category, categoryLabel, summary, system, fields, apply: apply || null,
  }));
}

export function runEstimate(id, rawInput) {
  const est = ESTIMATORS.find((e) => e.id === id);
  if (!est) return { error: "試算メニューが見つかりません。" };
  const input = normalizeEstimateInput(est, rawInput);
  const result = est.run(input);
  if (result.error) return result;
  // apply: 試算→申請の導線情報。echo: cert などのプリフィル用に生の入力を返す。
  return { id: est.id, name: est.name, apply: est.apply || null, echo: rawInput, ...result };
}
