// views.jsx — 次要頁面與 Modal

function PageTop({ title, sub, right }) {
  return <div className="topbar">
    <div><div className="serif" style={{ fontSize: 17, fontWeight: 700 }}>{title}</div>
    <div className="faint" style={{ fontSize: 11 }}>{sub}</div></div>
    {right && <div style={{ marginLeft: 'auto' }}>{right}</div>}
  </div>;
}

/* 法規參考庫 */
function LawLibrary() {
  const groups = [
  { kind: 'food', name: '食品・保健類', desc: '套用於保健食品、飲料等案件' },
  { kind: 'cosmetic', name: '化粧品・保養類', desc: '套用於保養品、彩妝等案件' },
  { kind: 'general', name: '一般商品', desc: '文具、服飾、家居、玩具等' }];

  return (
    <div className="viewcol">
      <PageTop title="法規參考庫" sub="風控評估自動依產品類別套用以下條文" />
      <div className="scrollpad">
        {groups.map((g) =>
        <div key={g.kind} style={{ marginBottom: 20 }}>
            <div style={{ display: 'flex', alignItems: 'baseline', gap: 9, marginBottom: 10 }}>
              <h3 className="serif" style={{ margin: 0, fontSize: 15, fontWeight: 700 }}>{g.name}</h3>
              <span className="faint" style={{ fontSize: 11 }}>{g.desc}</span>
            </div>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
              <div className="card" style={{ padding: '14px 16px' }}>
                <div className="boxlbl">適用法規</div>
                <div style={{ display: 'flex', flexDirection: 'column', gap: 9 }}>
                  {window.LAW_LIB[g.kind].map((l, i) =>
                <div key={i}><div className="mono lawcode" style={{ display: 'inline-block', marginBottom: 3 }}>{l.code}</div>
                    <div className="muted" style={{ fontSize: 12, lineHeight: 1.5 }}>{l.t}</div></div>
                )}
                </div>
              </div>
              <div className="card" style={{ padding: '14px 16px' }}>
                <div className="boxlbl">禁用／敏感字眼</div>
                <div style={{ display: 'flex', gap: 7, flexWrap: 'wrap' }}>
                  {window.AVOID_WORDS[g.kind].map((w, i) => <span key={i} className="pill risk-high" style={{ height: 24, textDecoration: 'line-through' }}>{w}</span>)}
                </div>
              </div>
            </div>
          </div>
        )}
      </div>
    </div>);

}

/* 簡易資料表頁 */
function TableView({ title, sub, cols, rows, rowIds, onRowClick }) {
  return (
    <div className="viewcol">
      <PageTop title={title} sub={sub} />
      <div className="scrollpad">
        <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
          <div className="trow thead">{cols.map((c, i) => <div key={i} style={{ flex: c.f || 1, textAlign: c.a || 'left' }}>{c.h}</div>)}</div>
          {rows.length === 0 && <div className="trow"><div className="faint">尚無資料</div></div>}
          {rows.map((r, i) =>
          <div key={i} className={'trow' + (onRowClick ? ' trk' : '')} onClick={onRowClick ? () => onRowClick(rowIds[i]) : undefined}>{r.map((cell, j) => <div key={j} style={{ flex: cols[j].f || 1, textAlign: cols[j].a || 'left' }}>{cell}</div>)}</div>
          )}
        </div>
      </div>
    </div>);

}

function DocsView({ cases, onOpen }) {
  const live = cases.filter((c) => c.progress !== '婉拒' && c.progress !== '未成交' && c.willing !== '婉拒');
  const rows = live.map((c) => [
  <span className="mono">{c.id}</span>, c.vendor,
  <span className={'pill ' + window.riskTone(c.risk)} style={{ height: 19, fontSize: 10 }}><span className="dot"></span>{c.riskLabel}</span>,
  c.intent.nature.length ?
  <span className="pill risk-low" style={{ height: 19, fontSize: 10 }}><span className="dot"></span>已填</span> :
  <span className="pill needfill" style={{ height: 19, fontSize: 10 }}><span className="dot"></span>待填</span>,
  c.quote.items.length ? <span className="mono">{window.fmt(c.quote.items.reduce((s, k) => s + ((window.PRICE_LIST.find((p) => p.key === k) || {}).price || 0), 0))}</span> : '—',
  window.stageName(c.stage)]
  );
  return <TableView title="意願・報價文件" sub="點選案件可進入「意願文件」分頁·各案件文件與報價狀態"
  rowIds={live.map((c) => c.id)} onRowClick={(id) => onOpen(id, 'intent')}
  cols={[{ h: '代號', f: .7 }, { h: '廠商', f: 1.3 }, { h: '風控' }, { h: '意願文件' }, { h: '報價', a: 'right' }, { h: '階段' }]} rows={rows} />;
}

