export type DateFormat = "DD/MM/YYYY" | "YYYY-MM-DD" | "MM/DD/YYYY" | "M/D/YYYY";

export interface ColumnMapping {
  dateCol: string;
  descriptionCol: string;
  amountMode: "single" | "debit_credit";
  amountCol?: string;
  debitCol?: string;
  creditCol?: string;
  merchantCol?: string;
  categoryCol?: string;
}

export interface BankPreset {
  bankName: string;
  mapping: ColumnMapping;
  dateFormat: DateFormat;
}

export interface ParsedTransaction {
  date: string;
  description: string;
  amount: number;
  transaction_type: string;
  merchant_name?: string;
  category?: string;
}

export function parseCSVRows(text: string): string[][] {
  const rows: string[][] = [];
  let row: string[] = [];
  let field = "";
  let inQuotes = false;

  for (let i = 0; i < text.length; i++) {
    const c = text[i];
    const next = text[i + 1];
    if (inQuotes) {
      if (c === '"' && next === '"') { field += '"'; i++; }
      else if (c === '"') { inQuotes = false; }
      else { field += c; }
    } else {
      if (c === '"') { inQuotes = true; }
      else if (c === ',') { row.push(field.trim()); field = ""; }
      else if (c === '\r' && next === '\n') {
        row.push(field.trim());
        if (row.some((f) => f !== "")) rows.push(row);
        row = []; field = ""; i++;
      } else if (c === '\n' || c === '\r') {
        row.push(field.trim());
        if (row.some((f) => f !== "")) rows.push(row);
        row = []; field = "";
      } else { field += c; }
    }
  }
  if (field || row.length > 0) {
    row.push(field.trim());
    if (row.some((f) => f !== "")) rows.push(row);
  }
  return rows;
}

export function parseDate(raw: string, format: DateFormat): string {
  const s = raw.trim();
  try {
    if (format === "DD/MM/YYYY") {
      const [d, m, y] = s.split("/");
      if (!d || !m || !y || y.length !== 4) return "";
      return `${y}-${m.padStart(2, "0")}-${d.padStart(2, "0")}`;
    }
    if (format === "YYYY-MM-DD") {
      return /^\d{4}-\d{2}-\d{2}$/.test(s) ? s : "";
    }
    if (format === "MM/DD/YYYY" || format === "M/D/YYYY") {
      const [m, d, y] = s.split("/");
      if (!d || !m || !y || y.length !== 4) return "";
      return `${y}-${m.padStart(2, "0")}-${d.padStart(2, "0")}`;
    }
  } catch { return ""; }
  return "";
}

export function detectHasHeaders(rows: string[][], dateFormat: DateFormat): boolean {
  if (rows.length === 0) return false;
  return parseDate(rows[0][0] ?? "", dateFormat) === "";
}

export function getColumnLabels(rows: string[][], hasHeaders: boolean): string[] {
  if (hasHeaders && rows.length > 0) {
    return rows[0].map((h, i) => h || `Column ${i + 1}`);
  }
  const maxCols = rows.reduce((m, r) => Math.max(m, r.length), 0);
  return Array.from({ length: maxCols }, (_, i) => `Column ${i + 1}`);
}

export function getDataRows(rows: string[][], hasHeaders: boolean): string[][] {
  return hasHeaders ? rows.slice(1) : rows;
}

export function applyMapping(
  dataRows: string[][],
  columnLabels: string[],
  mapping: ColumnMapping,
  dateFormat: DateFormat
): ParsedTransaction[] {
  const idx = (name: string) => columnLabels.indexOf(name);
  const dateIdx = idx(mapping.dateCol);
  const descIdx = idx(mapping.descriptionCol);
  const merchantIdx = mapping.merchantCol ? idx(mapping.merchantCol) : -1;
  const categoryIdx = mapping.categoryCol ? idx(mapping.categoryCol) : -1;

  const results: ParsedTransaction[] = [];
  for (const row of dataRows) {
    const date = parseDate(row[dateIdx] ?? "", dateFormat);
    const description = (row[descIdx] ?? "").trim();
    if (!date || !description) continue;

    let amount = 0;
    let transaction_type = "debit";

    if (mapping.amountMode === "single" && mapping.amountCol) {
      const raw = (row[idx(mapping.amountCol)] ?? "").replace(/[^\d.\-+]/g, "");
      const val = parseFloat(raw);
      if (isNaN(val) || val === 0) continue;
      amount = Math.abs(val);
      transaction_type = val < 0 ? "debit" : "credit";
    } else if (mapping.amountMode === "debit_credit") {
      const debitIdx = mapping.debitCol ? idx(mapping.debitCol) : -1;
      const creditIdx = mapping.creditCol ? idx(mapping.creditCol) : -1;
      const dVal = parseFloat((row[debitIdx] ?? "").replace(/[^\d.]/g, ""));
      const cVal = parseFloat((row[creditIdx] ?? "").replace(/[^\d.]/g, ""));
      if (!isNaN(dVal) && dVal > 0) { amount = dVal; transaction_type = "debit"; }
      else if (!isNaN(cVal) && cVal > 0) { amount = cVal; transaction_type = "credit"; }
      else continue;
    }

    if (amount <= 0) continue;
    const tx: ParsedTransaction = { date, description, amount, transaction_type };
    if (merchantIdx >= 0 && row[merchantIdx]) tx.merchant_name = row[merchantIdx].trim();
    if (categoryIdx >= 0 && row[categoryIdx]) tx.category = row[categoryIdx].trim();
    results.push(tx);
  }
  return results;
}

export function saveBankPreset(preset: BankPreset): void {
  try {
    const existing = loadBankPresets().filter((p) => p.bankName !== preset.bankName);
    localStorage.setItem("csv-presets", JSON.stringify([...existing, preset]));
  } catch { /* localStorage unavailable */ }
}

export function loadBankPresets(): BankPreset[] {
  try { return JSON.parse(localStorage.getItem("csv-presets") || "[]"); }
  catch { return []; }
}
