/**
 * Import/Export der Diagramm-Rohdaten (CSV/JSON/XLSX) + kleine Download-Helfer.
 * Reine Utilities, kein WordPress-API-Zugriff nötig.
 */
import { xlsxScriptUrl } from './editorData.js';

/** Deckt sich mit PHP ce_pd_render_block (max Balken/Zellen). */
export const MAX_IMPORT_ROWS = 1000;

/** Schutz vor extrem großen Paste-/Upload-Inhalten (Browser-DoS). */
export const MAX_IMPORT_TEXT_CHARS = 512000;

/** XLSX nur bis zu dieser Dateigröße einlesen (ArrayBuffer). */
export const MAX_XLSX_BYTES = 5242880;

function parseLinkUrl(raw) {
  return String(raw ?? '').trim();
}

function parseLinkNewTab(raw) {
  const s = String(raw ?? '').trim().toLowerCase();
  if (s === '' || s === '0' || s === 'false' || s === 'no' || s === 'nein') return false;
  return s === '1' || s === 'true' || s === 'yes' || s === 'ja' || s === 'x';
}

/**
 * Parst einen Import-Text zu einem Array von Bar-Rows.
 * Unterstützt JSON (Array/Objekt), CSV (Semikolon) und Tabulator-Paste aus Excel/Sheets.
 * Header-Zeilen werden automatisch erkannt (toleriert Aliase wie "title", "val", "wert").
 */
