// ═══════════════════════════════════════════════════════════════
//  bank.jsx — Банка: импорт на извлечение от ДСК (.xls = HTML таблица
//  или .csv). Парсва се на клиента — без ключове, без трети страни.
//  Кредит → приход (свързваем с фактура) · Дебит → разход / теглене.
//  Преглед + потвърждение. Дедуп: bank_tx_id + защита срещу ръчно
//  въведени (възможен дубликат).
// ═══════════════════════════════════════════════════════════════
const { useState: useStateB, useMemo: useMemoB } = React;

// "07.04.2026" → "2026-04-07"
const dmyToISO = (s) => {
  const m = (s || "").match(/(\d{2})\.(\d{2})\.(\d{4})/);
  return m ? `${m[3]}-${m[2]}-${m[1]}` : "";
};
const parseAmt = (s) => {
  if (!s) return 0;
  const c = String(s).replace(/\s/g, "").replace(",", ".").replace(/[^0-9.\-]/g, "");
  const n = parseFloat(c);
  return isNaN(n) ? 0 : n;
};
const daysBetween = (a, b) => { if (!a || !b) return 999; return Math.round((new Date(a) - new Date(b)) / 86400000); };

// Parse a DSK statement (HTML-table .xls or CSV) into normalized rows
function parseStatement(text) {
  const out = [];
  let rows = [];
  if (/<\s*table/i.test(text)) {
    const doc = new DOMParser().parseFromString(text, "text/html");
    rows = [...doc.querySelectorAll("tr")].map(tr => [...tr.querySelectorAll("td,th")].map(td => (td.textContent || "").replace(/\s+/g, " ").trim()));
  } else {
    const lines = text.split(/\r?\n/).filter(l => l.trim());
    const delim = (lines[0] && lines[0].indexOf("\t") >= 0) ? "\t" : ((lines[0] || "").indexOf(";") >= 0 ? ";" : ",");
    rows = lines.map(l => l.split(delim).map(c => c.replace(/^"|"$/g, "").trim()));
  }
  let hi = rows.findIndex(r => r.some(c => /Счетоводна дата/i.test(c)) && r.some(c => /Кредит/i.test(c)));
  let idx = { date: 0, osn: 2, party: 3, ref: 6, debit: 9, credit: 10 };
  if (hi >= 0) {
    const h = rows[hi];
    const find = (re, d) => { const i = h.findIndex(c => re.test(c)); return i >= 0 ? i : d; };
    idx = { date: find(/Счетоводна дата/i, 0), osn: find(/Основание/i, 2), party: find(/Наредител|Получател/i, 3), ref: find(/референция/i, 6), debit: find(/Дебит/i, 9), credit: find(/Кредит/i, 10) };
  }
  const start = hi >= 0 ? hi + 1 : 0;
  for (let i = start; i < rows.length; i++) {
    const r = rows[i];
    if (!r || r.length < Math.max(idx.credit, idx.debit) + 1) continue;
    if (/^Общо/i.test(r[idx.date] || "") || /^Общо/i.test(r[0] || "")) break;
    const date = dmyToISO(r[idx.date]);
    if (!date) continue;
    const osn = r[idx.osn] || "", party = r[idx.party] || "", ref = r[idx.ref] || "";
    const debit = parseAmt(r[idx.debit]), credit = parseAmt(r[idx.credit]);
    const time = r[r.length - 1] || "";
    const mk = (dir, amount) => {
      const target = dir === "in" ? "income" : (/дивидент/i.test(osn + " " + party) ? "withdrawal" : "expense");
      return { tx_id: `${date}|${dir}|${amount}|${ref}|${time}`, date, amount, currency: "EUR", name: party || osn || "—", info: osn, direction: dir, target };
    };
    if (credit > 0) out.push(mk("in", credit));
    else if (debit > 0) out.push(mk("out", debit));
  }
  return out;
}

