import ExcelJS from 'exceljs';

import type { ExportResult, ReportPayload, ReportSection } from '@/lib/reports/types';

/**
 * Multi-sheet Excel export. Sheet layout:
 *   - "Summary" — title + period + each KPI as a labelled row
 *   - One sheet per ReportSection — header row + data rows
 *
 * Excel sheet names are capped at 31 chars + can't contain certain
 * characters (\\/?*[]:); we sanitise + truncate accordingly.
 */

interface XlsxExportOptions {
  /** Filename without extension. Defaults to the payload's filenameSlug
   *  + the date range, matching the CSV exporter's pattern. */
  filenameOverride?: string;
}

export async function exportReportAsXlsx(
  payload: ReportPayload,
  options: XlsxExportOptions = {},
): Promise<ExportResult> {
  const workbook = new ExcelJS.Workbook();
  workbook.creator = 'Port Nimara CRM';
  workbook.created = new Date();
  workbook.modified = new Date();

  // ─── Summary sheet ─────────────────────────────────────────────────────
  const summary = workbook.addWorksheet('Summary', {
    properties: { tabColor: { argb: 'FF3A7BC8' } },
  });

  // Title block
  summary.mergeCells('A1:C1');
  const titleCell = summary.getCell('A1');
  titleCell.value = payload.title;
  titleCell.font = { name: 'Arial', size: 16, bold: true, color: { argb: 'FF0A1628' } };
  titleCell.alignment = { vertical: 'middle' };

  summary.mergeCells('A2:C2');
  summary.getCell('A2').value = payload.description ?? '';
  summary.getCell('A2').font = {
    name: 'Arial',
    size: 10,
    italic: true,
    color: { argb: 'FF6B6557' },
  };

  summary.mergeCells('A3:C3');
  summary.getCell('A3').value = `Period: ${formatDate(payload.range.from)} – ${formatDate(
    payload.range.to,
  )}`;
  summary.getCell('A3').font = { name: 'Arial', size: 10, color: { argb: 'FF6B6557' } };

  summary.mergeCells('A4:C4');
  summary.getCell('A4').value = `Generated: ${new Date().toISOString()}`;
  summary.getCell('A4').font = { name: 'Arial', size: 9, color: { argb: 'FF94A3B8' } };

  // KPI rows
  summary.addRow([]);
  const kpiHeader = summary.addRow(['Metric', 'Value', 'Hint']);
  kpiHeader.font = { name: 'Arial', bold: true, color: { argb: 'FFFFFFFF' } };
  kpiHeader.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF1E2844' } };
  for (const kpi of payload.kpis) {
    summary.addRow([kpi.label, kpi.value, kpi.hint ?? '']);
  }

  // Column widths
  summary.getColumn(1).width = 28;
  summary.getColumn(2).width = 22;
  summary.getColumn(3).width = 40;

  // Freeze the title block + KPI header
  summary.views = [{ state: 'frozen', ySplit: 6 }];

  // ─── One sheet per section ─────────────────────────────────────────────
  for (const section of payload.sections) {
    const sheetName = sanitizeSheetName(section.title);
    const sheet = workbook.addWorksheet(sheetName);

    // Header row from section columns
    const headerValues = section.columns.map((c) => c.label);
    const header = sheet.addRow(headerValues);
    header.font = { name: 'Arial', bold: true, color: { argb: 'FFFFFFFF' } };
    header.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF1E2844' } };
    header.alignment = { horizontal: 'left', vertical: 'middle' };
    header.height = 22;

    // Data rows
    addSectionRows(sheet, section);

    // Column widths — set based on header length plus content peek
    section.columns.forEach((col, i) => {
      const headerLen = col.label.length;
      const sampleLen = section.rows
        .slice(0, 20)
        .reduce((max, r) => Math.max(max, formatCell(col, r[col.key]).length), 0);
      sheet.getColumn(i + 1).width = Math.min(Math.max(headerLen, sampleLen) + 4, 50);
      if (col.align === 'right') {
        sheet.getColumn(i + 1).alignment = { horizontal: 'right' };
      }
    });

    // Freeze the header row
    sheet.views = [{ state: 'frozen', ySplit: 1 }];

    // Auto-filter on header
    sheet.autoFilter = {
      from: { row: 1, column: 1 },
      to: { row: 1, column: section.columns.length },
    };
  }

  const buffer = await workbook.xlsx.writeBuffer();
  const blob = new Blob([buffer], {
    type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  });

  return {
    filename:
      options.filenameOverride ?? `${payload.filenameSlug}-${dateRangeSlug(payload.range)}.xlsx`,
    mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    body: blob,
  };
}

/** Reusable filename derivation for UI previews. */
export function defaultXlsxFilename(payload: ReportPayload): string {
  return `${payload.filenameSlug}-${dateRangeSlug(payload.range)}.xlsx`;
}

function addSectionRows(sheet: ExcelJS.Worksheet, section: ReportSection): void {
  for (const row of section.rows) {
    const values = section.columns.map((col) => {
      const v = row[col.key];
      // Excel does best with native numbers / dates / strings; let
      // the column.format hint take precedence for display, fall back
      // to raw value for native typing.
      if (col.format) {
        return col.format(v);
      }
      if (v instanceof Date) return v;
      if (typeof v === 'number') return v;
      if (v === null || v === undefined) return '';
      return String(v);
    });
    sheet.addRow(values);
  }
}

function formatCell(col: { format?: (v: unknown) => string }, value: unknown): string {
  if (col.format) return col.format(value);
  if (value === null || value === undefined) return '';
  return String(value);
}

/** Excel sheet name constraints: max 31 chars, no \\/?*[]:. */
function sanitizeSheetName(raw: string): string {
  return raw.replace(/[\\/?*[\]:]/g, '-').slice(0, 31);
}

function formatDate(d: Date): string {
  return d.toISOString().slice(0, 10);
}

function dateRangeSlug(range: { from: Date; to: Date }): string {
  return `${formatDate(range.from)}_${formatDate(range.to)}`;
}