export function parseImportedBars(text) {
  const t = String(text || '').trim();
  if (!t) return null;
  if (t.length > MAX_IMPORT_TEXT_CHARS) return null;
  if (t.startsWith('[') || t.startsWith('{')) {
    try {
      const parsed = JSON.parse(t);
      const arr = Array.isArray(parsed) ? parsed : [parsed];
      return arr.slice(0, MAX_IMPORT_ROWS).map((b) => ({
        value: Number(b.value) || 0,
        unit: String(b.unit || '').trim(),
        headline: String(b.headline || b.label || '').trim(),
        paragraph: String(b.paragraph || b.desc || b.description || '').trim(),
        smallPrint: String(b.smallPrint || b.source || '').trim(),
        groupKey: b.groupKey != null ? String(b.groupKey).trim() : '',
        color: b.color ? String(b.color).trim() : undefined,
        scatterX: b.scatterX != null && b.scatterX !== '' ? Number(b.scatterX) : undefined,
        scatterY: b.scatterY != null && b.scatterY !== '' ? Number(b.scatterY) : undefined,
        annotation: b.annotation ? String(b.annotation).trim() : undefined,
        annotationDesc: b.annotationDesc ? String(b.annotationDesc).trim() : undefined,
        link: parseLinkUrl(b.link ?? b.linkUrl),
        linkNewTab: parseLinkNewTab(b.linkNewTab ?? b.linkNewWindow),
      }));
    } catch (_) { return null; }
  }
  const lines = t.split(/\r?\n/).filter((l) => l.trim());
  if (lines.length === 0) return null;
  /** Semikolon oder Tab als Spaltentrenner — Tabs werden automatisch erkannt (Paste aus Excel / Google Sheets). */
  const firstLine = lines[0];
  const tabCount = (firstLine.match(/\t/g) || []).length;
  const semiCount = (firstLine.match(/;/g) || []).length;
  const sep = tabCount > 0 && tabCount >= semiCount ? '\t' : ';';

  /** Trennt an sep; Zeichen innerhalb von "…" zählen nicht (RFC 4180). */
  function splitCsvLine(line) {
    const sepLen = sep.length;
    const out = [];
    let i = 0;
    const len = line.length;
    while (i < len) {
      let field = '';
      if (line[i] === '"') {
        i++;
        while (i < len) {
          if (line[i] === '"') {
            if (i + 1 < len && line[i + 1] === '"') {
              field += '"';
              i += 2;
              continue;
            }
            i++;
            break;
          }
          field += line[i++];
        }
      } else {
        while (i < len && line.slice(i, i + sepLen) !== sep) {
          field += line[i++];
        }
      }
      out.push(field.replace(/^["']|["']$/g, '').trim());
      if (i < len && line.slice(i, i + sepLen) === sep) i += sepLen;
    }
    return out;
  }

  const CSV_HEADER_TO_FIELD = {
    value: 'value', val: 'value', wert: 'value',
    unit: 'unit',
    headline: 'headline', title: 'headline', label: 'headline',
    paragraph: 'paragraph', desc: 'paragraph', description: 'paragraph',
    smallprint: 'smallPrint', source: 'smallPrint', note: 'smallPrint',
    color: 'color', colour: 'color', hex: 'color',
    scatterx: 'scatterX', scattery: 'scatterY',
    groupkey: 'groupKey', group: 'groupKey', xgroup: 'groupKey',
    annotation: 'annotation', annotationdesc: 'annotationDesc',
    link: 'link', url: 'link', href: 'link', linkurl: 'link',
    linknewtab: 'linkNewTab', newtab: 'linkNewTab', neuertab: 'linkNewTab',
    opentab: 'linkNewTab', targetblank: 'linkNewTab',
  };

  function normalizeHeaderToken(raw) {
    return String(raw || '').replace(/^\uFEFF/, '').trim().toLowerCase().replace(/[\s_\-]+/g, '');
  }

  function mapHeaderCell(cell) {
    const k = normalizeHeaderToken(cell);
    return CSV_HEADER_TO_FIELD[k] || null;
  }

  function buildFieldIndex(headerParts) {
    const fieldToIndex = {};
    headerParts.forEach((cell, i) => {
      const f = mapHeaderCell(cell);
      if (f && fieldToIndex[f] === undefined) fieldToIndex[f] = i;
    });
    return fieldToIndex;
  }

  function isUsableHeader(fieldToIndex) {
    return fieldToIndex.value !== undefined && Object.keys(fieldToIndex).length >= 2;
  }

  function barFromFieldMap(parts, fieldToIndex) {
    const get = (name) => {
      const i = fieldToIndex[name];
      if (i === undefined) return undefined;
      return parts[i];
    };
    const optNum = (name) => {
      const s = get(name);
      if (s == null || String(s).trim() === '') return undefined;
      const n = Number(s);
      return isNaN(n) ? undefined : n;
    };
    return {
      value: Number(get('value')) || 0,
      unit: String(get('unit') || '').trim(),
      headline: String(get('headline') || '').trim(),
      paragraph: String(get('paragraph') || '').trim(),
      smallPrint: String(get('smallPrint') || '').trim(),
      link: parseLinkUrl(get('link')),
      linkNewTab: parseLinkNewTab(get('linkNewTab')),
      color: (get('color') || '').trim() || undefined,
      scatterX: optNum('scatterX'),
      scatterY: optNum('scatterY'),
      groupKey: String(get('groupKey') || '').trim(),
      annotation: (get('annotation') || '').trim() || undefined,
      annotationDesc: (get('annotationDesc') || '').trim() || undefined,
    };
  }

  function barFromLegacyParts(parts) {
    const optNum = (i) => {
      const s = parts[i];
      if (s == null || s === '') return undefined;
      const n = Number(s);
      return isNaN(n) ? undefined : n;
    };
    return {
      value: Number(parts[0]) || 0,
      unit: (parts[1] || '').trim(),
      headline: (parts[2] || '').trim(),
      paragraph: (parts[3] || '').trim(),
      smallPrint: (parts[4] || '').trim(),
      link: parseLinkUrl(parts[11]),
      linkNewTab: parseLinkNewTab(parts[12]),
      color: (parts[5] || '').trim() || undefined,
      scatterX: optNum(6),
      scatterY: optNum(7),
      groupKey: (parts[8] || '').trim(),
      annotation: (parts[9] || '').trim() || undefined,
      annotationDesc: (parts[10] || '').trim() || undefined,
    };
  }

  const firstParts = splitCsvLine(lines[0]);
  const headerMap = buildFieldIndex(firstParts);
  const useHeader = isUsableHeader(headerMap);
  const dataLines = useHeader ? lines.slice(1) : lines;

  return dataLines.slice(0, MAX_IMPORT_ROWS).map((line) => {
    const parts = splitCsvLine(line);
    if (useHeader) return barFromFieldMap(parts, headerMap);
    return barFromLegacyParts(parts);
  });
}

export function escapeCsvField(val) {
  const str = val == null ? '' : String(val);
  if (/[";\r\n]/.test(str)) return '"' + str.replace(/"/g, '""') + '"';
  return str;
}

export function exportBarsAsCsv(bars) {
  const headers = [
    'value', 'unit', 'headline', 'paragraph', 'smallPrint',
    'link', 'linkNewTab',
    'color', 'groupKey', 'scatterX', 'scatterY', 'annotation', 'annotationDesc',
  ];
  const lines = [headers.map(escapeCsvField).join(';')];
  (bars || []).forEach((b) => {
    lines.push([
      escapeCsvField(b.value),
      escapeCsvField(b.unit),
      escapeCsvField(b.headline),
      escapeCsvField(b.paragraph),
      escapeCsvField(b.smallPrint),
      escapeCsvField(b.link),
      escapeCsvField(b.linkNewTab === true ? '1' : '0'),
      escapeCsvField(b.color),
      escapeCsvField(b.groupKey),
      escapeCsvField(b.scatterX),
      escapeCsvField(b.scatterY),
      escapeCsvField(b.annotation),
      escapeCsvField(b.annotationDesc),
    ].join(';'));
  });
  return lines.join('\r\n');
}

export function downloadTextFile(filename, text, mime) {
  const blob = new Blob([text], { type: mime || 'text/plain;charset=utf-8' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = filename;
  a.rel = 'noopener';
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  URL.revokeObjectURL(url);
}

export function downloadDataUrl(filename, dataUrl) {
  const a = document.createElement('a');
  a.href = dataUrl;
  a.download = filename;
  a.rel = 'noopener';
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
}

/** SheetJS lazy loader (für .xlsx-Import). URL kommt aus PHP via script_module_data. */
let _xlsxPromise = null;
export function loadXlsx() {
  if (window.XLSX) return Promise.resolve(window.XLSX);
  if (_xlsxPromise) return _xlsxPromise;
  if (!xlsxScriptUrl) return Promise.reject(new Error('xlsxScriptUrl not configured'));
  _xlsxPromise = new Promise((resolve, reject) => {
    const s = document.createElement('script');
    s.src = xlsxScriptUrl;
    s.onload = () => resolve(window.XLSX);
    s.onerror = () => { _xlsxPromise = null; reject(new Error('SheetJS load failed')); };
    document.head.appendChild(s);
  });
  return _xlsxPromise;
}