function BankView({ T, invoices, incomes, expenses, withdrawals, onSaveIncome, onSaveExpense, onSaveWithdrawal }) {
  const [items, setItems] = useStateB(null);
  const [fileNames, setFileNames] = useStateB([]);
  const [msg, setMsg] = useStateB("");

  const seen = useMemoB(() => new Set([
    ...(incomes || []).map(e => e.bankTxId).filter(Boolean),
    ...(expenses || []).map(e => e.bank_tx_id).filter(Boolean),
    ...(withdrawals || []).map(w => w.bank_tx_id).filter(Boolean),
  ]), [incomes, expenses, withdrawals]);

  // unpaid invoices for the link dropdown
  const unpaidInvoices = useMemoB(() => (invoices || [])
    .filter(i => i.docType === "invoice" && invoiceStatus(i, incomes) !== "paid")
    .map(i => ({ inv: i, rem: toEUR(i.total, i.currency) - invoicePaidGross(i, incomes) })), [invoices, incomes]);
  const invOptions = useMemoB(() => [{ v: "", l: "— Без фактура —" },
    ...unpaidInvoices.map(({ inv, rem }) => ({ v: inv.num, l: `№ ${inv.num} · ${inv.client?.name || inv.client?.nameEn} · ${eur(rem)}` }))], [unpaidInvoices]);
  const matchInvoiceNum = (amt) => { const m = unpaidInvoices.find(x => Math.abs(x.rem - amt) <= 1); return m ? m.inv.num : ""; };

  // is this row likely already entered manually (no bank_tx_id, same amount/date)?
  const likelyDup = (tx) => {
    const amt = Math.abs(tx.amount);
    if (tx.direction === "in") return (incomes || []).some(e => !e.bankTxId && Math.abs((e.amount || 0) - amt) <= 1 && Math.abs(daysBetween(e.date, tx.date)) <= 5);
    return (expenses || []).some(e => !e.bank_tx_id && Math.abs((e.amount || 0) - amt) <= 1 && Math.abs(daysBetween(e.date, tx.date)) <= 5)
      || (withdrawals || []).some(w => !w.bank_tx_id && Math.abs((w.amount || 0) - amt) <= 1 && Math.abs(daysBetween(w.date, tx.date)) <= 5);
  };

  const handleFiles = (e) => {
    const files = [...(e.target.files || [])];
    if (!files.length) return;
    setMsg("");
    Promise.all(files.map(f => f.text().then(t => parseStatement(t)))).then(lists => {
      const all = [].concat(...lists);
      const byId = {};
      all.forEach(t => { if (!seen.has(t.tx_id)) byId[t.tx_id] = t; });
      const fresh = Object.values(byId)
        .map(t => ({ ...t, invoiceNum: t.direction === "in" ? matchInvoiceNum(t.amount) : "", dup: likelyDup(t) }))
        .sort((a, b) => (b.date || "").localeCompare(a.date || ""));
      setItems(fresh);
      setFileNames(files.map(f => f.name));
      if (fresh.length === 0) setMsg("Няма нови транзакции (всичко вече е внесено или файлът е празен).");
    }).catch(err => { console.error("[bank parse]", err); setMsg("Грешка при четене на файла: " + err.message); });
    e.target.value = "";
  };

  const setTarget = (id, target) => setItems(prev => prev.map(t => t.tx_id === id ? { ...t, target } : t));
  const setInvoice = (id, invoiceNum) => setItems(prev => prev.map(t => t.tx_id === id ? { ...t, invoiceNum } : t));

  const importItem = (tx) => {
    if (tx.target === "income") {
      const inv = (invoices || []).find(i => i.num === tx.invoiceNum);
      const payer = inv ? (inv.client?.name || inv.client?.nameEn || tx.name) : tx.name;
      const note = inv ? (tx.info ? `${tx.name} · ${tx.info}` : tx.name) : (tx.info || "");
      onSaveIncome({ source: inv ? "invoice" : "other", invoiceNum: inv ? inv.num : "", date: tx.date, payer, project: inv ? (inv.project || "") : "", amount: tx.amount, currency: tx.currency, vat: 0, account: "dsk", note, bankTxId: tx.tx_id });
    } else if (tx.target === "withdrawal") {
      onSaveWithdrawal({ date: tx.date, amount: Math.abs(tx.amount), currency: tx.currency, note: tx.name || tx.info || "Теглене", bank_tx_id: tx.tx_id });
    } else {
      onSaveExpense({ date: tx.date, vendor: tx.name, category: "", description: tx.info || "", amount: Math.abs(tx.amount), currency: tx.currency, vat: 0, project: null, bank_tx_id: tx.tx_id });
    }
    setItems(prev => prev.filter(x => x.tx_id !== tx.tx_id));
  };
  const skip = (tx) => setItems(prev => prev.filter(x => x.tx_id !== tx.tx_id));
  const importAll = () => { (items || []).filter(t => !t.dup).slice().forEach(importItem); };

  const outOpts = [{ v: "expense", l: "Разход" }, { v: "withdrawal", l: "Теглене" }];
  const inCount = (items || []).filter(t => t.direction === "in").length;
  const outCount = (items || []).filter(t => t.direction === "out").length;
  const dupCount = (items || []).filter(t => t.dup).length;

  return (
    <div style={{ maxWidth: 860, margin: "0 auto", padding: "28px 28px 60px" }}>
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 20, gap: 10, flexWrap: "wrap" }}>
        <h2 style={{ fontSize: 18, fontWeight: 600, letterSpacing: "-0.02em", margin: 0 }}>Банка</h2>
        <label style={{ cursor: "pointer" }}>
          <span style={{ display: "inline-flex", alignItems: "center", gap: 7, padding: "9px 16px", borderRadius: 7, fontSize: 12.5, fontWeight: 600, fontFamily: MONO, background: T.blue, color: "#fff" }}>
            <Icon name="download" size={15} />Качи извлечение
          </span>
          <input type="file" accept=".xls,.csv,.html,.htm,.xlsx" multiple onChange={handleFiles} style={{ display: "none" }} />
        </label>
      </div>

      <Card T={T} title="Импорт от ДСК" icon="bank">
        <div style={{ fontSize: 12, color: T.sub, lineHeight: 1.65 }}>
          Изтегли извлечение от ДСК онлайн банкиране (.xls) и го качи — може и няколко наведнъж. Входящите стават <b style={{ color: T.sage }}>приходи</b> (свържи ги с фактура), изходящите — <b style={{ color: T.danger }}>разход или теглене</b>. Нищо не се внася без потвърждение; вече внесените се пропускат.
        </div>
        {fileNames.length > 0 && <div style={{ fontSize: 11, color: T.faint, marginTop: 10, fontFamily: MONO }}>Заредени: {fileNames.join(", ")}</div>}
      </Card>

      {msg && <div style={{ padding: "10px 14px", background: T.blueSoft, border: `1px solid ${T.blue}33`, borderRadius: 9, fontSize: 12, color: T.ink, marginBottom: 14 }}>{msg}</div>}

      {items && items.length > 0 && (
        <Card T={T} title="За внасяне" icon="wallet"
          right={<div style={{ display: "flex", alignItems: "center", gap: 10 }}><span style={{ fontSize: 11, color: T.faint }}>{inCount} прих. · {outCount} разх.{dupCount ? ` · ${dupCount} дубл.` : ""}</span><Button T={T} kind="ghost" size="sm" icon="check" onClick={importAll}>Внеси всички</Button></div>}>
          <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
            {items.map(tx => (
              <div key={tx.tx_id} style={{ display: "flex", alignItems: "center", gap: 12, padding: "11px 14px", background: tx.dup ? `${"#C9912B"}0D` : T.field, border: `1px solid ${tx.dup ? "#C9912B55" : T.line}`, borderRadius: 9, flexWrap: "wrap" }}>
                <div style={{ fontSize: 11, color: T.faint, fontFamily: MONO, minWidth: 72 }}>{fmtDate(tx.date)}</div>
                <span style={{ fontSize: 9.5, fontWeight: 700, textTransform: "uppercase", color: tx.direction === "in" ? T.sage : T.danger, background: `${tx.direction === "in" ? T.sage : T.danger}1A`, padding: "3px 8px", borderRadius: 5 }}>{tx.direction === "in" ? "приход" : "изход"}</span>
                <div style={{ flex: 1, minWidth: 150 }}>
                  <div style={{ fontSize: 12.5, fontWeight: 600, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{tx.name}{tx.dup && <span style={{ marginLeft: 8, fontSize: 9.5, fontWeight: 700, color: "#C9912B", background: "#C9912B22", padding: "2px 7px", borderRadius: 9 }}>възможен дубликат</span>}</div>
                  <div style={{ fontSize: 10.5, color: T.faint, whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }}>{tx.info}</div>
                </div>
                {tx.direction === "in"
                  ? <div style={{ minWidth: 200 }}><Select value={tx.invoiceNum} onChange={v => setInvoice(tx.tx_id, v)} options={invOptions} T={T} /></div>
                  : <div style={{ minWidth: 120 }}><Select value={tx.target} onChange={v => setTarget(tx.tx_id, v)} options={outOpts} T={T} /></div>}
                <div style={{ fontFamily: MONO, fontWeight: 700, fontSize: 13, color: tx.direction === "in" ? T.sage : T.danger, minWidth: 92, textAlign: "right" }}>{tx.direction === "in" ? "+" : "−"}{fmtNum(Math.abs(tx.amount))} {tx.currency}</div>
                <Button T={T} kind="primary" size="sm" icon="check" onClick={() => importItem(tx)}>Внеси</Button>
                <button title="Пропусни" onClick={() => skip(tx)} style={{ background: "none", border: "none", color: T.faint, cursor: "pointer", padding: 4 }}><Icon name="x" size={15} /></button>
              </div>
            ))}
          </div>
        </Card>
      )}
    </div>
  );
}

Object.assign(window, { BankView });
