// ────────────────────────────────────────────────────────────────────────────
//  export.js — Pro: Export selected day data as a genuine styled .xlsx file.
//
//  Uses xlsx-js-style (SheetJS fork) for real XLSX output with cell colours.
// ────────────────────────────────────────────────────────────────────────────

import { utciCategory, UTCI_BANDS, sunburnMinutes, burnLabel } from './utils.js';

// ── Palette (6-char hex, no #) ────────────────────────────────────────────
const P = {
  brass:    'C8922A', brassDeep: 'A06E18',
  honey:    'F0E4C4', cream:     'FFFCF2', warmCream: 'FDF8EE',
  espresso: '1E1208', warmDark:  '4A3420', muted:     'B09870',
  border:   'C9B08A', line:      'EDE4CC', white:     'FFFFFF',
  alert:    'C44A3A', warn:      'C8601A', blue:      '3A5A9B',
};

// Group header row background colours
const GROUP_BG = {
  timeSun:  '7A5C2A', felt: '9B3A3A', air:    '3A6B9B',
  uvSolar:  '9A6B1A', surf: '4A7A4A', wind:   '3A5A7A',
  sky:      '3A6A6A', aq:  '6A4A7A',
};

// ── Style builder ─────────────────────────────────────────────────────────

const thinBorder = (hex = P.line) => ({ style: 'thin', color: { rgb: hex } });
const allBorders = (hex = P.line) => ({
  top: thinBorder(hex), bottom: thinBorder(hex),
  left: thinBorder(hex), right: thinBorder(hex),
});

function S({ bg = P.cream, fg = P.warmDark, bold = false, align = 'right', sz = 10, numFmt, wrap = false, borderHex = P.line } = {}) {
  return {
    fill:      { patternType: 'solid', fgColor: { rgb: bg } },
    font:      { sz, bold, color: { rgb: fg }, name: 'Calibri' },
    alignment: { horizontal: align, vertical: 'center', wrapText: !!wrap },
    border:    allBorders(borderHex),
    ...(numFmt ? { numFmt } : {}),
  };
}

// ── Column definitions ────────────────────────────────────────────────────

// [label, unit, align, wch (char width), numFmt]
const COLS = [
  // Time & Sun (2)
  ['Time',       '',       'center',  7,  null ],
  ['Solar Elev', '°',      'right',   9,  '0.0'],
  // Felt Temperature (5)
  ['SunSoak',    '°C',     'right',  9,  '0.0'],
  ['Condition',  '',       'left',   14,  null ],
  ['UTCI',       '°C',     'right',   7,  '0.0'],
  ['Δ UTCI−Air', '°C',     'right',   9,  '0.0'],
  ['Tmrt',       '°C',     'right',   7,  '0.0'],
  // Air (4)
  ['Air Temp',   '°C',     'right',   8,  '0.0'],
  ['Shade Air',  '°C',     'right',   9,  '0.0'],
  ['Dew Point',  '°C',     'right',   9,  '0.0'],
  ['Humidity',   '%',      'right',   8,  '0'  ],
  // UV & Solar (7)
  ['UV Index',   '',       'right',   8,  '0.0'],
  ['UV-A',       '',       'right',   7,  '0.0'],
  ['UV-B',       '',       'right',   7,  '0.00'],
  ['Burn Time',  '',       'right',   9,  null ],
  ['Direct Rad', 'W/m²',   'right',  10,  '0'  ],
  ['Diffuse',    'W/m²',   'right',   9,  '0'  ],
  ['Global Rad', 'W/m²',   'right',  10,  '0'  ],
  // Surfaces (6)
  ['Vehicle',    '°C',     'right',   8,  '0.0'],
  ['Indoor',     '°C',     'right',   8,  '0.0'],
  ['Concrete',   '°C',     'right',   9,  '0.0'],
  ['Soil Surf',  '°C',     'right',   9,  '0.0'],
  ['Soil 6cm',   '°C',     'right',   8,  '0.0'],
  ['Soil Moist', '%',      'right',   9,  '0'  ],
  // Wind (3)
  ['Wind',       'm/s',    'right',   7,  '0.0'],
  ['Gusts',      'm/s',    'right',   7,  '0.0'],
  ['Direction',  '',       'center',  9,  null ],
  // Sky & Precip (8)
  ['Cloud',      '%',      'right',   7,  '0'  ],
  ['Cloud Low',  '%',      'right',   9,  '0'  ],
  ['Cloud Mid',  '%',      'right',   9,  '0'  ],
  ['Cloud High', '%',      'right',  10,  '0'  ],
  ['Precip',     'mm/h',   'right',   8,  '0.00'],
  ['Rain Prob',  '%',      'right',   9,  '0'  ],
  ['Snowfall',   'cm/h',   'right',   9,  '0.00'],
  ['Visibility', 'km',     'right',  10,  '0.0'],
  // Air Quality (7)
  ['AQI',        '',       'right',   7,  '0'  ],
  ['Grass Pollen','g/m³',  'right',  12,  '0'  ],
  ['Birch Pollen','g/m³',  'right',  12,  '0'  ],
  ['Alder Pollen','g/m³',  'right',  12,  '0'  ],
  ['Mugwort',    'g/m³',   'right',  10,  '0'  ],
  ['Olive Pollen','g/m³',  'right',  12,  '0'  ],
  ['Ragweed',    'g/m³',   'right',  10,  '0'  ],
];

