// ─────────────────────────────────────────────────────────────────────
// REPORTS DATA — the expense ledger behind the Monthly Expense Report.
//
// Expenses already live in the archive (created by Upload Expense).
// Reports never invent data: every figure below is rolled up from these
// records at render time. Nothing is hand-keyed into a report.
// ─────────────────────────────────────────────────────────────────────

// Where the report goes. The collector family never receives raw
// receipts — they receive this organized package.
const ACCOUNTANT = {
  name: 'Patricia Lang, CPA',
  firm: 'Lang & Associates',
  email: 'p.lang@langcpa.com',
  role: 'Accountant · Voss Family Office',
};

// Allocation targets a report can group by.
const REPORT_TARGETS = {
  'old-yeller':     { title: '1973 Porsche 911 Carrera RS 2.7', short: '911 Carrera RS', nickname: 'Old Yeller' },
  'mercedes-300sl': { title: '1955 Mercedes-Benz 300 SL Gullwing', short: '300 SL Gullwing', nickname: 'Silver Arrow' },
  'overhead':       { title: 'Collection Overhead', short: 'Collection Overhead', nickname: 'Shared operating expense', isOverhead: true },
  'none':           { title: 'Unassigned Expenses', short: 'No Assignment', nickname: 'Reviewed · left unassigned — see report notes', isOverhead: true, isNone: true },
};
const REPORT_TARGET_ORDER = ['old-yeller', 'mercedes-300sl', 'overhead', 'none'];

// ─────────────────────────────────────────────────────────────────────
// REPORTING MODEL — periods, not calendar months.
//
// Every expense is either NOT YET REPORTED or INCLUDED IN A SENT REPORT.
// A sent report is defined by its reporting-period date range and the sent
// date. The current draft is generated automatically: it begins at the first
// unreported expense and runs through the most recent one — it may span
// multiple months. Sending marks every included expense as reported, so the
// next report picks up exactly where the last one left off.
//
// Prior sent reports (seed). Ranges — not months. Their expenses are reported.
const SENT_REPORTS_SEED = [
  { id: 'r-2026-03', start: 301, end: 331, sentOn: 'Mar 30, 2026' },
  { id: 'r-2026-04', start: 401, end: 430, sentOn: 'Apr 29, 2026' },
];

