// loan-engine.jsx — 주택담보대출 계산 엔진 (모듈⑤-A)
// ─────────────────────────────────────────────────────────────
// ⚠ 본 엔진의 규제 수치는 아래 LOAN_RULES 한 곳에만 정의한다.
//   규제가 바뀌면 이 테이블만 수정하면 된다 (기준일 주석 필수).
// ⚠ 실제 대출 한도는 은행별 심사(소득 인정 방식·신용도·중도금 등)에
//   따라 달라지므로 모든 결과는 "참고용"이다. UI 에 면책 문구 표기.
// ⚠ 이 파일은 JSX 를 쓰지 않는 순수 JS — Node 로도 단위테스트 가능.
// 금액 단위: 만원 (12억 = 120000)
// ─────────────────────────────────────────────────────────────

// ─── 규제 파라미터 테이블 (기준일: 2026-08, 일반에 알려진 규칙의 근사) ───
const LOAN_RULES = {
  asOf: '2026-08', // UI 에 그대로 표기되는 기준일

  // DSR: 은행권 총부채원리금상환비율 한도 40%
  dsrLimit: 0.40,

  // 스트레스 DSR 3단계 (2025.7 시행): 가산금리 1.5%p 를
  // 금리 유형별 반영 비율만큼 더해 "상환능력 심사용 금리"를 만든다
  stress: {
    addRatePct: 1.5, // 스트레스 가산금리 (%p)
    applyRatio: {
      variable: 1.0,  // 변동금리: 100% 반영
      mixed: 0.6,     // 혼합형(5년 고정 후 변동): 60% 반영
      periodic: 0.3,  // 주기형(5년마다 재산정): 30% 반영
      fixed: 0.0,     // 순수 고정금리: 미반영
    },
  },

  // LTV 한도 (주택 수·규제지역 여부·생애최초 우대)
  // key: 'regulated'(조정대상/투기과열) | 'normal'(비규제)
  ltv: {
    regulated: { first: 0.50, h0: 0.40, h1: 0.40, multi: 0.00 },
    normal:    { first: 0.80, h0: 0.70, h1: 0.60, multi: 0.60 },
  },

  // 수도권·규제지역 주담대 최대 한도 (2025.6.27 가계부채 대책): 6억
  metroLoanCapMan: 60000,

  // 취득세 (주택 유상취득 기준, 지방교육세 포함 계산은 acqTax 참고)
  //  - 1주택: 6억↓ 1% / 6~9억 누진(1~3%) / 9억↑ 3%
  //  - 다주택 중과: 조정지역 2주택 8%·3주택+ 12% / 비조정 3주택 8%·4주택+ 12%
  acq: {
    lowRate: 0.01, highRate: 0.03,
    lowLimitMan: 60000, highLimitMan: 90000,
    surchargeMid: 0.08, surchargeHigh: 0.12,
  },
};

// ─── 상환 방식별 월 납입액·스케줄 ─────────────────────────────
// method: 'annuity'(원리금균등) | 'equal'(원금균등) | 'bullet'(만기일시)
// 반환: { firstMonthly, lastMonthly, totalInterest, totalPay, yearly[] }
//   yearly[i] = { year, principal, interest, balance } (만원, 반올림)
function buildSchedule(loanMan, ratePct, years, method) {
  const n = Math.max(1, Math.round(years * 12));
  const r = ratePct / 100 / 12;
  const out = { firstMonthly: 0, lastMonthly: 0, totalInterest: 0, totalPay: 0, yearly: [] };
  if (loanMan <= 0) return out;

  let balance = loanMan;
  let yp = 0, yi = 0; // 연 단위 누적 (원금/이자)
  let first = 0, last = 0, ti = 0;

  // 원리금균등의 고정 월 납입액
  const annuityPay = r === 0 ? loanMan / n
    : loanMan * r * Math.pow(1 + r, n) / (Math.pow(1 + r, n) - 1);

  for (let m = 1; m <= n; m++) {
    const interest = balance * r;
    let principal;
    if (method === 'equal') principal = loanMan / n;               // 원금균등
    else if (method === 'bullet') principal = (m === n) ? loanMan : 0; // 만기일시
    else principal = annuityPay - interest;                         // 원리금균등
    const pay = principal + interest;
    if (m === 1) first = pay;
    if (m === n) last = pay;
    ti += interest;
    balance = Math.max(0, balance - principal);
    yp += principal; yi += interest;
    if (m % 12 === 0 || m === n) {
      out.yearly.push({
        year: Math.ceil(m / 12),
        principal: Math.round(yp), interest: Math.round(yi),
        balance: Math.round(balance),
      });
      yp = 0; yi = 0;
    }
  }
  out.firstMonthly = first; out.lastMonthly = last;
  out.totalInterest = ti; out.totalPay = loanMan + ti;
  return out;
}