const TOTAL_COLS = COLS.length; // 42

// Groups: [label, span, bgKey]
const GROUPS = [
  ['Time & Sun',           2, 'timeSun'],
  ['Felt Temperature',     5, 'felt'   ],
  ['Air',                  4, 'air'    ],
  ['UV & Solar',           7, 'uvSolar'],
  ['Surfaces',             6, 'surf'   ],
  ['Wind',                 3, 'wind'   ],
  ['Sky & Precipitation',  8, 'sky'    ],
  ['Air Quality',          7, 'aq'     ],
];

// ── Worksheet cell helpers (X = the XLSXStyle instance) ──────────────────

function addr(X, r, c) {
  return X.utils.encode_cell({ r, c });
}

function setStr(X, ws, r, c, value, styleOpts) {
  ws[addr(X, r, c)] = { v: value ?? '', t: 's', s: S(styleOpts) };
}

function setNum(X, ws, r, c, value, styleOpts) {
  if (value == null) {
    ws[addr(X, r, c)] = { v: '', t: 's', s: S(styleOpts) };
  } else {
    ws[addr(X, r, c)] = { v: Number(value), t: 'n', s: S(styleOpts) };
  }
}

// ── UTCI colour helpers ───────────────────────────────────────────────────

function utciBgHex(val) {
  if (val == null) return P.cream;
  return (utciCategory(val)?.bg ?? '#' + P.cream).replace('#', '');
}

function utciFgHex(val) {
  if (val == null) return P.warmDark;
  return (utciCategory(val)?.fg ?? '#' + P.warmDark).replace('#', '');
}

// Blend a hex colour with white (ratio 0–1: 0 = full white, 1 = full colour)
function blendWhite(hex, ratio = 0.4) {
  const h = hex.replace('#', '');
  const n = parseInt(h, 16);
  const blend = (c) => Math.round(255 + (c - 255) * ratio);
  const r = blend((n >> 16) & 255);
  const g = blend((n >>  8) & 255);
  const b = blend( n        & 255);
  return [r, g, b].map(x => x.toString(16).padStart(2, '0')).join('');
}

function tempBg(val) {
  return blendWhite(utciBgHex(val), 0.42);
}

// ── Build Forecast worksheet ──────────────────────────────────────────────

