/* =========================================================================
   债务明细清单 · 数据层
   - 金额一律以「分」(整数) 存储与计算，杜绝浮点误差
   - localStorage 持久化
   - 自动重算：债务金额、状态与全局汇总
   ========================================================================= */
(function () {
  const STORE_KEY = "debt_ledger_v2";
  const AUTH_KEY = "debt_ledger_auth_v1";
  const AUTH_FAIL_KEY = "debt_ledger_auth_fail_v1";
  const SESSION_KEY = "debt_authed";
  const SESSION_TIMEOUT_MS = 30 * 60 * 1000;
  const LOGIN_LOCK_MS = 10 * 60 * 1000;
  const MAX_LOGIN_FAILS = 5;
  const TODAY = new Date();

  /* ---------- 工具：金额 ---------- */
  // 元(字符串/数字) -> 分(整数)
  function yuanToFen(v) {
    if (v === "" || v === null || v === undefined) return 0;
    const n = typeof v === "string" ? parseFloat(v.replace(/,/g, "")) : v;
    if (isNaN(n)) return 0;
    return Math.round(n * 100);
  }
  // 分 -> "12,345.67"
  function fenToYuanStr(fen) {
    const neg = fen < 0;
    const abs = Math.abs(fen);
    const yuan = Math.floor(abs / 100);
    const cents = abs % 100;
    const yuanStr = yuan.toLocaleString("en-US");
    return (neg ? "-" : "") + yuanStr + "." + String(cents).padStart(2, "0");
  }
  // 带符号货币：¥ 12,345.67
  function money(fen) {
    return "¥ " + fenToYuanStr(fen);
  }
  // 分 -> 纯数字元(用于输入框回填)
  function fenToYuanNum(fen) {
    return (fen / 100).toFixed(2);
  }

  /* ---------- 工具：日期 ---------- */
  function fmtDate(d) {
    if (!d) return "—";
    const dt = typeof d === "string" ? new Date(d) : d;
    if (isNaN(dt)) return "—";
    const y = dt.getFullYear();
    const m = String(dt.getMonth() + 1).padStart(2, "0");
    const day = String(dt.getDate()).padStart(2, "0");
    return `${y}-${m}-${day}`;
  }
  function fmtDateTime(d) {
    if (!d) return "—";
    const dt = typeof d === "string" ? new Date(d) : d;
    if (isNaN(dt)) return "—";
    const hh = String(dt.getHours()).padStart(2, "0");
    const mm = String(dt.getMinutes()).padStart(2, "0");
    return `${fmtDate(dt)} ${hh}:${mm}`;
  }
  function daysBetween(a, b) {
    const MS = 86400000;
    return Math.round((new Date(a) - new Date(b)) / MS);
  }
  function uid(prefix) {
    return (prefix || "id") + "_" + Math.random().toString(36).slice(2, 9);
  }

  /* ---------- 本地账号保护 ---------- */
  function simpleHash(text) {
    let h1 = 5381, h2 = 52711;
    for (let i = 0; i < text.length; i++) {
      const c = text.charCodeAt(i);
      h1 = ((h1 << 5) + h1) ^ c;
      h2 = ((h2 << 5) + h2) + c;
    }
    return (h1 >>> 0).toString(36) + (h2 >>> 0).toString(36);
  }
  function passwordHash(password, salt) {
    return simpleHash(`${salt}:${password}:debt-ledger`);
  }
  function getAuthConfig() {
    try {
      const raw = localStorage.getItem(AUTH_KEY);
      if (raw) return JSON.parse(raw);
    } catch (e) {}
    const cfg = { username: "admin", salt: "local-default", passwordHash: passwordHash("123456", "local-default"), updatedAt: TODAY.toISOString() };
    localStorage.setItem(AUTH_KEY, JSON.stringify(cfg));
    return cfg;
  }
  function getAuthFailState() {
    try {
      return JSON.parse(localStorage.getItem(AUTH_FAIL_KEY) || "{}");
    } catch (e) {
      return {};
    }
  }
  function saveAuthFailState(fail) {
    localStorage.setItem(AUTH_FAIL_KEY, JSON.stringify(fail || {}));
  }
  function getLockRemainingMs() {
    const fail = getAuthFailState();
    if (!fail.lockedUntil) return 0;
    return Math.max(0, fail.lockedUntil - Date.now());
  }
  function verifyLogin(username, password) {
    const lockMs = getLockRemainingMs();
    if (lockMs > 0) return { ok: false, locked: true, message: `登录失败次数过多，请 ${Math.ceil(lockMs / 60000)} 分钟后再试` };
    const cfg = getAuthConfig();
    const ok = username.trim() === cfg.username && passwordHash(password, cfg.salt) === cfg.passwordHash;
    if (ok) {
      saveAuthFailState({});
      return { ok: true };
    }
    const fail = getAuthFailState();
    const count = (fail.count || 0) + 1;
    const next = { count, lastFailedAt: Date.now() };
    if (count >= MAX_LOGIN_FAILS) next.lockedUntil = Date.now() + LOGIN_LOCK_MS;
    saveAuthFailState(next);
    return { ok: false, locked: !!next.lockedUntil, message: next.lockedUntil ? "登录失败次数过多，已锁定 10 分钟" : `账号或密码错误，还可尝试 ${MAX_LOGIN_FAILS - count} 次` };
  }
  function changePassword(oldPassword, newPassword) {
    const cfg = getAuthConfig();
    if (passwordHash(oldPassword, cfg.salt) !== cfg.passwordHash) return { ok: false, message: "当前密码不正确" };
    if (!newPassword || newPassword.length < 6) return { ok: false, message: "新密码至少 6 位" };
    const salt = uid("salt");
    const next = { ...cfg, salt, passwordHash: passwordHash(newPassword, salt), updatedAt: new Date().toISOString() };
    localStorage.setItem(AUTH_KEY, JSON.stringify(next));
    return { ok: true };
  }
  function setAuthSession() {
    sessionStorage.setItem(SESSION_KEY, JSON.stringify({ authed: true, lastActiveAt: Date.now() }));
  }
  function readAuthSession() {
    try {
      return JSON.parse(sessionStorage.getItem(SESSION_KEY) || "{}");
    } catch (e) {
      return {};
    }
  }
  function isAuthSessionValid() {
    const s = readAuthSession();
    return !!s.authed && !!s.lastActiveAt && Date.now() - s.lastActiveAt <= SESSION_TIMEOUT_MS;
  }
  function touchAuthSession() {
    if (isAuthSessionValid()) setAuthSession();
  }
  function clearAuthSession() {
    sessionStorage.removeItem(SESSION_KEY);
  }

  /* ---------- 初始数据：但静姝历史还款明细 ---------- */
  function seed() {
    const debtors = [
      { id: "d_danjingshu", name: "但静姝", gender: "女", birthYear: "1984", phone: "", wechat: "", idcard: "", address: "", relation: "其他", note: "1984年出生；原始欠款总额待补充", status: "active", createdAt: "2026-06-04T18:30:00" },
    ];

    const debts = [
      { id: "b_danjingshu_history", debtorId: "d_danjingshu", title: "历史欠款（原始金额待补充）", principalFen: 2120000, amountPending: true, occurDate: "2025-10-09", dueDate: "", type: "借款", interest: false, interestNote: "", source: "根据已提供的还款凭证建立", note: "当前原始金额暂按累计已还 ¥21,200.00 建立，仅用于录入历史还款。请确认原始欠款总额后编辑本债务金额。", status: "", archived: false, createdAt: "2026-06-04T18:30:00", attachments: [
        { name: "微信图片_20260604174457_513_164.jpg", size: "凭证", kind: "image" },
        { name: "微信图片_20260604174458_514_164.jpg", size: "凭证", kind: "image" },
        { name: "微信图片_20260604174503_515_164.jpg", size: "凭证", kind: "image" },
        { name: "微信图片_20260604174525_516_164.jpg", size: "凭证", kind: "image" },
        { name: "微信图片_20260604174527_517_164.jpg", size: "凭证", kind: "image" },
        { name: "微信图片_20260604174530_518_164.jpg", size: "凭证", kind: "image" },
        { name: "微信图片_20260604174532_519_164.jpg", size: "凭证", kind: "image" },
        { name: "微信图片_20260604174533_520_164.jpg", size: "凭证", kind: "image" },
        { name: "微信图片_20260604174535_521_164.jpg", size: "凭证", kind: "image" },
      ] },
    ];

    const payments = [
      { id: "p_dj_20251009", debtId: "b_danjingshu_history", debtorId: "d_danjingshu", amountFen: 500000, datetime: "2025-10-09T07:34:26", transferDatetime: "2025-10-09T07:34:26", channel: "微信/零钱", txnNo: "SOBS2025100900004098172133", account: "零钱（中信银行账户尾号3177）", payer: "中国农业银行 622848 **** **** * 2172", note: "十月九号还款五千", hasAttach: true, attachmentName: "微信图片_20260604174530_518_164.jpg", status: "valid", createdAt: "2026-06-04T18:30:00", updatedAt: "2026-06-04T18:30:00" },
      { id: "p_dj_20251109", debtId: "b_danjingshu_history", debtorId: "d_danjingshu", amountFen: 200000, datetime: "2025-11-09T09:35:03", transferDatetime: "2025-11-09T09:35:03", channel: "微信/零钱", txnNo: "SOBS2025110900004148466258", account: "零钱（中信银行账户尾号3177）", payer: "中国农业银行 622848 **** **** * 2172", note: "先支付你两千", hasAttach: true, attachmentName: "微信图片_20260604174527_517_164.jpg", status: "valid", createdAt: "2026-06-04T18:30:00", updatedAt: "2026-06-04T18:30:00" },
      { id: "p_dj_20260311", debtId: "b_danjingshu_history", debtorId: "d_danjingshu", amountFen: 600000, datetime: "2026-03-11T15:49:58", transferDatetime: "2026-03-11T15:46:39", channel: "微信/零钱通", txnNo: "", account: "零钱通", payer: "", note: "截图显示已转入零钱通", hasAttach: true, attachmentName: "微信图片_20260604174525_516_164.jpg", status: "valid", createdAt: "2026-06-04T18:30:00", updatedAt: "2026-06-04T18:30:00" },
      { id: "p_dj_20260506", debtId: "b_danjingshu_history", debtorId: "d_danjingshu", amountFen: 50000, datetime: "2026-05-06T18:44:43", transferDatetime: "2026-05-06T11:19:48", channel: "微信/零钱", txnNo: "", account: "零钱", payer: "", note: "收款时间晚于转账时间", hasAttach: true, attachmentName: "微信图片_20260604174503_515_164.jpg", status: "valid", createdAt: "2026-06-04T18:30:00", updatedAt: "2026-06-04T18:30:00" },
      { id: "p_dj_20260511", debtId: "b_danjingshu_history", debtorId: "d_danjingshu", amountFen: 200000, datetime: "2026-05-11T15:24:38", transferDatetime: "2026-05-11T15:24:16", channel: "微信/零钱", txnNo: "", account: "零钱", payer: "", note: "", hasAttach: true, attachmentName: "微信图片_20260604174458_514_164.jpg", status: "valid", createdAt: "2026-06-04T18:30:00", updatedAt: "2026-06-04T18:30:00" },
      { id: "p_dj_20260512", debtId: "b_danjingshu_history", debtorId: "d_danjingshu", amountFen: 300000, datetime: "2026-05-12T12:28:53", transferDatetime: "2026-05-12T12:13:31", channel: "微信/零钱", txnNo: "", account: "零钱", payer: "", note: "", hasAttach: true, attachmentName: "微信图片_20260604174457_513_164.jpg", status: "valid", createdAt: "2026-06-04T18:30:00", updatedAt: "2026-06-04T18:30:00" },
      { id: "p_dj_20260525", debtId: "b_danjingshu_history", debtorId: "d_danjingshu", amountFen: 140000, datetime: "2026-05-25T12:01:59", transferDatetime: "2026-05-25T12:01:59", channel: "银行转账", txnNo: "", account: "银行卡 622180 ****** 0117", payer: "东莞银行股份有限公司 6214 ****** 8365", note: "他行汇入；无附言", hasAttach: true, attachmentName: "微信图片_20260604174535_521_164.jpg", status: "valid", createdAt: "2026-06-04T18:30:00", updatedAt: "2026-06-04T18:30:00" },
      { id: "p_dj_20260601", debtId: "b_danjingshu_history", debtorId: "d_danjingshu", amountFen: 30000, datetime: "2026-06-01T22:07:11", transferDatetime: "2026-06-01T22:07:11", channel: "银行转账", txnNo: "", account: "银行卡 622180 ****** 0117", payer: "东莞银行股份有限公司 6214 ****** 8365", note: "他行汇入；无附言", hasAttach: true, attachmentName: "微信图片_20260604174533_520_164.jpg", status: "valid", createdAt: "2026-06-04T18:30:00", updatedAt: "2026-06-04T18:30:00" },
      { id: "p_dj_20260603", debtId: "b_danjingshu_history", debtorId: "d_danjingshu", amountFen: 100000, datetime: "2026-06-03T18:39:12", transferDatetime: "2026-06-03T18:39:12", channel: "银行转账", txnNo: "", account: "银行卡 622180 ****** 0117", payer: "东莞银行股份有限公司 6214 ****** 8365", note: "他行汇入；无附言", hasAttach: true, attachmentName: "微信图片_20260604174532_519_164.jpg", status: "valid", createdAt: "2026-06-04T18:30:00", updatedAt: "2026-06-04T18:30:00" },
    ];

    const adjustments = [];

    const auditLog = [
      { id: "l_import_danjingshu", time: "2026-06-04T18:30:00", type: "导入还款", target: "但静姝 · 历史欠款", summary: "根据 9 张还款凭证导入 9 笔有效还款，累计 ¥ 21,200.00" },
    ];

    return {
      debtors,
      debts,
      payments,
      adjustments,
      auditLog,
      channels: ["微信/零钱", "微信/零钱通", "银行转账", "现金", "其他"],
      relations: ["朋友", "亲属", "客户", "同事", "其他"],
      debtTypes: ["借款", "代付款", "货款", "其他"],
      settings: { currency: "CNY", dateFormat: "YYYY-MM-DD", remindDays: 7 },
      meta: { updatedAt: TODAY.toISOString() },
    };
  }

  /* ---------- 持久化 ---------- */
  function load() {
    try {
      const raw = localStorage.getItem(STORE_KEY);
      if (raw) return JSON.parse(raw);
    } catch (e) {}
    const s = seed();
    save(s);
    return s;
  }
  function save(state) {
    try {
      state.meta = state.meta || {};
      state.meta.updatedAt = TODAY.toISOString();
      localStorage.setItem(STORE_KEY, JSON.stringify(state));
    } catch (e) {}
  }
  function reset() {
    localStorage.removeItem(STORE_KEY);
    return load();
  }

  /* ---------- 计算引擎 ---------- */
  function debtCalc(state, debtId) {
    const debt = state.debts.find((d) => d.id === debtId);
    if (!debt) return null;
    const pays = state.payments.filter((p) => p.debtId === debtId);
    const adjs = state.adjustments.filter((a) => a.debtId === debtId);
    const increase = adjs.filter((a) => a.status === "valid" && a.type === "increase").reduce((s, a) => s + a.amountFen, 0);
    const reduce = adjs.filter((a) => a.status === "valid" && a.type === "reduce").reduce((s, a) => s + a.amountFen, 0);
    const currentDue = debt.principalFen + increase - reduce;
    const paid = pays.filter((p) => p.status === "valid").reduce((s, p) => s + p.amountFen, 0);
    const remaining = currentDue - paid;

    let status;
    if (debt.archived) status = "已归档";
    else if (debt.amountPending) status = "金额待补充";
    else if (remaining < 0) status = "存在多还款";
    else if (remaining === 0) status = "已结清";
    else {
      const overdue = debt.dueDate && daysBetween(TODAY, debt.dueDate) > 0;
      if (overdue) status = "已逾期";
      else if (paid > 0) status = "还款中";
      else status = "未开始还款";
    }
    const dueIn = debt.dueDate ? daysBetween(debt.dueDate, TODAY) : null; // >0 未来天数, <0 已过
    const progress = currentDue > 0 ? Math.min(1, Math.max(0, paid / currentDue)) : (paid > 0 ? 1 : 0);
    return { debt, principal: debt.principalFen, increase, reduce, currentDue, paid, remaining, status, dueIn, progress, payCount: pays.filter((p) => p.status === "valid").length, amountPending: !!debt.amountPending };
  }

  function debtorCalc(state, debtorId) {
    const debts = state.debts.filter((d) => d.debtorId === debtorId);
    let currentDue = 0, paid = 0, remaining = 0, overdue = 0, settled = 0, unsettled = 0, pending = 0, lastPay = null;
    debts.forEach((d) => {
      const c = debtCalc(state, d.id);
      currentDue += c.currentDue;
      paid += c.paid;
      remaining += c.remaining;
      if (c.status === "已逾期") overdue++;
      if (c.amountPending) pending++;
      if (c.status === "已结清") settled++;
      if (c.remaining > 0) unsettled++;
    });
    state.payments.filter((p) => p.debtorId === debtorId && p.status === "valid").forEach((p) => {
      if (!lastPay || new Date(p.datetime) > new Date(lastPay)) lastPay = p.datetime;
    });
    let status;
    if (pending > 0) status = "金额待补充";
    else if (remaining > 0) status = overdue > 0 ? "有逾期" : (paid > 0 ? "还款中" : "待还款");
    else if (remaining === 0) status = "已结清";
    else status = "存在多还款";
    return { debtCount: debts.length, currentDue, paid, remaining, overdue, settled, unsettled, pending, lastPay, status };
  }

  function globalSummary(state) {
    let totalDue = 0, totalPaid = 0, totalRemaining = 0;
    let unsettledDebts = 0, settledDebts = 0, overdueDebts = 0, overpayDebts = 0, pendingDebts = 0;
    const activeDebts = state.debts.filter((d) => !d.archived);
    activeDebts.forEach((d) => {
      const c = debtCalc(state, d.id);
      totalDue += c.currentDue;
      totalPaid += c.paid;
      totalRemaining += c.remaining;
      if (c.amountPending) pendingDebts++;
      if (c.status === "已结清") settledDebts++;
      else if (c.status === "已逾期") { overdueDebts++; unsettledDebts++; }
      else if (c.status === "存在多还款") overpayDebts++;
      else unsettledDebts++;
    });
    // 欠款人数：仍有剩余待还的欠款人
    const debtorIds = new Set(state.debtors.map((d) => d.id));
    let activeDebtors = 0;
    debtorIds.forEach((id) => {
      const dc = debtorCalc(state, id);
      if (dc.remaining > 0 || dc.pending > 0) activeDebtors++;
    });
    // 本月已还 / 近30天已还
    const monthStart = new Date(TODAY.getFullYear(), TODAY.getMonth(), 1);
    let monthPaid = 0, last30Paid = 0;
    state.payments.filter((p) => p.status === "valid").forEach((p) => {
      const dt = new Date(p.datetime);
      if (dt >= monthStart) monthPaid += p.amountFen;
      if (daysBetween(TODAY, dt) <= 30 && daysBetween(TODAY, dt) >= 0) last30Paid += p.amountFen;
    });
    return { totalDue, totalPaid, totalRemaining, unsettledDebts, settledDebts, overdueDebts, overpayDebts, pendingDebts, activeDebtors, debtorCount: state.debtors.length, monthPaid, last30Paid };
  }

  // 月度回款趋势（近 6 个月）
  function monthlyTrend(state) {
    const arr = [];
    for (let i = 5; i >= 0; i--) {
      const d = new Date(TODAY.getFullYear(), TODAY.getMonth() - i, 1);
      const next = new Date(TODAY.getFullYear(), TODAY.getMonth() - i + 1, 1);
      let sum = 0;
      state.payments.filter((p) => p.status === "valid").forEach((p) => {
        const pd = new Date(p.datetime);
        if (pd >= d && pd < next) sum += p.amountFen;
      });
      arr.push({ label: `${d.getMonth() + 1}月`, year: d.getFullYear(), fen: sum });
    }
    return arr;
  }

  // 还款途径占比
  function channelBreakdown(state) {
    const map = {};
    state.payments.filter((p) => p.status === "valid").forEach((p) => {
      map[p.channel] = (map[p.channel] || 0) + p.amountFen;
    });
    const total = Object.values(map).reduce((s, v) => s + v, 0);
    return Object.entries(map)
      .map(([channel, fen]) => ({ channel, fen, pct: total > 0 ? fen / total : 0 }))
      .sort((a, b) => b.fen - a.fen);
  }

  // 待还排行
  function topDebtors(state, n) {
    return state.debtors
      .map((d) => ({ debtor: d, calc: debtorCalc(state, d.id) }))
      .filter((x) => x.calc.remaining > 0 || x.calc.pending > 0)
      .sort((a, b) => b.calc.remaining - a.calc.remaining)
      .slice(0, n || 5);
  }

  /* ---------- 状态颜色映射 ---------- */
  const STATUS_TONE = {
    "未开始还款": "neutral",
    "待还款": "neutral",
    "还款中": "blue",
    "有逾期": "red",
    "已逾期": "red",
    "已结清": "green",
    "存在多还款": "amber",
    "金额待补充": "amber",
    "已归档": "neutral",
    "已作废": "neutral",
    "有效": "green",
  };

  /* ---------- 导出全局 ---------- */
  window.Ledger = {
    STORE_KEY, TODAY,
    AUTH_KEY, AUTH_FAIL_KEY, SESSION_KEY, SESSION_TIMEOUT_MS,
    yuanToFen, fenToYuanStr, fenToYuanNum, money,
    fmtDate, fmtDateTime, daysBetween, uid,
    getAuthConfig, verifyLogin, changePassword, setAuthSession, isAuthSessionValid, touchAuthSession, clearAuthSession, getLockRemainingMs,
    load, save, reset, seed,
    debtCalc, debtorCalc, globalSummary, monthlyTrend, channelBreakdown, topDebtors,
    STATUS_TONE,
  };
})();
