// ===== Data model & helpers for 對帳報表 =====
// Row tuple index map (compact storage)
const RC = {
  id: 0, nick: 1, handle: 2, agent: 3, group: 4, gm: 5, violative: 6, rookie: 7,
  diamonds: 8, days: 9, hours: 10, bonus: 11,
  bRank: 12, bActive: 13, bOff: 14, bOff263: 15, bMatureRank: 16, bIncr: 17, bMatureMaint: 18, bTiers: 19,
};

const BONUS_PARTS = [
  { key: 'bRank', label: '躍升獎金', en: 'Ranked up', color: 'oklch(0.52 0.11 195)' },
  { key: 'bActive', label: '活躍度', en: 'Activeness incentive', color: 'oklch(0.68 0.12 170)' },
  { key: 'bMatureRank', label: '成熟創作者晉升率', en: 'Mature creator rank-up rate', color: 'oklch(0.72 0.13 85)' },
  { key: 'bIncr', label: '增量收入', en: 'Incremental revenue incentive', color: 'oklch(0.62 0.15 45)' },
  { key: 'bMatureMaint', label: '成熟主播維持', en: 'Mature creator maintenance rate', color: 'oklch(0.55 0.1 290)' },
  { key: 'bTiers', label: '維持等級', en: 'Maintained tiers', color: 'oklch(0.7 0.05 250)' },
];

function toObj(t) {
  const o = {};
  for (const k in RC) o[k] = t[RC[k]];
  o.gm = o.gm || '';
  o.group = o.group || '';
  return o;
}

const fmtInt = (n) => (n == null ? '—' : Math.round(n).toLocaleString('en-US'));
const fmtMoney = (n) => (n == null ? '—' : '$' + Number(n).toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 }));
const fmtTWD = (n) => (n == null || !isFinite(n) ? '—' : 'NT' + '$' + Math.round(n).toLocaleString('en-US'));
const fmtHr = (n) => (n == null ? '—' : Number(n).toLocaleString('en-US', { minimumFractionDigits: 1, maximumFractionDigits: 1 }));
const fmtPct = (n) => (isFinite(n) ? (n * 100).toFixed(1) + '%' : '—');
const monthLabel = (m) => `${m.slice(0, 4)} 年 ${parseInt(m.slice(4), 10)} 月`;

function sumBy(rows, key) { let s = 0; for (const r of rows) s += Number(r[key]) || 0; return s; }

// ----- Local storage: imported months + statement status -----
const LS_MONTHS = 'recon.importedMonths.v1';
const LS_STATUS = 'recon.status.v1';
const LS_VIEW = 'recon.view.v1';

function loadImported() {
  try { return JSON.parse(localStorage.getItem(LS_MONTHS) || '{}'); } catch (e) { return {}; }
}
function saveImported(obj) {
  try { localStorage.setItem(LS_MONTHS, JSON.stringify(obj)); } catch (e) { alert('瀏覽器儲存空間不足，無法保存匯入資料'); }
}
function loadJSON(k, d) { try { return JSON.parse(localStorage.getItem(k)) ?? d; } catch (e) { return d; } }
function saveJSON(k, v) { try { localStorage.setItem(k, JSON.stringify(v)); } catch (e) {} }

// ----- XLSX import (SheetJS) -----
const HEADER_MAP = {
  'Data Month': 'month', 'Creator ID': 'id', 'Creator nickname': 'nick', 'Handle': 'handle',
  'Creator Network manager': 'agent', 'Group': 'group', 'Group manager': 'gm',
  'Is violative creators': 'violative', 'The creator was Rookie at the time of first joining': 'rookie',
  'Diamonds': 'diamonds', 'Valid days(d)': 'days', 'LIVE duration(h)': 'hours', 'Estimated bonus': 'bonus',
  'Estimated bonus - Ranked up': 'bRank', 'Estimated bonus - Activeness incentive': 'bActive',
  'Estimated bonus - Off-platform creator task': 'bOff', 'Estimated bonus - Off-platform creator task (2026.3)': 'bOff263',
  'Estimated bonus - Mature creator rank-up rate': 'bMatureRank', 'Estimated bonus - Incremental revenue incentive': 'bIncr',
  'Estimated bonus - Mature creator maintenance rate': 'bMatureMaint', 'Estimated bonus - Maintained tiers': 'bTiers',
};

function parseWorkbook(arrayBuffer) {
  const wb = XLSX.read(arrayBuffer, { type: 'array' });
  const ws = wb.Sheets[wb.SheetNames[0]];
  const raw = XLSX.utils.sheet_to_json(ws, { defval: '', raw: true });
  if (!raw.length) throw new Error('檔案沒有資料列');
  const miss = ['Creator ID', 'Diamonds', 'Estimated bonus'].filter((h) => !(h in raw[0]));
  if (miss.length) throw new Error('缺少必要欄位：' + miss.join('、'));
  const byMonth = {};
  const num = (v) => { const n = parseFloat(v); return isNaN(n) ? null : n; };
  for (const r of raw) {
    const o = {};
    for (const h in HEADER_MAP) o[HEADER_MAP[h]] = r[h];
    const m = String(o.month || '').replace(/\D/g, '').slice(0, 6);
    if (!/^\d{6}$/.test(m)) continue;
    const t = [
      String(o.id), String(o.nick || ''), String(o.handle || ''), String(o.agent || ''), String(o.group || ''), String(o.gm || ''),
      o.violative === true || String(o.violative).toLowerCase() === 'true',
      String(o.rookie).toLowerCase() === 'yes',
      Math.round(num(o.diamonds) || 0), Math.round(num(o.days) || 0), Math.round((num(o.hours) || 0) * 100) / 100, num(o.bonus) || 0,
      num(o.bRank), num(o.bActive), num(o.bOff), num(o.bOff263), num(o.bMatureRank), num(o.bIncr), num(o.bMatureMaint), num(o.bTiers),
    ];
    (byMonth[m] = byMonth[m] || []).push(t);
  }
  if (!Object.keys(byMonth).length) throw new Error('找不到有效的 Data Month（格式應為 YYYYMM）');
  return byMonth;
}