function buildForecastSheet(X, rows, locationName, dateLabel, skinType) {
  const ws = {};
  const merges = [];

  // ── Row 0: Title ─────────────────────────────────────────────────────
  const titleStyle = S({ bg: P.brass, fg: P.white, bold: true, align: 'center', sz: 12, borderHex: P.brassDeep });
  setStr(X, ws, 0, 0, `SunScope  ·  ${locationName}  ·  ${dateLabel}`, { bg: P.brass, fg: P.white, bold: true, align: 'center', sz: 12, borderHex: P.brassDeep });
  for (let c = 1; c < TOTAL_COLS; c++) {
    ws[addr(X, 0, c)] = { v: '', t: 's', s: titleStyle };
  }
  merges.push({ s: { r: 0, c: 0 }, e: { r: 0, c: TOTAL_COLS - 1 } });

  // ── Row 1: Group headers ──────────────────────────────────────────────
  let colCursor = 0;
  for (const [label, span, bgKey] of GROUPS) {
    const bg = GROUP_BG[bgKey];
    const groupStyle = S({ bg, fg: P.white, bold: true, align: 'center', sz: 10, borderHex: bg });
    setStr(X, ws, 1, colCursor, label, { bg, fg: P.white, bold: true, align: 'center', sz: 10, borderHex: bg });
    for (let i = 1; i < span; i++) {
      ws[addr(X, 1, colCursor + i)] = { v: '', t: 's', s: groupStyle };
    }
    if (span > 1) merges.push({ s: { r: 1, c: colCursor }, e: { r: 1, c: colCursor + span - 1 } });
    colCursor += span;
  }

  // ── Row 2: Column headers ─────────────────────────────────────────────
  for (let c = 0; c < TOTAL_COLS; c++) {
    const [label, unit] = COLS[c];
    const text = unit ? `${label} (${unit})` : label;
    setStr(X, ws, 2, c, text, { bg: P.honey, fg: P.espresso, bold: true, align: 'center', sz: 10, borderHex: P.border });
  }

  // ── Rows 3+: Data ─────────────────────────────────────────────────────
  rows.forEach((r, i) => {
    const rowIdx  = 3 + i;
    const isNight = r.elev < -0.833;
    const rowBg   = isNight ? 'EAE3D5' : (i % 2 === 1 ? 'FDF8EE' : P.cream);
    const timeFg  = isNight ? P.muted : P.warmDark;

    const def = (bg = rowBg) => ({ bg, fg: P.warmDark, sz: 10, borderHex: P.line });

    // Time & Sun
    setStr(X, ws, rowIdx, 0, r.iso.slice(11, 16), { ...def(), fg: timeFg, align: 'center', bold: true });
    setNum(X, ws, rowIdx, 1, r.elev, { ...def(), align: 'right', numFmt: '0.0' });

    // Felt Temperature — SunSoak with UTCI band colour
    const sBg  = utciBgHex(r.utciAdj);
    const sFg  = utciFgHex(r.utciAdj);
    const cond = r.utciAdj != null ? (utciCategory(r.utciAdj)?.label ?? '') : '';
    setNum(X, ws, rowIdx, 2, r.utciAdj, { bg: sBg, fg: sFg, bold: true, align: 'right', sz: 10, numFmt: '0.0', borderHex: P.line });
    setStr(X, ws, rowIdx, 3, cond,      { bg: blendWhite(sBg, 0.55), fg: P.warmDark, align: 'left', sz: 10, borderHex: P.line });
    setNum(X, ws, rowIdx, 4, r.utci,    { bg: tempBg(r.utci), fg: P.espresso, sz: 10, numFmt: '0.0', borderHex: P.line });
    const delta = (r.utci != null && r.Ta != null) ? r.utci - r.Ta : null;
    setNum(X, ws, rowIdx, 5, delta, { bg: rowBg, fg: delta != null && delta > 3 ? P.warn : delta != null && delta < -3 ? '3F73C4' : P.warmDark, sz: 10, numFmt: '0.0', borderHex: P.line });
    setNum(X, ws, rowIdx, 6, r.Tmrt,    { bg: tempBg(r.Tmrt), fg: P.espresso, sz: 10, numFmt: '0.0', borderHex: P.line });

    // Air
    setNum(X, ws, rowIdx, 7, r.Ta,     { bg: tempBg(r.Ta), fg: P.espresso, sz: 10, numFmt: '0.0', borderHex: P.line });
    setNum(X, ws, rowIdx, 8, r.shadeT, { bg: tempBg(r.shadeT), fg: P.espresso, sz: 10, numFmt: '0.0', borderHex: P.line });
    setNum(X, ws, rowIdx, 9, r.dew,    { ...def(), numFmt: '0.0' });
    setNum(X, ws, rowIdx, 10, r.RH,    { ...def(), numFmt: '0' });

    // UV & Solar
    const uvFg = r.uv >= 8 ? P.alert : r.uv >= 3 ? P.warn : P.warmDark;
    setNum(X, ws, rowIdx, 11, r.uv,  { ...def(), fg: uvFg, numFmt: '0.0' });
    setNum(X, ws, rowIdx, 12, r.uvA, { ...def(), numFmt: '0.0' });
    setNum(X, ws, rowIdx, 13, r.uvB, { ...def(), numFmt: '0.00' });
    const burnMins = sunburnMinutes(r.uv, skinType);
    const burnStr  = r.uv > 0 ? burnLabel(burnMins) : '—';
    const burnFg   = r.uv > 0 ? (burnMins < 30 ? P.alert : P.warn) : P.muted;
    setStr(X, ws, rowIdx, 14, burnStr, { ...def(), fg: burnFg, align: 'right' });
    setNum(X, ws, rowIdx, 15, r.dir,  { ...def(), numFmt: '0' });
    setNum(X, ws, rowIdx, 16, r.dif,  { ...def(), numFmt: '0' });
    setNum(X, ws, rowIdx, 17, r.glob, { ...def(), numFmt: '0' });

    // Surfaces
    setNum(X, ws, rowIdx, 18, r.vehicleT,  { bg: tempBg(r.vehicleT),  fg: P.espresso, sz: 10, numFmt: '0.0', borderHex: P.line });
    setNum(X, ws, rowIdx, 19, r.indoorT,   { bg: tempBg(r.indoorT),   fg: P.espresso, sz: 10, numFmt: '0.0', borderHex: P.line });
    setNum(X, ws, rowIdx, 20, r.concreteT, { bg: tempBg(r.concreteT), fg: P.espresso, sz: 10, numFmt: '0.0', borderHex: P.line });
    setNum(X, ws, rowIdx, 21, r.soilT0, { ...def(), numFmt: '0.0' });
    setNum(X, ws, rowIdx, 22, r.soilT6, { ...def(), numFmt: '0.0' });
    setNum(X, ws, rowIdx, 23, r.soilM != null ? Math.round(r.soilM * 100) : null, { ...def(), numFmt: '0' });

    // Wind
    setNum(X, ws, rowIdx, 24, r.va,   { ...def(), numFmt: '0.0' });
    setNum(X, ws, rowIdx, 25, r.gust, { ...def(), numFmt: '0.0' });
    setStr(X, ws, rowIdx, 26, r.compass?.label ?? (r.wd != null ? `${Math.round(r.wd)}°` : '—'), { ...def(), align: 'center' });

    // Sky & Precip
    setNum(X, ws, rowIdx, 27, r.cc,        { ...def(), numFmt: '0' });
    setNum(X, ws, rowIdx, 28, r.ccLow,     { ...def(), numFmt: '0' });
    setNum(X, ws, rowIdx, 29, r.ccMid,     { ...def(), numFmt: '0' });
    setNum(X, ws, rowIdx, 30, r.ccHigh,    { ...def(), numFmt: '0' });
    setNum(X, ws, rowIdx, 31, r.precip > 0 ? r.precip : null,   { ...def(), fg: r.precip > 2 ? P.blue : P.warmDark, numFmt: '0.00' });
    setNum(X, ws, rowIdx, 32, r.precipProb, { ...def(), fg: r.precipProb >= 70 ? P.blue : P.warmDark, numFmt: '0' });
    setNum(X, ws, rowIdx, 33, r.snow > 0 ? r.snow : null,       { ...def(), numFmt: '0.00' });
    setNum(X, ws, rowIdx, 34, r.visKm,     { ...def(), numFmt: '0.0' });

    // Air Quality
    setNum(X, ws, rowIdx, 35, r.aqi,           { ...def(), fg: r.aqi >= 50 ? P.alert : P.warmDark, numFmt: '0' });
    setNum(X, ws, rowIdx, 36, r.grassPollen,   { ...def(), numFmt: '0' });
    setNum(X, ws, rowIdx, 37, r.birchPollen,   { ...def(), numFmt: '0' });
    setNum(X, ws, rowIdx, 38, r.alderPollen,   { ...def(), numFmt: '0' });
    setNum(X, ws, rowIdx, 39, r.mugwortPollen, { ...def(), numFmt: '0' });
    setNum(X, ws, rowIdx, 40, r.olivePollen,   { ...def(), numFmt: '0' });
    setNum(X, ws, rowIdx, 41, r.ragweedPollen, { ...def(), numFmt: '0' });
  });

  // ── Sheet metadata ────────────────────────────────────────────────────
  ws['!ref']    = X.utils.encode_range({ s: { r: 0, c: 0 }, e: { r: 3 + rows.length - 1, c: TOTAL_COLS - 1 } });
  ws['!cols']   = COLS.map(([,,,wch]) => ({ wch }));
  ws['!merges'] = merges;
  ws['!freeze'] = { xSplit: 1, ySplit: 3 };

  return ws;
}