// The ledger. source: 'contributor' (submitted via Upload Receipt) or
// 'manager' (uploaded directly, e.g. a PDF invoice from a vendor).
const REPORT_EXPENSES = [
  // ── MAY 2026 ────────────────────────────────────────────────────
  { id: 'x-0501', period: '2026-05', date: 'May 1',
    vendor: 'Meridian Storage', contributor: null, source: 'manager',
    vehicleId: 'overhead', category: 'Storage',
    desc: 'Climate-controlled storage — May', amount: 1150.00,
    file: 'MS-2026-05.pdf', fileKind: 'pdf' },
  { id: 'x-0505', period: '2026-05', date: 'May 5',
    vendor: 'Meridian Insurance Group', contributor: null, source: 'manager',
    vehicleId: 'overhead', category: 'Insurance',
    desc: 'Collection policy — monthly premium', amount: 2485.00,
    file: 'MIG-2026-0507.pdf', fileKind: 'pdf' },
  { id: 'x-0508', period: '2026-05', date: 'May 8',
    vendor: 'Karosserie Meister GmbH', contributor: null, source: 'manager',
    vehicleId: 'mercedes-300sl', category: 'Bodywork & Paint',
    desc: 'Bare-metal respray — DB 180 silver, doors & bonnet refit', amount: 38400.00,
    file: 'KM-2026-0142.pdf', fileKind: 'pdf' },
  { id: 'x-0509', period: '2026-05', date: 'May 9',
    vendor: 'Reier Logistics', contributor: 'Tomás Reier', source: 'contributor',
    vehicleId: 'mercedes-300sl', category: 'Transport',
    desc: 'Transport to Rennsport Technik — head machining', amount: 780.00,
    note: 'No new marks. Front splitter protected as requested.',
    file: 'IMG_4471.heic', fileKind: 'photo' },
  { id: 'x-0514a', period: '2026-05', date: 'May 14',
    vendor: 'Rennsport Technik GmbH', contributor: 'Marco Brandt', source: 'contributor',
    vehicleId: 'old-yeller', category: 'Engine & Machine Work',
    desc: 'Engine rebuild — pistons, rings, main & rod bearing set', amount: 9655.20,
    note: 'Crank was within spec — no regrind needed on #3.',
    file: 'RT-2026-0488.pdf', fileKind: 'pdf' },
  { id: 'x-0514b', period: '2026-05', date: 'May 14',
    vendor: 'Rennsport Technik GmbH', contributor: 'Marco Brandt', source: 'contributor',
    vehicleId: 'old-yeller', category: 'Engine & Machine Work',
    desc: 'Crankshaft regrind & rebalance', amount: 2354.40,
    file: 'RT-2026-0488.pdf', fileKind: 'pdf' },
  { id: 'x-0514c', period: '2026-05', date: 'May 14',
    vendor: 'Rennsport Technik GmbH', contributor: 'Marco Brandt', source: 'contributor',
    vehicleId: 'mercedes-300sl', category: 'Engine & Machine Work',
    desc: 'Cylinder head overhaul — seats, guides, 3-angle valve job', amount: 6091.20,
    note: 'Head still out for machining.',
    file: 'RT-2026-0488.pdf', fileKind: 'pdf' },
  { id: 'x-0514d', period: '2026-05', date: 'May 14',
    vendor: 'Rennsport Technik GmbH', contributor: 'Marco Brandt', source: 'contributor',
    vehicleId: 'overhead', category: 'Shipping',
    desc: 'Export crating & sea freight (DE → US)', amount: 1274.40,
    file: 'RT-2026-0488.pdf', fileKind: 'pdf' },
  { id: 'x-0516', period: '2026-05', date: 'May 16',
    vendor: 'Gullwing Trim Werks', contributor: null, source: 'manager',
    vehicleId: 'mercedes-300sl', category: 'Interior',
    desc: 'Full leather interior — hides, seat rebuild, door cards', amount: 17850.00,
    file: 'GTW-0771.pdf', fileKind: 'pdf' },
  { id: 'x-0520', period: '2026-05', date: 'May 20',
    vendor: 'Stoddard NLA Parts', contributor: null, source: 'manager',
    vehicleId: 'old-yeller', category: 'Parts',
    desc: 'MFI throttle bodies — rebuild kit', amount: 1840.00,
    file: 'SNP-88213.pdf', fileKind: 'pdf' },
  { id: 'x-0522', period: '2026-05', date: 'May 22',
    vendor: 'Coachwerk Interiors', contributor: null, source: 'manager',
    vehicleId: 'mercedes-300sl', category: 'Interior',
    desc: 'Square-weave carpet set — German wool', amount: 2310.00,
    file: 'CW-1174.pdf', fileKind: 'pdf' },
  { id: 'x-0523', period: '2026-05', date: 'May 23',
    vendor: 'RS Werks California', contributor: 'Dana Whitlock', source: 'contributor',
    vehicleId: 'old-yeller', category: 'Parts',
    desc: 'NOS Fuchs wheel set — 7" & 8", period date stamps', amount: 13750.00,
    note: 'Date stamps verified — all four within build window.',
    file: 'RSW-3310.pdf', fileKind: 'pdf' },
  { id: 'x-0527', period: '2026-05', date: 'May 27',
    vendor: 'Concours Detail Co.', contributor: 'Dana Whitlock', source: 'contributor',
    vehicleId: 'old-yeller', category: 'Detailing',
    desc: 'Paint correction & wax — front clip', amount: 640.00,
    file: 'IMG_2208.heic', fileKind: 'photo' },
  { id: 'x-0529', period: '2026-05', date: 'May 29',
    vendor: 'Hagemann Inspection Services', contributor: null, source: 'manager',
    vehicleId: 'none', category: 'Inspection',
    desc: 'Pre-purchase inspection — 1962 250 GT candidate', amount: 485.00,
    note: 'Left unassigned — inspection of a candidate acquisition; the car was not purchased.',
    file: 'HIS-2026-0221.pdf', fileKind: 'pdf' },

  // ── APRIL 2026 (sent) ───────────────────────────────────────────
  { id: 'x-0401', period: '2026-04', date: 'Apr 1',
    vendor: 'Meridian Storage', contributor: null, source: 'manager',
    vehicleId: 'overhead', category: 'Storage',
    desc: 'Climate-controlled storage — April', amount: 1150.00,
    file: 'MS-2026-04.pdf', fileKind: 'pdf' },
  { id: 'x-0405', period: '2026-04', date: 'Apr 5',
    vendor: 'Meridian Insurance Group', contributor: null, source: 'manager',
    vehicleId: 'overhead', category: 'Insurance',
    desc: 'Collection policy — monthly premium', amount: 2485.00,
    file: 'MIG-2026-0405.pdf', fileKind: 'pdf' },
  { id: 'x-0409', period: '2026-04', date: 'Apr 9',
    vendor: 'Reier Logistics', contributor: 'Tomás Reier', source: 'contributor',
    vehicleId: 'mercedes-300sl', category: 'Transport',
    desc: '300 SL cylinder head — checkout to vendor', amount: 420.00,
    file: 'IMG_3980.heic', fileKind: 'photo' },
  { id: 'x-0416', period: '2026-04', date: 'Apr 16',
    vendor: 'Mahle Motorsport', contributor: 'Marco Brandt', source: 'contributor',
    vehicleId: 'old-yeller', category: 'Parts',
    desc: 'Piston set 90mm — RS spec, w/ certificate', amount: 4680.00,
    file: 'MM-55212.pdf', fileKind: 'pdf' },
  { id: 'x-0424', period: '2026-04', date: 'Apr 24',
    vendor: 'Concours Detail Co.', contributor: 'Dana Whitlock', source: 'contributor',
    vehicleId: 'mercedes-300sl', category: 'Detailing',
    desc: 'Interior deep clean — pre-trim removal', amount: 520.00,
    file: 'IMG_2011.heic', fileKind: 'photo' },

  // ── MARCH 2026 (sent) ───────────────────────────────────────────
  { id: 'x-0301', period: '2026-03', date: 'Mar 1',
    vendor: 'Meridian Storage', contributor: null, source: 'manager',
    vehicleId: 'overhead', category: 'Storage',
    desc: 'Climate-controlled storage — March', amount: 1150.00,
    file: 'MS-2026-03.pdf', fileKind: 'pdf' },
  { id: 'x-0305', period: '2026-03', date: 'Mar 5',
    vendor: 'Meridian Insurance Group', contributor: null, source: 'manager',
    vehicleId: 'overhead', category: 'Insurance',
    desc: 'Collection policy — monthly premium', amount: 2485.00,
    file: 'MIG-2026-0305.pdf', fileKind: 'pdf' },
  { id: 'x-0312', period: '2026-03', date: 'Mar 12',
    vendor: 'Rennsport Technik GmbH', contributor: 'Marco Brandt', source: 'contributor',
    vehicleId: 'old-yeller', category: 'Engine & Machine Work',
    desc: 'Engine teardown & measurement report', amount: 3120.00,
    file: 'RT-2026-0301.pdf', fileKind: 'pdf' },
  { id: 'x-0321', period: '2026-03', date: 'Mar 21',
    vendor: 'Reier Logistics', contributor: 'Tomás Reier', source: 'contributor',
    vehicleId: 'old-yeller', category: 'Transport',
    desc: 'Enclosed transport — Carmel to Stuttgart port', amount: 1980.00,
    file: 'IMG_3712.heic', fileKind: 'photo' },
];