// ─── DSR 기준 최대 대출액 (역산) ─────────────────────────────
// 연소득·기존대출 연상환액에서 남는 상환 여력으로,
// "스트레스 금리 + 원리금균등" 가정 하에 빌릴 수 있는 최대 원금을 구한다.
// (은행 심사도 DSR 산정 시 원리금균등 상당 방식으로 환산하는 것의 근사)
function maxLoanByDSR(annualIncomeMan, existingAnnualPayMan, ratePct, years, rateType) {
  const ratio = LOAN_RULES.stress.applyRatio[rateType] != null
    ? LOAN_RULES.stress.applyRatio[rateType] : 1.0;
  const stressedPct = ratePct + LOAN_RULES.stress.addRatePct * ratio;
  const capacityYear = annualIncomeMan * LOAN_RULES.dsrLimit - existingAnnualPayMan;
  if (capacityYear <= 0) return { maxLoan: 0, stressedPct, capacityMonthly: 0 };
  const pay = capacityYear / 12; // 감당 가능한 월 상환액
  const n = Math.max(1, Math.round(years * 12));
  const r = stressedPct / 100 / 12;
  // 원리금균등 공식 역산: L = pay × (1 − (1+r)^−n) / r
  const maxLoan = r === 0 ? pay * n : pay * (1 - Math.pow(1 + r, -n)) / r;
  return { maxLoan, stressedPct, capacityMonthly: pay };
}

// ─── LTV 기준 최대 대출액 ────────────────────────────────────
// houses: 보유 주택 수(이번 매수분 제외), firstHome: 생애최초 여부
function ltvOf(regulated, houses, firstHome) {
  const t = regulated ? LOAN_RULES.ltv.regulated : LOAN_RULES.ltv.normal;
  if (firstHome && houses === 0) return t.first;
  if (houses === 0) return t.h0;
  if (houses === 1) return t.h1;
  return t.multi;
}

// ─── 종합 한도: LTV vs DSR vs 절대 상한 중 최솟값 ────────────
// 반환: { maxLoan, binding: 'LTV'|'DSR'|'CAP', caps:{ltv,dsr,cap}, ltvPct, stressedPct }
function computeMaxLoan(opt) {
  const { priceMan, annualIncomeMan, existingAnnualPayMan = 0,
    ratePct, years, rateType = 'variable',
    regulated = false, metro = false, houses = 0, firstHome = false } = opt;

  const ltvPct = ltvOf(regulated, houses, firstHome);
  const capLtv = priceMan * ltvPct;
  const d = maxLoanByDSR(annualIncomeMan, existingAnnualPayMan, ratePct, years, rateType);
  // 절대 상한: 수도권 또는 규제지역 매수 시 6억 (그 외 무제한 취급)
  const capAbs = (metro || regulated) ? LOAN_RULES.metroLoanCapMan : Infinity;

  const caps = { ltv: capLtv, dsr: d.maxLoan, cap: capAbs };
  let binding = 'LTV', maxLoan = capLtv;
  if (d.maxLoan < maxLoan) { maxLoan = d.maxLoan; binding = 'DSR'; }
  if (capAbs < maxLoan) { maxLoan = capAbs; binding = 'CAP'; }
  maxLoan = Math.max(0, Math.floor(maxLoan));
  return { maxLoan, binding, caps, ltvPct, stressedPct: d.stressedPct };
}

// ─── 취득세 (지방교육세 포함, 85㎡ 이하 농특세 면제 가정) ─────
// houses: 이번 매수로 보유하게 될 주택 수 기준 (1 = 1주택자 되는 매수)
// 반환: { ratePct, acq, edu, total }
function acqTaxOf(priceMan, housesAfter, regulated) {
  const A = LOAN_RULES.acq;
  let rate;
  // 다주택 중과 판정
  if (regulated && housesAfter >= 3) rate = A.surchargeHigh;        // 조정 3주택+ 12%
  else if (regulated && housesAfter === 2) rate = A.surchargeMid;   // 조정 2주택 8%
  else if (!regulated && housesAfter >= 4) rate = A.surchargeHigh;  // 비조정 4주택+ 12%
  else if (!regulated && housesAfter === 3) rate = A.surchargeMid;  // 비조정 3주택 8%
  else {
    // 일반 세율 (1~2주택 비조정 / 1주택 조정)
    if (priceMan <= A.lowLimitMan) rate = A.lowRate;
    else if (priceMan >= A.highLimitMan) rate = A.highRate;
    else rate = ((priceMan / 10000) * (2 / 3) - 3) / 100; // 6~9억 누진: (가격억×2/3−3)%
  }
  const acq = priceMan * rate;
  // 지방교육세: 일반세율 구간은 취득세율의 1/10 (0.1~0.3%), 중과 구간은 0.4% 고정 근사
  const edu = (rate >= A.surchargeMid) ? priceMan * 0.004 : acq * 0.1;
  return { ratePct: rate * 100, acq: Math.round(acq), edu: Math.round(edu), total: Math.round(acq + edu) };
}

// ─── Node 단위테스트용 export (브라우저에서는 무시됨) ─────────
if (typeof module !== 'undefined' && module.exports) {
  module.exports = { LOAN_RULES, buildSchedule, maxLoanByDSR, ltvOf, computeMaxLoan, acqTaxOf };
}
