// Minimal real .xlsx writer (no external libs). Builds a valid OOXML workbook
// as a ZIP of "stored" (uncompressed) entries with correct CRC32, so Excel opens
// it natively with bold header row, auto-fitted column widths and numeric cells.
// Exposes window.exportXlsx(filename, head, rows, opts).
(function(){
  // ---- CRC32 ----
  const CRC = (() => {
    const t = new Uint32Array(256);
    for (let n=0;n<256;n++){ let c=n; for(let k=0;k<8;k++) c = c&1 ? (0xEDB88320 ^ (c>>>1)) : (c>>>1); t[n]=c>>>0; }
    return t;
  })();
  function crc32(bytes){
    let c = 0xFFFFFFFF;
    for (let i=0;i<bytes.length;i++) c = CRC[(c ^ bytes[i]) & 0xFF] ^ (c>>>8);
    return (c ^ 0xFFFFFFFF) >>> 0;
  }
  const enc = new TextEncoder();
  const xml = s => String(s==null?'':s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;');
  function colLetter(i){ let s=''; i++; while(i>0){ const m=(i-1)%26; s=String.fromCharCode(65+m)+s; i=Math.floor((i-1)/26); } return s; }
  const isNum = v => typeof v==='number' || (typeof v==='string' && v.trim()!=='' && !isNaN(Number(v.replace(/,/g,''))));

  // ---- sheet ----
  function sheetXml(head, rows, widths, colStyles){
    colStyles = colStyles || [];
    const cols = widths.map((w,i)=>{
      const s = colStyles[i] ? ` style="${colStyles[i]}"` : '';
      return `<col min="${i+1}" max="${i+1}" width="${w}" customWidth="1"${s}/>`;
    }).join('');
    const rowXml = (cells, r, headerRow) => {
      const cs = cells.map((v,i)=>{
        const ref = colLetter(i)+r;
        const cs = colStyles[i];
        if (!headerRow && isNum(v)){
          const n = Number(String(v).replace(/,/g,''));
          const sAttr = cs ? ` s="${cs}"` : '';
          return `<c r="${ref}"${sAttr}><v>${n}</v></c>`;
        }
        const s = headerRow ? ' s="1"' : (cs ? ` s="${cs}"` : '');
        return `<c r="${ref}" t="inlineStr"${s}><is><t xml:space="preserve">${xml(v)}</t></is></c>`;
      }).join('');
      return `<row r="${r}">${cs}</row>`;
    };
    let body = rowXml(head, 1, true);
    rows.forEach((rw,i)=> body += rowXml(rw, i+2, false));
    return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><cols>${cols}</cols><sheetData>${body}</sheetData></worksheet>`;
  }

  // Styles: 0=default, 1=header, 2=date dd/mm/yy, 3=number 0.00, 4=text, 5=number #,##0.00, 6=percent #,##0.00"%", 7=centered text
  const STYLES = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><numFmts count="3"><numFmt numFmtId="164" formatCode="dd/mm/yy"/><numFmt numFmtId="165" formatCode="#,##0.00"/><numFmt numFmtId="166" formatCode="#,##0.00&quot;%&quot;"/></numFmts><fonts count="2"><font><sz val="11"/><name val="Calibri"/></font><font><b/><sz val="11"/><color rgb="FFFFFFFF"/><name val="Calibri"/></font></fonts><fills count="3"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill><fill><patternFill patternType="solid"><fgColor rgb="FF1F2A8E"/><bgColor indexed="64"/></patternFill></fill></fills><borders count="1"><border/></borders><cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs><cellXfs count="8"><xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/><xf numFmtId="0" fontId="1" fillId="2" borderId="0" xfId="0" applyFont="1" applyFill="1" applyAlignment="1"><alignment horizontal="center" vertical="center"/></xf><xf numFmtId="164" fontId="0" fillId="0" borderId="0" xfId="0" applyNumberFormat="1"/><xf numFmtId="2" fontId="0" fillId="0" borderId="0" xfId="0" applyNumberFormat="1"/><xf numFmtId="49" fontId="0" fillId="0" borderId="0" xfId="0" applyNumberFormat="1"/><xf numFmtId="165" fontId="0" fillId="0" borderId="0" xfId="0" applyNumberFormat="1"/><xf numFmtId="166" fontId="0" fillId="0" borderId="0" xfId="0" applyNumberFormat="1"/><xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" applyAlignment="1"><alignment horizontal="center"/></xf></cellXfs></styleSheet>`;

  const CONTENT_TYPES = `<?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/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/></Types>`;

  const RELS = `<?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>`;

  function workbookXml(sheetName){
    return `<?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="${xml(sheetName).slice(0,31)}" sheetId="1" r:id="rId1"/></sheets></workbook>`;
  }
  const WB_RELS = `<?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/styles" Target="styles.xml"/></Relationships>`;

  // ---- ZIP (stored) ----
  function zip(files){
    // files: [{name, data: Uint8Array}]
    const chunks = [];
    const central = [];
    let offset = 0;
    const u16 = n => [n&0xFF,(n>>8)&0xFF];
    const u32 = n => [n&0xFF,(n>>8)&0xFF,(n>>16)&0xFF,(n>>24)&0xFF];
    files.forEach(f => {
      const nameBytes = enc.encode(f.name);
      const crc = crc32(f.data);
      const size = f.data.length;
      const lfh = [0x50,0x4b,0x03,0x04, ...u16(20), ...u16(0), ...u16(0), ...u16(0), ...u16(0),
        ...u32(crc), ...u32(size), ...u32(size), ...u16(nameBytes.length), ...u16(0)];
      chunks.push(new Uint8Array(lfh), nameBytes, f.data);
      const cdh = [0x50,0x4b,0x01,0x02, ...u16(20), ...u16(20), ...u16(0), ...u16(0), ...u16(0), ...u16(0),
        ...u32(crc), ...u32(size), ...u32(size), ...u16(nameBytes.length), ...u16(0), ...u16(0), ...u16(0), ...u16(0), ...u32(0), ...u32(offset)];
      central.push(new Uint8Array(cdh), nameBytes);
      offset += lfh.length + nameBytes.length + size;
    });
    let cdSize = 0; central.forEach(c => cdSize += c.length);
    const cdOffset = offset;
    const eocd = [0x50,0x4b,0x05,0x06, ...u16(0), ...u16(0), ...u16(files.length), ...u16(files.length),
      ...u32(cdSize), ...u32(cdOffset), ...u16(0)];
    return new Blob([...chunks, ...central, new Uint8Array(eocd)], {type:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
  }

  window.exportXlsx = function(filename, head, rows, opts){
    opts = opts || {};
    const sheetName = opts.sheetName || 'Report';
    // auto column widths from content (chars), clamped
    const ncol = head.length;
    const widths = [];
    for (let i=0;i<ncol;i++){
      let max = String(head[i]||'').length;
      rows.forEach(r => { const v = String(r[i]==null?'':r[i]); if (v.length>max) max=v.length; });
      widths.push(Math.min(Math.max(max+2, 8), 42));
    }
    // Optional per-column width overrides (map colIndex -> width chars)
    const wOverride = opts.widths || {};
    for (const k in wOverride){ const i = Number(k); if (!isNaN(i) && i>=0 && i<ncol) widths[i] = wOverride[k]; }
    const colStyles = opts.colStyles || [];
    const file = name => enc.encode(name);
    const parts = [
      { name:'[Content_Types].xml', data: file(CONTENT_TYPES) },
      { name:'_rels/.rels', data: file(RELS) },
      { name:'xl/workbook.xml', data: file(workbookXml(sheetName)) },
      { name:'xl/_rels/workbook.xml.rels', data: file(WB_RELS) },
      { name:'xl/styles.xml', data: file(STYLES) },
      { name:'xl/worksheets/sheet1.xml', data: file(sheetXml(head, rows, widths, colStyles)) },
    ];
    const blob = zip(parts);
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url; a.download = filename.endsWith('.xlsx') ? filename : filename+'.xlsx';
    document.body.appendChild(a); a.click();
    setTimeout(()=>{ document.body.removeChild(a); URL.revokeObjectURL(url); }, 100);
  };
})();
