export interface ExportColumnDef<T> {
  key: string;
  label: string;
  render?: (row: T) => unknown;
  exportValue?: (row: T) => string | number;
}

const CSV_BOM = '\uFEFF';

export function escapeCsvCell(value: string | number): string {
  const raw = String(value);
  let firstNonControl = 0;
  while (firstNonControl < raw.length && raw.charCodeAt(firstNonControl) <= 32) firstNonControl++;
  const firstCode = raw.charCodeAt(0);
  const dangerous =
    firstCode === 9 ||
    firstCode === 10 ||
    firstCode === 13 ||
    '=+-@'.includes(raw[firstNonControl] ?? '');
  const str = dangerous ? `'${raw}` : raw;
  if (/[",\n\r]/.test(str)) return `"${str.replace(/"/g, '""')}"`;
  return str;
}

export function rowExportValue<T>(row: T, col: ExportColumnDef<T>): string | number {
  if (col.exportValue) return col.exportValue(row);
  if (col.render) {
    const v = col.render(row);
    if (typeof v === 'string' || typeof v === 'number') return v;
    return String(v ?? '');
  }
  const raw = (row as Record<string, unknown>)[col.key];
  return typeof raw === 'string' || typeof raw === 'number' ? raw : String(raw ?? '');
}

export function buildCsv<T>(columns: ExportColumnDef<T>[], rows: T[]): string {
  const headerLine = columns.map((c) => escapeCsvCell(c.label)).join(',');
  const lines = rows.map((row) =>
    columns.map((col) => escapeCsvCell(rowExportValue(row, col))).join(','),
  );
  return CSV_BOM + [headerLine, ...lines].join('\n');
}

export function buildJson<T>(columns: ExportColumnDef<T>[], rows: T[]): string {
  const payload = rows.map((row) => {
    const record: Record<string, string | number> = {};
    for (const col of columns) record[col.key] = rowExportValue(row, col);
    return record;
  });
  return JSON.stringify(payload, null, 2);
}

export function downloadBlob(filename: string, mime: string, content: string): void {
  if (typeof window === 'undefined') return;
  const blob = new Blob([content], { type: mime });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = filename;
  a.style.display = 'none';
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  URL.revokeObjectURL(url);
}