function ContractView({ cases, onOpen }) {
  const [filter, setFilter] = React.useState('all');
  const all = cases.filter((c) => ['contract', 'schedule', 'produce', 'finance'].includes(c.stage));
  const sorted = [
    ...all.filter(c=> !(c.io && c.io.signed)),
    ...all.filter(c=> c.io && c.io.signed),
  ];
  const list = filter==='signed' ? sorted.filter(c=>c.io&&c.io.signed)
    : filter==='unsigned' ? sorted.filter(c=>!(c.io&&c.io.signed))
    : sorted;
  const filterCtrl = (
    <div className="segctrl">
      {[['all','全部'],['unsigned','未簽署'],['signed','已簽署']].map(([v,l])=>(
        <button key={v} className={'segbtn'+(filter===v?' on':'')} onClick={()=>setFilter(v)}>{l}</button>
      ))}
    </div>
  );
  const rows = list.map((c) => [
  <span className="mono">{c.id}</span>, c.vendor,
  <span className={'pill ' + (c.io && c.io.signed ? 'risk-low' : 'risk-mid')} style={{ height: 19, fontSize: 10 }}><span className="dot"></span>{c.io && c.io.signed ? '已簽署' : '待簽署'}</span>,
  c.schedule.shoot || '—', c.schedule.online || '—',
  <span className="mono">{window.fmt(c.value)}</span>, window.stageName(c.stage)]
  );
  return (
    <div className="viewcol">
      <div className="topbar">
        <div><div className="serif" style={{fontSize:17,fontWeight:700}}>合約・委刊單</div><div className="faint" style={{fontSize:11}}>點選案件可進入「委刊單」分頁</div></div>
        <div style={{marginLeft:'auto'}}>{filterCtrl}</div>
      </div>
      <div className="scrollpad">
        <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
          <div className="trow thead">
            {[{h:'廠商',f:1.6},{h:'委刊單',f:.8},{h:'上線',f:.9},{h:'金額',f:.9}].map((c,i)=><div key={i} style={{flex:c.f||1}}>{c.h}</div>)}
          </div>
          {list.length===0 && <div className="trow"><div className="faint">尚無符合條件的案件</div></div>}
          {list.map((c,i)=>(
            <div key={i} className="trow trk" onClick={()=>onOpen(list[i].id,'io')}>
              <div style={{flex:1.6,minWidth:0}}><div className="ell" style={{fontWeight:600}}>{c.vendor}</div><div className="faint ell" style={{fontSize:10.5}}>{c.product}</div></div>
              <div style={{flex:.8}}><span className={'pill '+(c.io&&c.io.signed?'risk-low':'risk-mid')} style={{height:19,fontSize:10}}><span className="dot"></span>{c.io&&c.io.signed?'已簽署':'待簽署'}</span></div>
              <div style={{flex:.9,fontSize:12}} className="faint">{c.schedule.online||'—'}</div>
              <div style={{flex:.9,fontSize:12}} className="mono">{window.fmt(c.value)}</div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

function CalView({ cases, toast }) {
  const live = cases.filter((c) => c.progress !== '婉拒' && c.progress !== '未成交');
  const evts = [];
  live.forEach((c) => {
    window.buildProdEvents(c).forEach((e) => evts.push({ ...e, c }));
  });
  const sortKey = (e) => e.kind === 'range' ? e.start : e.date;
  evts.sort((a, b) => sortKey(a).localeCompare(sortKey(b)));
  const fmtD = (ymd) => ymd ? `${ymd.slice(0, 4)}/${ymd.slice(4, 6)}/${ymd.slice(6, 8)}` : '';
  const dl = () => {
    if (!evts.length) {toast('尚無可匯出的排程');return;}
    const ics = window.buildICS('果米排程', evts);
    window.downloadICS('guomi-schedule.ics', ics);
    const reds = evts.filter((e) => e.color === 'red').length;
    toast(`已下載全部排程 .ics（國美工作日 ${reds} · 區間 ${evts.length - reds}）`);
  };
  return (
    <div className="viewcol">
      <PageTop title="行事曆・排程" sub={`${evts.length} 個排程事件· 匯入行事曆後依色系區分重要性`} right={<button className="btn primary" onClick={dl}>◵ 下載全部 .ics</button>} />
      <div className="scrollpad">
        <div className="callegend" style={{ marginBottom: 12 }}>
          <span className="lgd"><span className="lgddot red"></span>國美工作日（紅）· 草圖・腳本・拍攝・成品・上線</span>
          <span className="lgd"><span className="lgddot yellow"></span>區間排程（淺黃）· 修改・後製期</span>
        </div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 9 }}>
          {evts.length === 0 && <div className="card faint" style={{ padding: '18px', textAlign: 'center' }}>尚無排程。於案件詳情「製作排程」分頁填寫里程碑即會出現於此。</div>}
          {evts.map((e, i) =>
          <div key={i} className="card calrow" style={{ borderLeft: `3px solid ${e.color === 'red' ? 'var(--rose)' : 'var(--gold-2)'}` }}>
              <span className="mono" style={{ fontSize: 12.5, fontWeight: 700, width: 200, flex: 'none', whiteSpace: 'nowrap' }}>{e.kind === 'range' ? `${fmtD(e.start)} ~ ${fmtD(e.end)}` : fmtD(e.date)}</span>
              <span style={{ fontSize: 15, width: 22, flex: 'none', textAlign: 'center' }}>{e.icon}</span>
              <span className="caltag" style={{ width: 86, flex: 'none', justifyContent: 'center', background: e.color === 'red' ? 'var(--rose-soft)' : 'var(--gold-soft)', color: e.color === 'red' ? 'var(--rose-ink)' : 'oklch(0.5 0.09 70)' }}>{e.label}</span>
              <span style={{ fontSize: 13 }}>{e.c.vendor} · {e.c.product}</span>
              <span className="mono faint" style={{ fontSize: 11, marginLeft: 'auto' }}>{e.c.id}</span>
            </div>
          )}
        </div>
      </div>
    </div>);

}

/* 案件追蹤（依真實追蹤表欄位）*/
function TrackingView({ cases, onOpen, onSetProgress, onSetNote }) {
  const OPTS = [...window.PROGRESS_PIPELINE, '未成交', '婉拒'];
  const [filter, setFilter] = React.useState('all');
  const isDead = (c)=> c.progress==='婉拒' || c.progress==='未成交' || c.willing==='婉拒';
  const rank = (c)=> isDead(c) ? 2 : c.progress==='結案' ? 1 : 0;
  const filtered = filter==='all' ? cases
    : filter==='live' ? cases.filter(c=>!isDead(c))
    : filter==='dead' ? cases.filter(isDead)
    : cases.filter(c=> c.progress===filter);
  const ordered = [...filtered].sort((a,b)=> rank(a) - rank(b));
  const filterCtrl = (
    <select className="selinp" value={filter} onChange={e=>setFilter(e.target.value)}>
      <option value="all">全部進度</option>
      <option value="live">進行中（排除婉拒）</option>
      <option value="dead">未成交／婉拒</option>
      <optgroup label="依進度篩選">
        {OPTS.map(o=><option key={o} value={o}>{o}</option>)}
      </optgroup>
    </select>
  );
  return (
    <div className="viewcol">
      <PageTop title="案件追蹤" sub={`${filtered.length} / ${cases.length} 件 · 進度可手動調整，亦隨工作流自動推進`} right={filterCtrl} />
      <div className="scrollpad">
        <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
          <div className="trow thead">
            <div style={{ width: 56 }}>收件日</div>
            <div style={{ width: 54 }}>來源</div>
            <div style={{ width: 78 }}>類型</div>
            <div style={{ flex: 1.4 }}>廠商・案名</div>
            <div style={{ width: 120 }}>進度</div>
            <div style={{ width: 96 }}>財務</div>
            <div style={{ width: 58 }}>窗口</div>
            <div style={{ flex: 1.2 }}>備註</div>
          </div>
          {ordered.length===0 && <div className="trow"><div className="faint">此篩選條件下尚無案件</div></div>}
          {ordered.map((c) => {
            const mt = window.progressTone(c.progress);
            const pay = c.pay || { dep: { paid: false }, bal: { paid: false } };
            return (
              <div key={c.id} className="trow trk" onClick={() => onOpen(c.id)}>
              <div style={{ width: 56 }} className="mono faint">{c.received.slice(5)}</div>
              <div style={{ width: 54 }}><span className={'sourcebadge '+(c.source==='開發信'?'dev':'in')}>{c.source==='開發信'?'↗開發':'↙自來'}</span></div>
              <div style={{ width: 78 }}><span className="tag" style={{ height: 19, fontSize: 10, whiteSpace: 'nowrap' }}>{c.caseType}</span></div>
              <div style={{ flex: 1.4, minWidth: 0 }}>
                <div style={{ fontWeight: 600 }} className="ell">{c.vendor}</div>
                <div className="faint ell" style={{ fontSize: 10.5 }}>{c.product}</div>
              </div>
              <div style={{ width: 120 }} onClick={(e) => e.stopPropagation()}>
                <div className={'progresssel tone-' + mt}>
                  <span className="pdot"></span>
                  <select value={c.progress} onChange={(e) => onSetProgress(c.id, e.target.value)}>
                    {OPTS.map((o) => <option key={o} value={o}>{o}</option>)}
                  </select>
                  <span className="caret">▾</span>
                </div>
              </div>
              <div style={{ width: 104 }} title={window.paySummary(c).label}>
                <div className="paycell">
                  {window.payPlan(c).plan.map((p) =>
                    <span key={p.key} className={'paychip' + (pay[p.key] && pay[p.key].paid ? ' on' : '')}>{p.label[0]}</span>
                    )}
                </div>
              </div>
              <div style={{ width: 58, fontSize: 11 }} className="ell faint">{c.contact}</div>
              <div style={{ flex: 1.2 }} onClick={(e)=>e.stopPropagation()}>
                <input className="noteinp" value={c.note||''} placeholder="— 填寫備註" onChange={(e)=>onSetNote(c.id, e.target.value)} />
              </div>
            </div>);
          })}
        </div>
      </div>
    </div>);

}

function PayView({ cases, onOpen, onSetPay, onSetProgress, onOpenInvoice, toast }) {
  const today = () => {const d = new Date();return `${d.getFullYear()}/${String(d.getMonth() + 1).padStart(2, '0')}/${String(d.getDate()).padStart(2, '0')}`;};
  const list = cases.filter((c) => {
    const dead = c.progress === '婉拒' || c.progress === '未成交';
    if (dead) return false;
    const hasPay = c.pay && (c.pay.dep.paid || c.pay.bal.paid);
    return ['contract', 'schedule', 'produce', 'finance'].includes(c.stage) || c.io && c.io.signed || hasPay;
  });
  const totalDue = list.reduce((s, c) => s + window.ioTotals(c).total, 0);
  const received = list.reduce((s, c) => {const { plan } = window.payPlan(c);const pay = c.pay || { dep: {}, bal: {} };return s + plan.reduce((a, p) => a + (pay[p.key] && pay[p.key].paid ? p.amount : 0), 0);}, 0);
  const toggle = (c, plan, p) => {
    const cur = c.pay[p.key];
    onSetPay(c.id, p.key, { paid: !cur.paid, date: !cur.paid ? today() : '' });
    // 若此次收款後、計畫內所有款項皆已入帳 → 進度設為結案
    const willAllPaid = !cur.paid && plan.every((x) => x.key === p.key || c.pay[x.key] && c.pay[x.key].paid);
    const invoiced = (c.invoices || {})[p.key];
    if (willAllPaid) {onSetProgress(c.id, '結案');toast(invoiced ? '已結清 · 進度設為「結案」' : '已結清 · 該款項尚未開立發票');} else
    if (!cur.paid && !invoiced) {toast(p.label + '已入帳 · 尚未開立發票，請點「開發票」');} else
    toast(cur.paid ? '已取消收款註記' : p.label + '已標記入帳');
  };
  return (
    <div className="viewcol">
      <PageTop title="收款紀錄" sub="收款比例依「委刊單」約定· 點選狀態燈號即可標記入帳"
      right={<div style={{ textAlign: 'right' }}><div className="mono" style={{ fontSize: 13, fontWeight: 700 }}>{window.fmt(received)} / {window.fmt(totalDue)}</div><div className="faint" style={{ fontSize: 10 }}>已收 / 應收總額（含稅）</div></div>} />
      <div className="scrollpad">
        <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
          <div className="trow thead">
            <div style={{ width: 78 }}>代號</div>
            <div style={{ flex: 1.2 }}>廠商</div>
            <div style={{ flex: 1.7 }}>收款與發票（依委刊單約定）</div>
            <div style={{ width: 96 }}>財務狀態</div>
          </div>
          {list.length === 0 && <div className="trow"><div className="faint">尚無已簽約之收款案件</div></div>}
          {list.map((c) => {
            const { plan, total } = window.payPlan(c);
            const pay = c.pay || { dep: { paid: false, date: '' }, bal: { paid: false, date: '' } };
            const sm = window.paySummary(c);
            const invs = c.invoices || {};
            return (
              <div key={c.id} className="trow trk" onClick={() => onOpen(c.id, 'io')}>
              <div style={{ width: 78 }} className="mono faint">{c.id}</div>
              <div style={{ flex: 1.2, minWidth: 0 }}><div className="ell" style={{ fontWeight: 600 }}>{c.vendor}</div><div className="faint ell" style={{ fontSize: 10.5 }}>總額含稅 {window.fmt(total)}</div></div>
              <div style={{ flex: 1.7, display: 'flex', gap: 8 }} onClick={(e) => e.stopPropagation()}>
                {plan.map((p) => {const pd = pay[p.key] || { paid: false, date: '' };const inv = invs[p.key];return (
                      <div key={p.key} className="payunit">
                    <button className={'paybtn' + (pd.paid ? ' on' : '')} onClick={() => toggle(c, plan, p)}>
                      <span className="paylbl">{p.label}<span className="faint"> {p.pct}%</span></span>
                      <span className="mono" style={{ fontFamily: "monospace" }}>{window.fmtNum(p.amount)}</span>
                      <span className="paystate">{pd.paid ? '✓ 入帳 ' + pd.date.slice(5) : '點選入帳'}</span>
                    </button>
                    {inv ?
                        <span className="invtag ok">✓ 發票 {inv.no}</span> :
                        <button className="invtag todo" onClick={() => onOpenInvoice(c, window.invoiceSegments(c).find((s) => s.key === p.key))}>開發票</button>}
                  </div>);
                  })}
              </div>
              <div style={{ width: 96 }}><span className={'pill ' + sm.tone} style={{ height: 19, fontSize: 10 }}><span className="dot"></span>{sm.label}</span></div>
            </div>);
          })}
        </div>
      </div>
    </div>);
}

function InvoiceView({ cases, onOpen, onOpenInvoice }) {
  const dead = (c) => c.progress === '婉拒' || c.progress === '未成交';
  const eligible = cases.filter((c) => !dead(c) && (c.io && c.io.signed || c.pay && (c.pay.dep.paid || c.pay.bal.paid)));
  const pending = [],issued = [];
  eligible.forEach((c) => {
    window.invoiceSegments(c).forEach((seg) => {
      if (seg.invoiced) issued.push({ c, seg });else
      if (seg.due) pending.push({ c, seg });
    });
  });
  const segName = (seg) => seg.label === '全額結清' ? '全額發票' : seg.key === 'dep' ? '訂金發票' : '尾款發票';
  return (
    <div className="viewcol">
      <PageTop title="發票紀錄" sub="金額連動委刊單含稅總額· 訂金於簽署後提醒、尾款於案件完成後提醒" />
      <div className="scrollpad">
        <div className="sectlbl" style={{ marginBottom: 9 }}>待開立（{pending.length}）{pending.length > 0 && <span className="needfill pill" style={{ height: 18, fontSize: 10, marginLeft: 8 }}><span className="dot"></span>待處理</span>}</div>
        <div className="card" style={{ padding: 0, overflow: 'hidden', marginBottom: 20 }}>
          <div className="trow thead"><div style={{ width: 78 }}>代號</div><div style={{ flex: 1.3 }}>廠商</div><div style={{ width: 104 }}>款項</div><div style={{ width: 120, textAlign: 'right' }}>金額（含稅）</div><div style={{ width: 128 }}>提醒來源</div><div style={{ width: 104, textAlign: 'right' }}>動作</div></div>
          {pending.length === 0 && <div className="trow"><div className="faint">無待開立發票</div></div>}
          {pending.map(({ c, seg }) =>
          <div key={c.id + seg.key} className="trow trk" onClick={() => onOpen(c.id, 'io')}>
              <div style={{ width: 78 }} className="mono faint">{c.id}</div>
              <div style={{ flex: 1.3, minWidth: 0 }}><div className="ell" style={{ fontWeight: 600 }}>{c.vendor}</div><div className="faint ell" style={{ fontSize: 10.5 }}>{c.product}</div></div>
              <div style={{ width: 104 }}><span className="tag" style={{ height: 19, fontSize: 10 }}>{segName(seg)}</span></div>
              <div style={{ width: 120, textAlign: 'right' }} className="mono">{window.fmt(seg.amount)}</div>
              <div style={{ width: 128, fontSize: 10.5 }} className="faint">{seg.key === 'dep' ? '委刊單已簽署' : '案件已完成'}</div>
              <div style={{ width: 104, textAlign: 'right' }} onClick={(e) => e.stopPropagation()}>
                <button className="btn primary" style={{ height: 28, fontSize: 11.5 }} onClick={() => onOpenInvoice(c, seg)}>＋ 開立發票</button>
              </div>
            </div>
          )}
        </div>
        <div className="sectlbl" style={{ marginBottom: 9 }}>已開立（{issued.length}）</div>
        <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
          <div className="trow thead"><div style={{ flex: 1 }}>發票號碼</div><div style={{ flex: 1.3 }}>廠商</div><div style={{ width: 104 }}>款項</div><div style={{ width: 104 }}>日期</div><div style={{ width: 120, textAlign: 'right' }}>金額</div></div>
          {issued.length === 0 && <div className="trow"><div className="faint">尚未開立任何發票</div></div>}
          {issued.map(({ c, seg }) =>
          <div key={c.id + seg.key} className="trow trk" onClick={() => onOpenInvoice(c, seg)}>
              <div style={{ flex: 1 }} className="mono">{seg.invoice.no}</div>
              <div style={{ flex: 1.3, minWidth: 0 }} className="ell">{c.vendor}</div>
              <div style={{ width: 104 }}><span className="tag" style={{ height: 19, fontSize: 10 }}>{segName(seg)}</span></div>
              <div style={{ width: 104 }} className="mono faint">{seg.invoice.date}</div>
              <div style={{ width: 120, textAlign: 'right' }} className="mono">{window.fmt(seg.invoice.amount)}</div>
            </div>
          )}
        </div>
      </div>
    </div>);
}

/* 月報表・抽成（依實際入帳日期）*/
function ReportView({ cases, onOpen, toast }) {
  const periods = window.availablePeriods(cases);
  const years = periods.years.length ? periods.years : [new Date().getFullYear()];
  const latest = (() => {
    const recs = window.allReceipts(cases).sort((a, b) => b.y * 100 + b.m - (a.y * 100 + a.m));
    return recs[0] ? { y: recs[0].y, m: recs[0].m } : { y: years[0], m: new Date().getMonth() + 1 };
  })();
  const [mode, setMode] = React.useState('month'); // month | year
  const [year, setYear] = React.useState(latest.y);
  const [month, setMonth] = React.useState(latest.m);
  const MONTHS = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'];
  const fmt = window.fmt;

  const exportCsv = (rows, head) => {
    const lines = [head.join(',')];
    rows.forEach((r) => lines.push(r.join(',')));
    const blob = new Blob(['\ufeff' + lines.join('\n')], { type: 'text/csv;charset=utf-8' });
    const url = URL.createObjectURL(blob);const a = document.createElement('a');
    a.href = url;a.download = `果米帳務_${mode === 'year' ? year + '年' : year + '-' + month + '月'}.csv`;a.click();
    URL.revokeObjectURL(url);toast('已下載帳務 CSV');
  };

  const exportPdf = (title, tiles, head, rows, totalRow) => {
    const esc = (s) => String(s == null ? '' : s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
    const align = (i) => i === 0 ? 'left' : 'right';
    const tileHtml = tiles.map((t) => `<div class="tile${t.hl ? ' hl' : ''}"><div class="tl">${esc(t.label)}</div><div class="tv">${esc(t.value)}</div></div>`).join('');
    const headHtml = head.map((h, i) => `<th style="text-align:${align(i)}">${esc(h)}</th>`).join('');
    const rowHtml = rows.map((r) => `<tr>${r.map((c, i) => `<td style="text-align:${align(i)}">${esc(c)}</td>`).join('')}</tr>`).join('');
    const totHtml = totalRow ? `<tr class="tot">${totalRow.map((c, i) => `<td style="text-align:${align(i)}">${esc(c)}</td>`).join('')}</tr>` : '';
    const win = window.open('', '_blank');
    if (!win || !win.document) { toast('請允許彈出視窗以列印 PDF'); return; }
    win.document.write(`<!DOCTYPE html><html lang="zh-Hant"><head><meta charset="utf-8"><title>${esc(title)}</title>
<style>@page{size:A4;margin:18mm;}
body{font-family:"Noto Sans TC","新細明體",sans-serif;color:#2a2a2a;margin:0;}
h1{font-size:19px;margin:0 0 2px;letter-spacing:1px;} .sub{color:#999;font-size:11px;margin:0 0 18px;}
.tiles{display:flex;gap:12px;margin-bottom:18px;}
.tile{flex:1;border:1px solid #e3dccf;border-radius:10px;padding:12px 14px;}
.tile.hl{background:#f6ede1;border-color:#e8d6bd;}
.tl{font-size:11px;color:#888;margin-bottom:6px;} .tv{font-size:20px;font-weight:700;font-family:ui-monospace,monospace;}
table{border-collapse:collapse;width:100%;font-size:12px;}
th{background:#f4efe7;color:#666;font-weight:600;padding:9px 11px;border-bottom:2px solid #e3dccf;font-size:11px;}
td{padding:9px 11px;border-bottom:1px solid #eee;font-variant-numeric:tabular-nums;}
tr.tot td{border-top:2px solid #cfc4b2;font-weight:700;background:#faf7f1;}
.foot{margin-top:16px;color:#aaa;font-size:10px;}</style></head><body>
<h1>${esc(title)}</h1>
<p class="sub">果米工作室 · KOL 國美ゴメ5cm小人物の日常　|　抽成 15%，結餘為撥付國美金額</p>
<div class="tiles">${tileHtml}</div>
<table><thead><tr>${headHtml}</tr></thead><tbody>${rowHtml}${totHtml}</tbody></table>
<p class="foot">列印日期：${new Date().toLocaleDateString('zh-TW')}　·　本報表依實際入帳日期結算</p>
</body></html>`);
    win.document.close();
    setTimeout(() => { win.focus(); win.print(); }, 350);
    toast('已開啟列印視窗，選「儲存為 PDF」即可');
  };

  const periodCtrl =
  <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
      <div className="segctrl">
        <button className={'segbtn' + (mode === 'month' ? ' on' : '')} onClick={() => setMode('month')}>月報表</button>
        <button className={'segbtn' + (mode === 'year' ? ' on' : '')} onClick={() => setMode('year')}>年報表</button>
      </div>
      <select className="selinp" value={year} onChange={(e) => setYear(+e.target.value)}>
        {years.map((y) => <option key={y} value={y}>{y} 年</option>)}
      </select>
      {mode === 'month' && <select className="selinp" value={month} onChange={(e) => setMonth(+e.target.value)}>
        {MONTHS.map((mn, i) => <option key={i} value={i + 1}>{mn}</option>)}
      </select>}
    </div>;


  let body;
  if (mode === 'month') {
    const rep = window.monthlyReport(cases, year, month);
    body =
    <React.Fragment>
        <div className="reptiles">
          <div className="reptile"><div className="rtl">本月收款總額</div><div className="rtv mono" style={{ fontFamily: "monospace", fontSize: "24px" }}>{fmt(rep.total)}</div></div>
          <div className="reptile"><div className="rtl">抽成 15%</div><div className="rtv mono" style={{ color: 'var(--clay)', fontFamily: "monospace" }}>{fmt(rep.totalCommission)}</div></div>
          <div className="reptile hl"><div className="rtl">結餘（撥付國美）</div><div className="rtv mono" style={{ fontFamily: "monospace" }}>{fmt(rep.totalNet)}</div></div>
        </div>
        <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
          <div className="trow thead"><div style={{ flex: 1.6 }}>案名</div><div style={{ flex: 1.1 }}>收款項目</div><div style={{ flex: 1, textAlign: 'right' }}>收款</div><div style={{ flex: 1, textAlign: 'right' }}>抽成 15%</div><div style={{ flex: 1, textAlign: 'right' }}>結餘</div></div>
          {rep.rows.length === 0 && <div className="trow"><div className="faint">本月尚無入帳紀錄</div></div>}
          {rep.rows.map((r) =>
        <div key={r.id} className="trow trk" onClick={() => onOpen(r.id, 'io')}>
              <div style={{ flex: 1.6, minWidth: 0 }}><div className="ell" style={{ fontWeight: 600 }}>{r.vendor}</div><div className="faint ell" style={{ fontSize: 10.5 }}>{r.product}</div></div>
              <div style={{ flex: 1.1, fontSize: 11 }} className="faint">{[...new Set(r.segs)].join('、')}</div>
              <div style={{ flex: 1, textAlign: 'right', fontFamily: "monospace" }} className="mono">{fmt(r.amount)}</div>
              <div style={{ flex: 1, textAlign: 'right', color: 'var(--clay)', fontFamily: "monospace" }} className="mono">{fmt(r.commission)}</div>
              <div style={{ flex: 1, textAlign: 'right', fontWeight: 700, fontFamily: "monospace" }} className="mono">{fmt(r.net)}</div>
            </div>
        )}
          {rep.rows.length > 0 &&
        <div className="trow trtotal">
            <div style={{ flex: 1.6, fontWeight: 700 }}>總和</div><div style={{ flex: 1.1 }}></div>
            <div style={{ flex: 1, textAlign: 'right', fontWeight: 700, fontFamily: "monospace" }} className="mono">{fmt(rep.total)}</div>
            <div style={{ flex: 1, textAlign: 'right', fontWeight: 700, color: 'var(--clay)', fontFamily: "monospace" }} className="mono">{fmt(rep.totalCommission)}</div>
            <div style={{ flex: 1, textAlign: 'right', fontWeight: 700, fontFamily: "monospace" }} className="mono">{fmt(rep.totalNet)}</div>
          </div>}
        </div>
        <div style={{ display: 'flex', gap: 9, marginTop: 14 }}>
          <button className="btn" style={{ marginLeft: 'auto' }} disabled={!rep.rows.length} onClick={() => exportPdf(
          `${year} 年 ${month} 月帳務月報表`,
          [{ label: '本月收款總額', value: fmt(rep.total) }, { label: '抽成 15%', value: fmt(rep.totalCommission) }, { label: '結餘（撥付國美）', value: fmt(rep.totalNet), hl: true }],
          ['案名', '收款項目', '收款', '抽成 15%', '結餘'],
          rep.rows.map((r) => [r.vendor, [...new Set(r.segs)].join('、'), fmt(r.amount), fmt(r.commission), fmt(r.net)]),
          ['總和', '', fmt(rep.total), fmt(rep.totalCommission), fmt(rep.totalNet)])}>↧ 匯出 PDF</button>
          <button className="btn" disabled={!rep.rows.length} onClick={() => exportCsv(
          rep.rows.map((r) => [r.vendor, r.amount, r.commission, r.net]).concat([['總和', rep.total, rep.totalCommission, rep.totalNet]]),
          ['案名', '收款', '抽成15%', '結餘'])}>↧ 匯出 CSV</button>
        </div>
      </React.Fragment>;

  } else {
    const rep = window.annualReport(cases, year);
    body =
    <React.Fragment>
        <div className="reptiles">
          <div className="reptile"><div className="rtl">{year} 年收款總額</div><div className="rtv mono">{fmt(rep.total)}</div></div>
          <div className="reptile"><div className="rtl">全年抽成 15%</div><div className="rtv mono" style={{ color: 'var(--clay)' }}>{fmt(rep.totalCommission)}</div></div>
          <div className="reptile hl"><div className="rtl">全年結餘</div><div className="rtv mono">{fmt(rep.totalNet)}</div></div>
        </div>
        <div className="card" style={{ padding: 0, overflow: 'hidden' }}>
          <div className="trow thead"><div style={{ width: 90 }}>月份</div><div style={{ flex: 1 }}>案件數</div><div style={{ flex: 1, textAlign: 'right' }}>收款</div><div style={{ flex: 1, textAlign: 'right' }}>抽成 15%</div><div style={{ flex: 1, textAlign: 'right' }}>結餘</div></div>
          {rep.months.map((mo) =>
        <div key={mo.m} className={'trow' + (mo.amount ? ' trk' : '')} onClick={mo.amount ? () => {setMode('month');setMonth(mo.m);} : undefined} style={mo.amount ? null : { opacity: .5 }}>
              <div style={{ width: 90, fontWeight: 600 }}>{mo.m} 月</div>
              <div style={{ flex: 1, fontSize: 11 }} className="faint">{mo.count ? mo.count + ' 件' : '—'}</div>
              <div style={{ flex: 1, textAlign: 'right' }} className="mono">{mo.amount ? fmt(mo.amount) : '—'}</div>
              <div style={{ flex: 1, textAlign: 'right', color: mo.amount ? 'var(--clay)' : 'var(--ink-3)' }} className="mono">{mo.amount ? fmt(mo.commission) : '—'}</div>
              <div style={{ flex: 1, textAlign: 'right', fontWeight: mo.amount ? 700 : 400 }} className="mono">{mo.amount ? fmt(mo.net) : '—'}</div>
            </div>
        )}
          <div className="trow trtotal">
            <div style={{ width: 90, fontWeight: 700 }}>合計</div><div style={{ flex: 1 }}></div>
            <div style={{ flex: 1, textAlign: 'right', fontWeight: 700 }} className="mono">{fmt(rep.total)}</div>
            <div style={{ flex: 1, textAlign: 'right', fontWeight: 700, color: 'var(--clay)' }} className="mono">{fmt(rep.totalCommission)}</div>
            <div style={{ flex: 1, textAlign: 'right', fontWeight: 700 }} className="mono">{fmt(rep.totalNet)}</div>
          </div>
        </div>
        <div style={{ display: 'flex', gap: 9, marginTop: 14 }}>
          <button className="btn" style={{ marginLeft: 'auto' }} disabled={!rep.total} onClick={() => exportPdf(
          `${year} 年年度帳務報表`,
          [{ label: `${year} 年收款總額`, value: fmt(rep.total) }, { label: '全年抽成 15%', value: fmt(rep.totalCommission) }, { label: '全年結餘', value: fmt(rep.totalNet), hl: true }],
          ['月份', '案件數', '收款', '抽成 15%', '結餘'],
          rep.months.filter((m) => m.amount).map((m) => [m.m + ' 月', m.count + ' 件', fmt(m.amount), fmt(m.commission), fmt(m.net)]),
          ['合計', '', fmt(rep.total), fmt(rep.totalCommission), fmt(rep.totalNet)])}>↧ 匯出 PDF</button>
          <button className="btn" disabled={!rep.total} onClick={() => exportCsv(
          rep.months.filter((m) => m.amount).map((m) => [m.m + '月', m.count, m.amount, m.commission, m.net]).concat([['合計', '', rep.total, rep.totalCommission, rep.totalNet]]),
          ['月份', '案件數', '收款', '抽成15%', '結餘'])}>↧ 匯出 CSV</button>
        </div>
      </React.Fragment>;

  }

  return (
    <div className="viewcol">
      <PageTop title="月報表・抽成" sub="依實際入帳日期結算· 抽成 15%、結餘為撥付國美金額" right={periodCtrl} />
      <div className="scrollpad">{body}</div>
    </div>);
}
const SAMPLE_EMAIL = `多多您好，\n\n我們是「曦光手工皂」，主打天然冷製手工皂。想邀請國美合作一篇圖文貼文，介紹我們的洋甘菊舒緩皂。\n\n預算約 3 萬，預計下個月上線，再請提供報價，謝謝！\n\n曦光手工皂 行銷 王思婷`;
function NewCaseModal({ open, onClose, onCreate }) {
  const { Modal } = window;
  const [text, setText] = React.useState('');
  const [source, setSource] = React.useState('自來信');
  const [vendor, setVendor] = React.useState('');
  const [product, setProduct] = React.useState('');
  const [contact, setContact] = React.useState('');
  const [kind, setKind] = React.useState('general');
  const create = () => {
    onCreate({ vendor: vendor || '新廠商', product: product || '未命名產品', contact: contact || '窗口', kind, source, email: text || (source==='開發信'?'':SAMPLE_EMAIL) });
    setText('');setVendor('');setProduct('');setContact('');setKind('general');setSource('自來信');
  };
  return (
    <Modal open={open} onClose={onClose} width={620} title={source==='開發信'?'開發新廠商':'貼上廠商來信'} sub={source==='開發信'?'主動開發新廠商，後續可產出開發信範本':'貼上邀約信內容，建立案件後即可進行風控與報價'}>
      <div style={{ padding: '4px 20px 18px' }}>
        <div style={{ display:'flex', gap:8, marginBottom:14 }}>
          {['自來信','開發信'].map(s=><button key={s} className={'btn'+(source===s?' primary':'')} onClick={()=>setSource(s)}>
            {s==='自來信'?'↙ 廠商自來信':'↗ 主動開發'}
          </button>)}
        </div>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 }}>
          <div className="faint" style={{ fontSize: 10.5 }}>來信內容</div>
          <button className="btn ghost" style={{ height: 24, fontSize: 11 }} onClick={() => setText(SAMPLE_EMAIL)}>填入範例</button>
        </div>
        <textarea className="inp" rows={6} value={text} placeholder="貼上廠商來信全文…" onChange={(e) => setText(e.target.value)} />
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10, marginTop: 12 }}>
          <Field2 label="廠商名稱"><input className="inp" value={vendor} onChange={(e) => setVendor(e.target.value)} placeholder="如：曦光手工皂" /></Field2>
          <Field2 label="窗口"><input className="inp" value={contact} onChange={(e) => setContact(e.target.value)} placeholder="如：王思婷" /></Field2>
          <Field2 label="產品名稱"><input className="inp" value={product} onChange={(e) => setProduct(e.target.value)} placeholder="如：洋甘菊手工皂" /></Field2>
          <Field2 label="產品類別">
            <select className="inp" value={kind} onChange={(e) => setKind(e.target.value)}>
              <option value="food">食品・保健</option>
              <option value="cosmetic">化粧品・保養</option>
              <option value="general">一般商品</option>
            </select>
          </Field2>
        </div>
      </div>
      <div className="modalfoot">
        <button className="btn" onClick={onClose}>取消</button>
        <button className="btn primary" onClick={create}>建立案件 →</button>
      </div>
    </Modal>);

}
function Field2({ label, children }) {
  return <div><div className="faint" style={{ fontSize: 10, marginBottom: 4 }}>{label}</div>{children}</div>;
}