// Reminder schedule options for the "Send Expense Report" nudge.
const REPORT_REMINDER_OPTIONS = [
  { id: 'monthly', label: 'Monthly', sub: 'Near the end of each month', task: 'Monthly' },
  { id: 'biweekly', label: 'Every 2 weeks', sub: '1st and 15th of each month', task: 'Every 2 weeks' },
  { id: 'quarterly', label: 'Quarterly', sub: 'Last week of each quarter', task: 'Quarterly' },
  { id: 'off', label: 'Off', sub: 'No reminder — send manually', task: null },
];

// Roadmap — the report library this section grows into.
const REPORT_LIBRARY_PLANNED = [
  { title: 'Vehicle Expense Report', icon: 'car' },
  { title: 'Vehicle Financial History', icon: 'BookOpen' },
  { title: 'Cost of Ownership', icon: 'CircleDollarSign' },
  { title: 'Restoration Cost Report', icon: 'wrench' },
  { title: 'Maintenance History', icon: 'ClipboardCheck' },
  { title: 'Annual Collection Expenses', icon: 'CalendarRange' },
  { title: 'Vendor Payment Report', icon: 'Building2' },
  { title: 'Contributor Reimbursement', icon: 'Users' },
  { title: 'Insurance Documentation', icon: 'Shield' },
  { title: 'Appraisal Support', icon: 'Stamp' },
  { title: 'Tax & Accounting Export', icon: 'Percent' },
  { title: 'Collection Financial Summary', icon: 'FileSpreadsheet' },
  { title: 'Vehicle Sale Package', icon: 'hammer' },
  { title: 'Vehicle Value & ROI', icon: 'TrendingUp' },
  { title: 'Records Completeness', icon: 'archive' },
];