// ── Build Legend worksheet ────────────────────────────────────────────────

function buildLegendSheet(X) {
  const ws = {};
  const merges = [];

  setStr(X, ws, 0, 0, 'SunSoak / UTCI Condition Scale', { bg: P.brass, fg: P.white, bold: true, align: 'center', sz: 11, borderHex: P.brassDeep });
  ws[addr(X, 0, 1)] = { v: '', t: 's', s: S({ bg: P.brass, fg: P.white, borderHex: P.brassDeep }) };
  merges.push({ s: { r: 0, c: 0 }, e: { r: 0, c: 1 } });

  setStr(X, ws, 1, 0, 'Condition',             { bg: P.honey, fg: P.espresso, bold: true, align: 'center', sz: 10, borderHex: P.border });
  setStr(X, ws, 1, 1, 'SunSoak / UTCI range',  { bg: P.honey, fg: P.espresso, bold: true, align: 'center', sz: 10, borderHex: P.border });

  let prev = -Infinity;
  UTCI_BANDS.forEach((band, i) => {
    const bg = band.bg.replace('#', '');
    const fg = band.fg.replace('#', '');
    const rangeStr = band.max != null
      ? `${prev === -Infinity ? 'below' : `${prev}°C to`} ${band.max}°C`
      : `${prev}°C and above`;
    setStr(X, ws, 2 + i, 0, band.label, { bg, fg, bold: true, align: 'center', sz: 10, borderHex: P.border });
    setStr(X, ws, 2 + i, 1, rangeStr,   { bg: P.cream, fg: P.warmDark, align: 'left', sz: 10, borderHex: P.line });
    if (band.max != null) prev = band.max;
  });

  ws['!ref']    = X.utils.encode_range({ s: { r: 0, c: 0 }, e: { r: 2 + UTCI_BANDS.length - 1, c: 1 } });
  ws['!cols']   = [{ wch: 18 }, { wch: 22 }];
  ws['!merges'] = merges;

  return ws;
}

// ── Main export function ──────────────────────────────────────────────────

export async function exportDayXls(rows, { locationName = '', dateLabel = '', skinType = 'type2' } = {}) {
  if (!rows || rows.length === 0) return;

  const xlsxMod = await import('xlsx-js-style').catch(() => null);
  if (!xlsxMod) {
    alert('Export requires the built version of SunScope. Run npm run build and serve from dist/.');
    return;
  }
  const X = xlsxMod.default ?? xlsxMod;

  const wb = X.utils.book_new();
  X.utils.book_append_sheet(wb, buildForecastSheet(X, rows, locationName, dateLabel, skinType), 'Forecast');
  X.utils.book_append_sheet(wb, buildLegendSheet(X), 'Legend');

  const buf  = X.write(wb, { bookType: 'xlsx', type: 'array' });
  const blob = new Blob([buf], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
  const url  = URL.createObjectURL(blob);

  const slug     = (locationName || 'location').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)/g, '');
  const dateSlug = (dateLabel || '').replace(/\s+/g, '-').replace(/[^a-z0-9-]/gi, '').toLowerCase();

  const a = document.createElement('a');
  a.href     = url;
  a.download = `sunscope-${slug}-${dateSlug}.xlsx`;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  URL.revokeObjectURL(url);
}