/* 排入行事曆 */
function ScheduleModal({ open, onClose, c, onSave, toast }) {
  const { Modal } = window;
  const conv = (s) => s && /\d{4}/.test(s) ? s.replace(/\//g, '-').slice(0, 10) : '';
  const [shoot, setShoot] = React.useState('');
  const [online, setOnline] = React.useState('');
  React.useEffect(() => {if (c) {setShoot(conv(c.schedule.shoot));setOnline(conv(c.schedule.online));}}, [c && c.id, open]);
  if (!c) return null;
  const save = (dl) => {
    const sh = shoot ? shoot.replace(/-/g, '/') : '';const on = online ? online.replace(/-/g, '/') : '';
    onSave({ shoot: sh, online: on });
    if (dl) {
      const evts = [];
      if (sh) evts.push({ date: sh, summary: `【拍攝】${c.vendor} · ${c.product}`, desc: `${c.id} ${c.vendor}` });
      if (on) evts.push({ date: on, summary: `【上線】${c.vendor} · ${c.product}`, desc: `${c.id} ${c.vendor}` });
      window.downloadICS(`${c.id}-schedule.ics`, window.buildICS(c.id, evts));
      toast('已下載 .ics · 可匯入 Google 行事曆');
    } else toast('已儲存檔期');
    onClose();
  };
  return (
    <Modal open={open} onClose={onClose} width={460} title="排入行事曆" sub={`${c.vendor} · ${c.product}`}>
      <div style={{ padding: '4px 20px 18px' }}>
        <Field2 label="拍攝日"><input className="inp" type="date" value={shoot} onChange={(e) => setShoot(e.target.value)} /></Field2>
        <div style={{ height: 12 }}></div>
        <Field2 label="上線日"><input className="inp" type="date" value={online} onChange={(e) => setOnline(e.target.value)} /></Field2>
        <div className="faint" style={{ fontSize: 10.5, marginTop: 12, lineHeight: 1.6 }}>
          下載的 .ics 為全天事件，於 Google 行事曆「設定 → 匯入與匯出」匯入即可，不需連動您的私人帳號。
        </div>
      </div>
      <div className="modalfoot">
        <button className="btn" onClick={() => save(false)}>僅儲存</button>
        <button className="btn primary" onClick={() => save(true)}>◵ 儲存並下載 .ics</button>
      </div>
    </Modal>);

}

/* 開立發票 Modal（按付款段）*/
function InvoiceModal({ open, onClose, target, onSave, toast }) {
  const { Modal } = window;
  const c = target && target.c;const seg = target && target.seg;
  const [no, setNo] = React.useState('');
  const [date, setDate] = React.useState('');
  const [amount, setAmount] = React.useState('');
  React.useEffect(() => {if (c && seg) {
      const existing = (c.invoices || {})[seg.key];
      setNo(existing ? existing.no : '');
      setDate(existing ? existing.date.replace(/\//g, '-') : new Date().toISOString().slice(0, 10));
      setAmount(existing ? existing.amount : seg.amount);
    }}, [c && c.id, seg && seg.key, open]);
  if (!c || !seg) return null;
  const segName = seg.label === '全額結清' ? '全額發票' : seg.key === 'dep' ? '訂金發票' : '尾款發票';
  const save = () => {
    if (!no.trim()) {toast('請輸入發票號碼');return;}
    onSave(c.id, seg.key, { no: no.trim(), date: (date || '').replace(/-/g, '/'), amount: Number(amount) || 0 });
    toast(segName + '已登錄');onClose();
  };
  return (
    <Modal open={open} onClose={onClose} width={460} title={'開立' + segName} sub={`${c.vendor} · ${c.id}`}>
      <div style={{ padding: '4px 20px 18px' }}>
        <div className="faint" style={{ fontSize: 10.5, marginBottom: 10, lineHeight: 1.6 }}>金額已自動帶入該款項金額（含稅），可手動修改。台灣另可串接財政部電子發票，此處為手動登錄。</div>
        <div style={{ marginBottom: 12 }}><div className="faint" style={{ fontSize: 10, marginBottom: 4 }}>發票號碼</div><input className="inp" value={no} placeholder="如 AB-12345678" onChange={(e) => setNo(e.target.value)} /></div>
        <div style={{ display: 'flex', gap: 10 }}>
          <div style={{ flex: 1 }}><div className="faint" style={{ fontSize: 10, marginBottom: 4 }}>開立日期</div><input className="inp" type="date" value={date} onChange={(e) => setDate(e.target.value)} /></div>
          <div style={{ width: 140 }}><div className="faint" style={{ fontSize: 10, marginBottom: 4 }}>金額（含稅）</div><input className="inp mono" type="number" value={amount} onChange={(e) => setAmount(e.target.value)} /></div>
        </div>
      </div>
      <div className="modalfoot">
        <button className="btn" onClick={onClose}>取消</button>
        <button className="btn primary" onClick={save}>✓ 登錄發票</button>
      </div>
    </Modal>);

}

Object.assign(window, { LawLibrary, DocsView, ContractView, CalView, PayView, InvoiceView, NewCaseModal, ScheduleModal, TrackingView, InvoiceModal, ReportView });