// ── Working state — lives across screens for the session ─────────────
// Edits, exclusions, deletions and sent-status made while preparing a
// report survive navigation (e.g. jumping out to Upload Expense).
const ReportStore = {
  state: {
    edits: {},        // expenseId -> {desc?, amount?, category?, vehicleId?}
    excluded: {},     // expenseId -> true (held for a later report)
    deleted: {},      // expenseId -> true
    attached: {},     // expenseId -> filename (doc attached during review)
    sentReports: [],  // session-sent reports: { id, start, end, sentOn }
    reminder: 'monthly',
  },
  listeners: new Set(),
  set(patch) {
    Object.assign(this.state, patch);
    this.listeners.forEach(f => f());
  },
  setIn(key, id, val) {
    const next = { ...this.state[key] };
    if (val === undefined || val === null || val === false) delete next[id];
    else next[id] = val;
    this.set({ [key]: next });
  },
};

function useReportStore() {
  const [, tick] = React.useState(0);
  React.useEffect(() => {
    const f = () => tick(t => t + 1);
    ReportStore.listeners.add(f);
    return () => ReportStore.listeners.delete(f);
  }, []);
  return ReportStore.state;
}

// ── roll-ups (computed, never hand-keyed) ─────────────────────────────
const rMoney = (n) => '$' + n.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2 });

// Comparable date value for range filtering — 'May 14' → 514.
const RPT_MONTHS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const rptDateVal = (dateStr) => {
  const [mo, d] = dateStr.split(' ');
  return (RPT_MONTHS.indexOf(mo) + 1) * 100 + parseInt(d, 10);
};
const rptFmtVal = (val) => `${RPT_MONTHS[Math.floor(val / 100) - 1]} ${val % 100}`;
// "May 1 – May 25, 2026" — the canonical way to render a reporting period.
const fmtRange = (start, end) => `${rptFmtVal(start)} – ${rptFmtVal(end)}, 2026`;

// ── Reported-status model ────────────────────────────────────────────
// All sent reports (seed + session), earliest first.
function allSentReports() {
  const session = ReportStore.state.sentReports || [];
  return [...SENT_REPORTS_SEED, ...session].sort((a, b) => a.start - b.start);
}
// Is an expense's date already inside a sent report's range?
function isDateReported(dv) {
  return allSentReports().some(r => dv >= r.start && dv <= r.end);
}
// The ledger's not-yet-reported expenses (deletions honored).
function unreportedLedger() {
  const s = ReportStore.state;
  return REPORT_EXPENSES.filter(x => !s.deleted[x.id] && !isDateReported(rptDateVal(x.date)));
}
// The default draft reporting period: first → last unreported expense.
function draftPeriod() {
  const un = unreportedLedger();
  if (!un.length) {
    const sent = allSentReports();
    const lastEnd = sent.length ? sent[sent.length - 1].end : 500;
    return { start: lastEnd + 1, end: lastEnd + 1, empty: true };
  }
  const vals = un.map(x => rptDateVal(x.date));
  return { start: Math.min(...vals), end: Math.max(...vals), empty: false };
}
// Resolve a report id to its bounds. 'draft' (or falsy) = current draft period.
function reportById(id) {
  if (!id || id === 'draft') {
    const d = draftPeriod();
    return { id: 'draft', start: d.start, end: d.end, sentOn: null, draft: true, empty: d.empty };
  }
  const r = allSentReports().find(rr => rr.id === id);
  if (!r) { const d = draftPeriod(); return { id: 'draft', start: d.start, end: d.end, sentOn: null, draft: true, empty: d.empty }; }
  return { ...r, draft: false };
}
// Mark a reporting period as sent → its expenses become reported.
function markReported(range, sentOn) {
  const id = 'r-sent-' + Date.now();
  const session = [...(ReportStore.state.sentReports || []), { id, start: range.start, end: range.end, sentOn }];
  ReportStore.set({ sentReports: session });
  return id;
}

