// export.jsx — Excel (.xlsx) and PDF report generators

// ── Excel export (XLSX) ──────────────────────────────
// We write a minimal .xlsx by hand (no SheetJS needed).
// Three sheets: Habits, Riwayat (long-format), Ringkasan Harian.

function xmlEscape(s) {
  return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;')
    .replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&apos;');
}

function colLetter(n) {
  let s = '';
  while (n > 0) { const r = (n - 1) % 26; s = String.fromCharCode(65 + r) + s; n = Math.floor((n - 1) / 26); }
  return s;
}

function buildSheetXml(rows) {
  // rows: array of arrays. cells can be {v, t, s} or primitive.
  const rowsXml = rows.map((row, ri) => {
    const cells = row.map((cell, ci) => {
      if (cell === null || cell === undefined || cell === '') return '';
      const ref = `${colLetter(ci + 1)}${ri + 1}`;
      const obj = typeof cell === 'object' ? cell : { v: cell };
      const v = obj.v;
      const s = obj.s ? ` s="${obj.s}"` : '';
      if (typeof v === 'number') {
        return `<c r="${ref}"${s}><v>${v}</v></c>`;
      } else if (typeof v === 'boolean') {
        return `<c r="${ref}" t="b"${s}><v>${v ? 1 : 0}</v></c>`;
      } else {
        return `<c r="${ref}" t="inlineStr"${s}><is><t>${xmlEscape(v)}</t></is></c>`;
      }
    }).filter(Boolean).join('');
    return `<row r="${ri + 1}">${cells}</row>`;
  }).join('');
  return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetData>${rowsXml}</sheetData></worksheet>`;
}

// ── Tiny ZIP writer (store, no compression) ───────────
function crc32(arr) {
  const table = crc32.table || (crc32.table = (() => {
    const t = new Uint32Array(256);
    for (let i = 0; i < 256; i++) {
      let c = i;
      for (let j = 0; j < 8; j++) c = (c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1);
      t[i] = c >>> 0;
    }
    return t;
  })());
  let c = 0xFFFFFFFF;
  for (let i = 0; i < arr.length; i++) c = table[(c ^ arr[i]) & 0xFF] ^ (c >>> 8);
  return (c ^ 0xFFFFFFFF) >>> 0;
}

function zipFiles(files) {
  // files: [{name, data: Uint8Array}]
  const encoder = new TextEncoder();
  const parts = [];
  const central = [];
  let offset = 0;
  for (const f of files) {
    const nameBytes = encoder.encode(f.name);
    const data = typeof f.data === 'string' ? encoder.encode(f.data) : f.data;
    const crc = crc32(data);
    const size = data.length;
    const localHeader = new Uint8Array(30 + nameBytes.length);
    const dv = new DataView(localHeader.buffer);
    dv.setUint32(0, 0x04034b50, true);
    dv.setUint16(4, 20, true);  // version
    dv.setUint16(6, 0, true);   // flags
    dv.setUint16(8, 0, true);   // method=store
    dv.setUint16(10, 0, true);  // time
    dv.setUint16(12, 0, true);  // date
    dv.setUint32(14, crc, true);
    dv.setUint32(18, size, true);
    dv.setUint32(22, size, true);
    dv.setUint16(26, nameBytes.length, true);
    dv.setUint16(28, 0, true);
    localHeader.set(nameBytes, 30);
    parts.push(localHeader, data);
    // central directory
    const cd = new Uint8Array(46 + nameBytes.length);
    const dv2 = new DataView(cd.buffer);
    dv2.setUint32(0, 0x02014b50, true);
    dv2.setUint16(4, 20, true);
    dv2.setUint16(6, 20, true);
    dv2.setUint16(8, 0, true);
    dv2.setUint16(10, 0, true);
    dv2.setUint16(12, 0, true);
    dv2.setUint16(14, 0, true);
    dv2.setUint32(16, crc, true);
    dv2.setUint32(20, size, true);
    dv2.setUint32(24, size, true);
    dv2.setUint16(28, nameBytes.length, true);
    dv2.setUint32(42, offset, true);
    cd.set(nameBytes, 46);
    central.push(cd);
    offset += localHeader.length + data.length;
  }
  let cdSize = 0;
  for (const c of central) cdSize += c.length;
  const cdStart = offset;
  for (const c of central) parts.push(c);
  const eocd = new Uint8Array(22);
  const dv3 = new DataView(eocd.buffer);
  dv3.setUint32(0, 0x06054b50, true);
  dv3.setUint16(8, files.length, true);
  dv3.setUint16(10, files.length, true);
  dv3.setUint32(12, cdSize, true);
  dv3.setUint32(16, cdStart, true);
  parts.push(eocd);
  return new Blob(parts, { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
}

function exportToExcel(state) {
  const habits = state.habits;
  const checks = state.checks;

  // Sheet 1: Habits
  const habitsRows = [
    ['Kelompok', 'Tipe', 'Nama', 'Poin'],
  ];
  for (const h of habits) {
    habitsRows.push([h.group, 'Utama', h.name, h.points]);
    for (const a of (h.addons || [])) {
      habitsRows.push([h.group, 'Penyempurna', a.name, a.points]);
    }
  }

  // Sheet 2: Riwayat (long format)
  const dates = Object.keys(checks).sort();
  const riwayatRows = [
    ['Tanggal', 'Hari', 'Kelompok', 'Tipe', 'Habit', 'Selesai', 'Poin'],
  ];
  for (const dateKey of dates) {
    const dc = checks[dateKey];
    const date = parseDateKey(dateKey);
    const dayName = DAYS_ID_LONG[date.getDay()];
    for (const h of habits) {
      const mainDone = !!dc[h.id];
      riwayatRows.push([dateKey, dayName, h.group, 'Utama', h.name, mainDone ? 'Ya' : '-', mainDone ? h.points : 0]);
      for (const a of (h.addons || [])) {
        const done = !!dc[a.id];
        if (done) {
          riwayatRows.push([dateKey, dayName, h.group, 'Penyempurna', a.name, 'Ya', a.points]);
        }
      }
    }
  }

  // Sheet 3: Ringkasan Harian
  const summaryRows = [
    ['Tanggal', 'Hari', 'Total Poin', 'Max Poin', 'Persentase', 'Grade'],
  ];
  for (const dateKey of dates) {
    const dc = checks[dateKey];
    const date = parseDateKey(dateKey);
    const dayName = DAYS_ID_LONG[date.getDay()];
    const { total, max } = pointsForDay(habits, dc);
    const pct = max > 0 ? total / max : 0;
    summaryRows.push([
      dateKey, dayName, total, max,
      `${Math.round(pct * 100)}%`,
      gradeFor(pct),
    ]);
  }

  // Build xlsx
  const files = [
    { name: '[Content_Types].xml', data: `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
<Default Extension="xml" ContentType="application/xml"/>
<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/>
<Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>
<Override PartName="/xl/worksheets/sheet2.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>
<Override PartName="/xl/worksheets/sheet3.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/>
</Types>` },
    { name: '_rels/.rels', data: `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>
</Relationships>` },
    { name: 'xl/_rels/workbook.xml.rels', data: `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>
<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet2.xml"/>
<Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet3.xml"/>
</Relationships>` },
    { name: 'xl/workbook.xml', data: `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
<sheets>
<sheet name="Habits" sheetId="1" r:id="rId1"/>
<sheet name="Riwayat" sheetId="2" r:id="rId2"/>
<sheet name="Ringkasan Harian" sheetId="3" r:id="rId3"/>
</sheets>
</workbook>` },
    { name: 'xl/worksheets/sheet1.xml', data: buildSheetXml(habitsRows) },
    { name: 'xl/worksheets/sheet2.xml', data: buildSheetXml(riwayatRows) },
    { name: 'xl/worksheets/sheet3.xml', data: buildSheetXml(summaryRows) },
  ];

  const blob = zipFiles(files);
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = `Habit-Ican-${toDateKey(new Date())}.xlsx`;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  setTimeout(() => URL.revokeObjectURL(url), 1000);
}

// ── PDF Report (open print-friendly page) ────────────
function exportPDFReport(month, state) {
  const habits = state.habits;
  const checks = state.checks;
  const y = month.getFullYear();
  const m = month.getMonth();
  const daysCount = daysInMonth(y, m);
  const today = new Date();

  // Calculate stats
  let totalPts = 0, maxPts = 0, bestDay = null, bestPts = 0, doneDays = 0, elapsedDays = 0;
  const habitCounts = {};
  for (let d = 1; d <= daysCount; d++) {
    const dt = new Date(y, m, d);
    if (dt > today) break;
    elapsedDays++;
    const key = toDateKey(dt);
    const dc = checks[key] || {};
    const { total, max } = pointsForDay(habits, dc);
    totalPts += total; maxPts += max;
    if (total > bestPts) { bestPts = total; bestDay = d; }
    if (max > 0 && total / max >= 0.5) doneDays++;
    for (const h of habits) if (dc[h.id]) habitCounts[h.id] = (habitCounts[h.id] || 0) + 1;
  }
  const pct = maxPts > 0 ? totalPts / maxPts : 0;
  const grade = gradeFor(pct);
  const gradeC = gradeColor(grade);

  // Daily table
  const dailyRows = [];
  for (let d = 1; d <= daysCount; d++) {
    const dt = new Date(y, m, d);
    const dayName = DAYS_ID[dt.getDay()];
    const isFuture = dt > today;
    const dc = checks[toDateKey(dt)] || {};
    const { total, max } = pointsForDay(habits, dc);
    const dPct = max > 0 ? total / max : 0;
    dailyRows.push({ d, dayName, total, max, pct: dPct, grade: gradeFor(dPct), isFuture, dc });
  }

  // Habit consistency
  const habitStats = habits.map(h => ({
    h, count: habitCounts[h.id] || 0,
    pct: elapsedDays > 0 ? (habitCounts[h.id] || 0) / elapsedDays : 0,
  })).sort((a, b) => b.pct - a.pct);

  const html = `<!doctype html>
<html lang="id"><head><meta charset="utf-8">
<title>Laporan ${formatMonth(month)} — Habit Ican</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Newsreader:opsz,wght@6..72,400;6..72,500;6..72,600&family=Plus+Jakarta+Sans:wght@400;500;600;700&display=swap"/>
<style>
  * { box-sizing: border-box; margin: 0; padding: 0; }
  body { font-family: 'Plus Jakarta Sans', system-ui, sans-serif; color: #1c1b17; padding: 32px; background: white; line-height: 1.5; }
  .display { font-family: 'Newsreader', Georgia, serif; font-weight: 500; letter-spacing: -0.01em; }
  h1 { font-size: 32px; margin-bottom: 4px; }
  h2 { font-size: 18px; margin: 24px 0 12px; padding-bottom: 6px; border-bottom: 1px solid #e0d8c4; font-weight: 500; }
  .sub { color: #6b6960; font-size: 14px; margin-bottom: 24px; }
  .grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
  .card { border: 1px solid #e0d8c4; border-radius: 8px; padding: 12px 14px; }
  .label { font-size: 10px; color: #6b6960; text-transform: uppercase; letter-spacing: 0.08em; font-weight: 600; margin-bottom: 4px; }
  .val { font-family: 'Newsreader', serif; font-size: 24px; font-weight: 500; }
  .val .sub2 { font-family: 'Plus Jakarta Sans', sans-serif; font-size: 13px; color: #6b6960; font-weight: 400; margin-left: 4px; }
  table { width: 100%; border-collapse: collapse; font-size: 12px; }
  th { text-align: left; padding: 8px 6px; border-bottom: 1.5px solid #1c1b17; font-weight: 600; }
  td { padding: 6px; border-bottom: 1px solid #f0ecdf; }
  td.num { text-align: right; font-variant-numeric: tabular-nums; }
  td.grade { font-weight: 600; }
  .grade-A { color: #1D5A47; } .grade-B { color: #3D8B6F; } .grade-C { color: #C99A3C; } .grade-D { color: #B86A3A; } .grade-E { color: #9C4A3C; }
  .row-future td { color: #c5c0b3; }
  .habit-row { display: flex; align-items: center; gap: 10px; padding: 8px 0; border-bottom: 1px solid #f0ecdf; }
  .bar { flex: 1; height: 6px; background: #efece4; border-radius: 100px; overflow: hidden; }
  .bar-fill { height: 100%; background: #2c6e6b; }
  .footer { margin-top: 32px; padding-top: 16px; border-top: 1px solid #e0d8c4; font-size: 11px; color: #9a968b; }
  @media print {
    body { padding: 16px; }
    h2 { page-break-after: avoid; }
    table, .habit-row { page-break-inside: avoid; }
  }
  @page { margin: 12mm; }
</style>
</head><body>

<h1 class="display">Laporan Habit ${xmlEscape(formatMonth(month))}</h1>
<div class="sub">Dicetak ${formatDateLong(new Date())} · Habit Ican</div>

<div class="grid">
  <div class="card">
    <div class="label">Total Poin</div>
    <div class="val">${totalPts}<span class="sub2">/ ${maxPts}</span></div>
  </div>
  <div class="card">
    <div class="label">Grade</div>
    <div class="val" style="color: ${gradeC}">${grade}<span class="sub2">${Math.round(pct*100)}%</span></div>
  </div>
  <div class="card">
    <div class="label">Hari Konsisten</div>
    <div class="val">${doneDays}<span class="sub2">/ ${elapsedDays} hari</span></div>
  </div>
  <div class="card">
    <div class="label">Hari Terbaik</div>
    <div class="val">${bestDay ? `${bestDay} ${MONTHS_ID_SHORT[m]}` : '—'}<span class="sub2">${bestPts ? `${bestPts} pts` : ''}</span></div>
  </div>
</div>

<h2>Konsistensi per Habit</h2>
${habitStats.map(({h, count, pct}) => `
  <div class="habit-row">
    <span style="font-size: 16px;">${h.icon || '•'}</span>
    <div style="flex: 1; min-width: 0;">
      <div style="font-weight: 500;">${xmlEscape(h.group)}</div>
      <div style="font-size: 11px; color: #6b6960;">${xmlEscape(h.name)}</div>
    </div>
    <div class="bar" style="max-width: 200px;"><div class="bar-fill" style="width: ${pct*100}%"></div></div>
    <div style="min-width: 70px; text-align: right; font-variant-numeric: tabular-nums;">
      <b>${count}</b><span style="color: #6b6960;">/${elapsedDays}</span>
      <span style="color: #6b6960; font-size: 11px; display: block;">${Math.round(pct*100)}%</span>
    </div>
  </div>
`).join('')}

<h2>Catatan Harian</h2>
<table>
  <thead>
    <tr><th>Tgl</th><th>Hari</th><th class="num">Poin</th><th class="num">Max</th><th class="num">%</th><th>Grade</th><th>Catatan</th></tr>
  </thead>
  <tbody>
${dailyRows.map(r => {
  const doneHabits = habits.filter(h => r.dc[h.id]).map(h => h.group);
  const note = r.isFuture ? '—' : (doneHabits.length === 0 ? '<i style="color:#c5c0b3">tidak ada</i>' : doneHabits.join(', '));
  return `<tr class="${r.isFuture ? 'row-future' : ''}">
    <td>${r.d}</td>
    <td>${r.dayName}</td>
    <td class="num">${r.isFuture ? '' : r.total}</td>
    <td class="num">${r.isFuture ? '' : r.max}</td>
    <td class="num">${r.isFuture ? '' : Math.round(r.pct*100) + '%'}</td>
    <td class="grade grade-${r.grade}">${r.isFuture ? '' : r.grade}</td>
    <td style="font-size: 11px; color: #6b6960;">${note}</td>
  </tr>`;
}).join('')}
  </tbody>
</table>

<div class="footer">
  Laporan otomatis dari aplikasi Habit Ican · A=≥85%, B=≥70%, C=≥55%, D=≥40%, E=&lt;40%
</div>

<script>setTimeout(() => window.print(), 400);</script>
</body></html>`;

  const blob = new Blob([html], { type: 'text/html' });
  const url = URL.createObjectURL(blob);
  const win = window.open(url, '_blank');
  if (!win) {
    alert('Browser memblokir popup. Izinkan popup untuk situs ini lalu coba lagi.');
  }
  setTimeout(() => URL.revokeObjectURL(url), 60_000);
}

Object.assign(window, { exportToExcel, exportPDFReport, __exportPDFReport: exportPDFReport });