// ----- CSV export -----
function downloadCSV(filename, header, rows) {
  const esc = (v) => { const s = v == null ? '' : String(v); return /[",\n]/.test(s) ? '"' + s.replace(/"/g, '""') + '"' : s; };
  const csv = '\uFEFF' + [header, ...rows].map((r) => r.map(esc).join(',')).join('\n');
  const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' });
  const a = document.createElement('a');
  a.href = URL.createObjectURL(blob); a.download = filename; a.click();
  setTimeout(() => URL.revokeObjectURL(a.href), 1000);
}

// Group rows by a dimension
const DIMENSIONS = [
  { key: 'agent', label: '經紀人' },
  { key: 'gm', label: '群組經理' },
  { key: 'group', label: '群組' },
];

function aggregate(rows, dim) {
  const map = new Map();
  for (const r of rows) {
    const k = r[dim] || '';
    if (!map.has(k)) map.set(k, { name: k, rows: [], count: 0, earning: 0, diamonds: 0, bonus: 0, hours: 0, violative: 0, payout: 0, payoutTWD: 0, guild: 0 });
    const g = map.get(k);
    g.rows.push(r); g.count++; if (r.diamonds > 0) g.earning++;
    g.diamonds += r.diamonds; g.bonus += r.bonus || 0; g.payout += r.payout || 0; g.payoutTWD += r.payoutTWD || 0; if (r.guild) g.guild++; g.hours += r.hours || 0; if (r.violative) g.violative++;
  }
  return [...map.values()];
}

// ----- 分潤規則 -----
const LS_RULES = 'recon.shareRules.v1';
const DEFAULT_RULES = { defaultRate: 75, guildRate: 50, groupRates: { '財幹行銷有限公司': 100 }, guildIds: {} };
function loadRules() { const r = loadJSON(LS_RULES, null); return r ? { ...DEFAULT_RULES, ...r, groupRates: { ...(r.groupRates || DEFAULT_RULES.groupRates) }, guildIds: { ...(r.guildIds || {}) } } : JSON.parse(JSON.stringify(DEFAULT_RULES)); }
function applyShare(o, rules) {
  if (rules.guildIds[o.id]) { o.guild = true; o.shareRate = rules.guildRate; o.shareType = '公會代運營'; }
  else if (rules.groupRates[o.group] != null) { o.guild = false; o.shareRate = rules.groupRates[o.group]; o.shareType = '群組指定'; }
  else { o.guild = false; o.shareRate = rules.defaultRate; o.shareType = '一般分潤'; }
  o.payout = Math.round((o.bonus || 0) * o.shareRate / 100 * 10000) / 10000;
  return o;
}

const STATUS_OPTS = [
  { v: 'pending', label: '待對帳' },
  { v: 'sent', label: '已寄送' },
  { v: 'confirmed', label: '已確認' },
  { v: 'disputed', label: '有異議' },
];

// ----- 結算方式 (Invoice type) -----
// 每個群組獨立設定, invoice = 開發票 (免服務費), labor = 勞報單 (5% 服務費)
// 綁「群組」而不是「對象」——因為結算單位是群組, 依經紀人/群組經理維度
// 呈現對帳單時, 每列主播依所屬群組各自套用.
const LS_INVOICE = 'recon.invoiceType.v1';
const LABOR_FEE_RATE = 0.05; // 勞報單服務費 5%
const INVOICE_TYPES = [
  { v: 'invoice', label: '發票',       short: '發票',       fee: 0 },
  { v: 'labor',   label: '勞報單 5%',  short: '勞報單',     fee: LABOR_FEE_RATE },
];
function loadInvoiceTypes() { return loadJSON(LS_INVOICE, {}); }
function saveInvoiceTypes(o) { saveJSON(LS_INVOICE, o); }
function getInvoiceType(map, groupName) {
  return (map && map[groupName]) || 'invoice'; // 預設: 發票 (免費)
}
function feeRateOf(type) {
  return type === 'labor' ? LABOR_FEE_RATE : 0;
}

Object.assign(window, {
  RC, BONUS_PARTS, toObj, fmtInt, fmtMoney, fmtHr, fmtPct, monthLabel, sumBy,
  loadImported, saveImported, loadJSON, saveJSON, LS_STATUS, LS_VIEW,
  parseWorkbook, downloadCSV, LS_RULES, DEFAULT_RULES, loadRules, applyShare, DIMENSIONS, aggregate, STATUS_OPTS,
  LS_INVOICE, LABOR_FEE_RATE, INVOICE_TYPES, loadInvoiceTypes, saveInvoiceTypes, getInvoiceType, feeRateOf,
});