// Expenses for a period with session edits applied; excluded/deleted split out.
// A custom `range` ({start, end} date values) overrides the period's month —
// it pulls from the whole ledger, so a range can cross month boundaries.
// Resolve a vehicleId to display info. Fixed targets first; any other id
// (picked via search or split from the full collection pool) resolves against
// the roster so the report can group under any vehicle in the collection.
const _rtCache = {};
function reportTarget(vid) {
  if (REPORT_TARGETS[vid]) return REPORT_TARGETS[vid];
  if (_rtCache[vid]) return _rtCache[vid];
  const pool = (typeof vehiclePool === 'function' && typeof VEHICLE !== 'undefined')
    ? vehiclePool([VEHICLE, VEHICLE_MERCEDES]) : [];
  const v = pool.find(p => p.id === vid);
  const t = v
    ? { title: `${v.year} ${v.make} ${v.model}`, short: `${v.year} ${v.make} ${v.model}`,
        nickname: v.nickname || 'Collection vehicle', isOverhead: !v.nickname }
    : { title: vid, short: vid, nickname: '', isOverhead: true };
  _rtCache[vid] = t;
  return t;
}

// Group order for report lists: fixed vehicles, then any other vehicles that
// appear in the data (search/split picks), then overhead, then unassigned.
function reportGroupOrder(expenses) {
  const order = REPORT_TARGET_ORDER.filter(v => v !== 'overhead' && v !== 'none');
  expenses.forEach(x => {
    if (x.vehicleId !== 'overhead' && x.vehicleId !== 'none' && !order.includes(x.vehicleId)) order.push(x.vehicleId);
  });
  return [...order, 'overhead', 'none'];
}

function reportExpenses(periodId, range) {
  const s = ReportStore.state;
  const rep = reportById(periodId);
  const bounds = range || { start: rep.start, end: rep.end };
  const all = REPORT_EXPENSES
    .filter(x => rptDateVal(x.date) >= bounds.start && rptDateVal(x.date) <= bounds.end)
    .filter(x => !s.deleted[x.id])
    .map(x => ({ ...x, ...(s.edits[x.id] || {}),
      file: s.attached[x.id] || x.file,
      fileKind: s.attached[x.id] ? 'pdf' : x.fileKind }))
    // A split expense renders as one share per vehicle — same record, divided.
    .flatMap(x => {
      if (!x.split || x.split.length < 2) return [x];
      const shares = x.split.map(sp => Math.round(x.amount * sp.pct) / 100);
      shares[shares.length - 1] = Math.round((x.amount - shares.slice(0, -1).reduce((a, b) => a + b, 0)) * 100) / 100;
      return x.split.map((sp, i) => ({
        ...x, id: `${x.id}~${sp.id}`, baseId: x.id, vehicleId: sp.id,
        amount: shares[i], fullAmount: x.amount, splitPct: sp.pct,
      }));
    });
  return {
    included: all.filter(x => !s.excluded[x.baseId || x.id]),
    excluded: all.filter(x => s.excluded[x.baseId || x.id]),
  };
}

function reportRollup(expenses) {
  const total = expenses.reduce((s, x) => s + x.amount, 0);
  const byVehicle = {};
  const byCategory = {};
  expenses.forEach(x => {
    byVehicle[x.vehicleId] = (byVehicle[x.vehicleId] || 0) + x.amount;
    byCategory[x.category] = (byCategory[x.category] || 0) + x.amount;
  });
  const docs = expenses.filter(x => x.file).length;
  const missing = expenses.filter(x => !x.file);
  return { total, count: expenses.length, byVehicle, byCategory, docs, missing };
}

Object.assign(window, {
  ACCOUNTANT, REPORT_TARGETS, REPORT_TARGET_ORDER, SENT_REPORTS_SEED,
  REPORT_EXPENSES, REPORT_REMINDER_OPTIONS, REPORT_LIBRARY_PLANNED,
  ReportStore, useReportStore, rMoney, reportExpenses, reportRollup,
  reportTarget, reportGroupOrder,
  RPT_MONTHS, rptDateVal, rptFmtVal, fmtRange,
  allSentReports, isDateReported, unreportedLedger, draftPeriod, reportById, markReported,
});
