// Perdura — Manual Entry connector (Stage 7.3). A generic journal-entry form:
// pick a date + accounts, enter balanced debits/credits, and post to
// gl_transactions (source_system='manual_entry') via the manual-journal-entry
// edge function. Includes a bulk CSV-paste mode and a history list with
// edit (re-post same ref) and soft-delete (post a reversing entry).
//
// Generic — no industry/customer-conditional logic. Route: /<tenant>/manual-entry.

(function () {
  const R = window.React;
  if (!R) return;
  const { useState, useEffect, useCallback, useMemo } = R;
  const db = () => window.supabaseClient;
  const h = R.createElement;

  function money(n) { return (Number(n) || 0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }); }
  function todayISO() { try { return new Date().toISOString().slice(0, 10); } catch (_e) { return ""; } }
  function emptyLine() { return { account_no: "", debit: "", credit: "", memo: "" }; }
  function freshForm() { return { manual_ref: null, date: todayISO(), description: "", reference_number: "", lines: [emptyLine(), emptyLine()] }; }
  function num(v) { if (v === "" || v == null) return 0; const n = Number(String(v).replace(/[,$\s]/g, "")); return Number.isFinite(n) ? n : 0; }

  function StatusPill({ status }) {
    const reversed = status === "reversed";
    const color = reversed ? "#b7791f" : "#10b981";
    return h("span", { style: { fontSize: 10.5, fontWeight: 700, textTransform: "uppercase", letterSpacing: 0.3, color, background: color + "1a", padding: "2px 8px", borderRadius: 999 } }, status || "posted");
  }

  // ── CSV parse for bulk mode ────────────────────────────────────────────────
  // Columns (header row required): entry_ref,date,account,debit,credit,description,memo
  // Rows sharing entry_ref aggregate into one journal entry; date/description
  // come from the first row of each group. Returns { entries, error }.
  function parseBulkCsv(text) {
    const lines = String(text || "").split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
    if (lines.length < 2) return { entries: [], error: "Need a header row plus at least one data row." };
    const header = lines[0].split(",").map((c) => c.trim().toLowerCase());
    const idx = (name) => header.indexOf(name);
    const ci = { ref: idx("entry_ref"), date: idx("date"), account: idx("account"), debit: idx("debit"), credit: idx("credit"), desc: idx("description"), memo: idx("memo") };
    if (ci.ref < 0 || ci.date < 0 || ci.account < 0 || ci.debit < 0 || ci.credit < 0) {
      return { entries: [], error: "Header must include: entry_ref, date, account, debit, credit (description, memo optional)." };
    }
    const groups = new Map();
    for (let r = 1; r < lines.length; r++) {
      const cells = lines[r].split(",").map((c) => c.trim());
      const ref = cells[ci.ref];
      if (!ref) return { entries: [], error: `Row ${r + 1}: missing entry_ref.` };
      if (!groups.has(ref)) {
        groups.set(ref, { manual_ref: ref, date: cells[ci.date], description: ci.desc >= 0 ? cells[ci.desc] : "", reference_number: "", lines: [] });
      }
      groups.get(ref).lines.push({ account_no: cells[ci.account], debit: cells[ci.debit], credit: cells[ci.credit], memo: ci.memo >= 0 ? cells[ci.memo] : "" });
    }
    return { entries: [...groups.values()], error: null };
  }

  function ManualEntryPage(props) {
    const companyId = props.scopedCompanyId || (props.companyProfile && props.companyProfile.id);
    const [accounts, setAccounts] = useState([]);
    const [history, setHistory] = useState(null);
    const [form, setForm] = useState(freshForm);
    const [mode, setMode] = useState("single");      // single | bulk
    const [bulkText, setBulkText] = useState("");
    const [busy, setBusy] = useState(false);
    const [msg, setMsg] = useState(null);            // { kind: 'ok'|'err', text }

    const loadAccounts = useCallback(async () => {
      if (!companyId) return;
      try {
        const { data } = await db().from("account_master").select("account_no, account_name, account_type").eq("company_id", companyId).order("account_no");
        setAccounts(data || []);
      } catch (_e) { setAccounts([]); }
    }, [companyId]);

    const loadHistory = useCallback(async () => {
      if (!companyId) return;
      try {
        const { data } = await db().from("manual_journal_entries")
          .select("id, manual_ref, entry_date, description, reference_number, status, reversal_of, line_count, total_debit, created_at")
          .eq("company_id", companyId).order("created_at", { ascending: false }).limit(100);
        setHistory(data || []);
      } catch (_e) { setHistory([]); }
    }, [companyId]);

    useEffect(() => { loadAccounts(); loadHistory(); }, [loadAccounts, loadHistory]);

    const totals = useMemo(() => {
      let dr = 0, cr = 0;
      for (const ln of form.lines) { dr += num(ln.debit); cr += num(ln.credit); }
      return { dr, cr, balanced: Math.abs(dr - cr) < 0.005 && (dr > 0 || cr > 0) };
    }, [form.lines]);

    function setLine(i, patch) {
      setForm((f) => ({ ...f, lines: f.lines.map((ln, j) => (j === i ? { ...ln, ...patch } : ln)) }));
    }
    function addLine() { setForm((f) => ({ ...f, lines: f.lines.concat([emptyLine()]) })); }
    function removeLine(i) { setForm((f) => ({ ...f, lines: f.lines.length > 1 ? f.lines.filter((_, j) => j !== i) : f.lines })); }

    async function submitSingle() {
      if (!companyId) return;
      if (!totals.balanced) { setMsg({ kind: "err", text: "Debits must equal credits (and be non-zero) before saving." }); return; }
      setBusy(true); setMsg(null);
      try {
        const payload = {
          company_id: companyId, action: "post",
          entry: {
            manual_ref: form.manual_ref || undefined,
            date: form.date, description: form.description || null, reference_number: form.reference_number || null,
            lines: form.lines.filter((ln) => ln.account_no && (num(ln.debit) || num(ln.credit)))
              .map((ln) => ({ account_no: ln.account_no, debit: num(ln.debit), credit: num(ln.credit), memo: ln.memo || null })),
          },
        };
        const { data, error } = await db().functions.invoke("manual-journal-entry", { body: payload });
        if (error || (data && data.status && data.status !== "success")) {
          setMsg({ kind: "err", text: (data && (data.error || JSON.stringify(data.errors))) || (error && error.message) || "Save failed." });
        } else {
          setMsg({ kind: "ok", text: `Entry ${form.manual_ref ? "updated" : "posted"} (${(data && data.results && data.results[0] && data.results[0].manual_ref) || ""}).` });
          setForm(freshForm()); loadHistory();
        }
      } catch (e) { setMsg({ kind: "err", text: String(e) }); }
      setBusy(false);
    }

    async function submitBulk() {
      if (!companyId) return;
      const { entries, error } = parseBulkCsv(bulkText);
      if (error) { setMsg({ kind: "err", text: error }); return; }
      setBusy(true); setMsg(null);
      try {
        const { data, error: invErr } = await db().functions.invoke("manual-journal-entry", { body: { company_id: companyId, action: "post", entries } });
        if (invErr || (data && data.status === "validation_failed")) {
          const detail = data && data.errors ? data.errors.map((e) => `#${e.index + 1}: ${e.error}`).join("; ") : (invErr && invErr.message);
          setMsg({ kind: "err", text: `Batch rejected — ${detail}` });
        } else {
          setMsg({ kind: "ok", text: `Posted ${data.posted} of ${entries.length} entries${data.failed ? `, ${data.failed} failed` : ""}.` });
          setBulkText(""); loadHistory();
        }
      } catch (e) { setMsg({ kind: "err", text: String(e) }); }
      setBusy(false);
    }

    async function startEdit(entry) {
      // Reload the entry's lines from gl_transactions and load into the form.
      try {
        const { data } = await db().from("gl_transactions")
          .select("account_no, debit, credit, memo, reference").eq("company_id", companyId).like("reference", `manual:${entry.manual_ref}:%`).order("reference");
        const lines = (data || []).map((r) => ({ account_no: r.account_no || "", debit: r.debit || "", credit: r.credit || "", memo: r.memo || "" }));
        setForm({ manual_ref: entry.manual_ref, date: entry.entry_date || todayISO(), description: entry.description || "", reference_number: entry.reference_number || "", lines: lines.length ? lines : [emptyLine(), emptyLine()] });
        setMode("single"); setMsg({ kind: "ok", text: `Editing ${entry.manual_ref} — saving re-posts and replaces its lines.` });
        try { window.scrollTo({ top: 0, behavior: "smooth" }); } catch (_e) {}
      } catch (e) { setMsg({ kind: "err", text: String(e) }); }
    }

    async function reverse(entry) {
      if (!companyId) return;
      if (typeof window !== "undefined" && window.confirm && !window.confirm(`Reverse entry ${entry.manual_ref}? This posts a balanced reversing entry (it is not hard-deleted).`)) return;
      setBusy(true); setMsg(null);
      try {
        const { data, error } = await db().functions.invoke("manual-journal-entry", { body: { company_id: companyId, action: "reverse", manual_ref: entry.manual_ref } });
        if (error || (data && data.status === "error")) setMsg({ kind: "err", text: (data && data.error) || (error && error.message) || "Reverse failed." });
        else { setMsg({ kind: "ok", text: `Reversed — posted ${data.reversal_ref}.` }); loadHistory(); }
      } catch (e) { setMsg({ kind: "err", text: String(e) }); }
      setBusy(false);
    }

    if (!companyId) return h("div", { className: "pc-page" }, h("div", { style: { padding: 40, color: "var(--text-3)" } }, "Select a company to enter journal entries."));

    const inputStyle = { width: "100%", padding: "7px 9px", border: "1px solid var(--border, #d8dee9)", borderRadius: 6, fontSize: 13, boxSizing: "border-box" };
    const acctList = "pc-acct-options";

    return h("div", { className: "pc-page" },
      h("div", { style: { marginBottom: 16 } },
        h("h1", { style: { fontSize: 22, fontWeight: 800, margin: 0 } }, "Manual Entry"),
        h("div", { style: { fontSize: 13, color: "var(--text-3)", marginTop: 4 } },
          "Record journal entries by hand or paste them in bulk. Debits must equal credits; entries post to your general ledger and flow straight into the dashboards.")),

      // shared account datalist for autocomplete
      h("datalist", { id: acctList }, accounts.map((a) => h("option", { key: a.account_no, value: a.account_no }, `${a.account_no} — ${a.account_name || ""}`))),

      msg ? h("div", { style: { margin: "0 0 14px", padding: "9px 12px", borderRadius: 7, fontSize: 12.5, fontWeight: 600, color: msg.kind === "ok" ? "#10b981" : "#c0392b", background: (msg.kind === "ok" ? "#10b981" : "#c0392b") + "14" } }, msg.text) : null,

      // mode toggle
      h("div", { style: { display: "flex", gap: 8, marginBottom: 12 } },
        ["single", "bulk"].map((m) => h("button", { key: m, className: "pc-btn " + (mode === m ? "pc-btn-primary" : "pc-btn-ghost"), style: { fontSize: 12.5 }, onClick: () => { setMode(m); setMsg(null); } }, m === "single" ? "Single entry" : "Bulk paste (CSV)"))),

      mode === "single"
        ? h("div", { className: "pc-card", style: { padding: 18, marginBottom: 20 } },
            h("div", { style: { display: "grid", gridTemplateColumns: "160px 1fr 200px", gap: 12, marginBottom: 14 } },
              h("label", null, h("div", { style: { fontSize: 11, fontWeight: 700, color: "var(--text-3)", marginBottom: 4 } }, "Date"), h("input", { type: "date", style: inputStyle, value: form.date, onChange: (e) => setForm((f) => ({ ...f, date: e.target.value })) })),
              h("label", null, h("div", { style: { fontSize: 11, fontWeight: 700, color: "var(--text-3)", marginBottom: 4 } }, "Description"), h("input", { type: "text", style: inputStyle, placeholder: "e.g. Accrue June rent", value: form.description, onChange: (e) => setForm((f) => ({ ...f, description: e.target.value })) })),
              h("label", null, h("div", { style: { fontSize: 11, fontWeight: 700, color: "var(--text-3)", marginBottom: 4 } }, "Reference # (optional)"), h("input", { type: "text", style: inputStyle, value: form.reference_number, onChange: (e) => setForm((f) => ({ ...f, reference_number: e.target.value })) }))),

            h("table", { style: { width: "100%", borderCollapse: "collapse", fontSize: 12.5 } },
              h("thead", null, h("tr", null, ["Account", "Debit", "Credit", "Line memo", ""].map((c, i) => h("th", { key: i, style: { textAlign: i === 1 || i === 2 ? "right" : "left", padding: "4px 6px", fontSize: 11, color: "var(--text-3)", fontWeight: 700 } }, c)))),
              h("tbody", null, form.lines.map((ln, i) => h("tr", { key: i },
                h("td", { style: { padding: "3px 6px", width: "30%" } }, h("input", { list: acctList, style: inputStyle, placeholder: "account no.", value: ln.account_no, onChange: (e) => setLine(i, { account_no: e.target.value }) })),
                h("td", { style: { padding: "3px 6px", width: "18%" } }, h("input", { type: "number", step: "0.01", style: { ...inputStyle, textAlign: "right" }, value: ln.debit, onChange: (e) => setLine(i, { debit: e.target.value, credit: e.target.value ? "" : ln.credit }) })),
                h("td", { style: { padding: "3px 6px", width: "18%" } }, h("input", { type: "number", step: "0.01", style: { ...inputStyle, textAlign: "right" }, value: ln.credit, onChange: (e) => setLine(i, { credit: e.target.value, debit: e.target.value ? "" : ln.debit }) })),
                h("td", { style: { padding: "3px 6px" } }, h("input", { type: "text", style: inputStyle, value: ln.memo, onChange: (e) => setLine(i, { memo: e.target.value }) })),
                h("td", { style: { padding: "3px 6px", width: 28 } }, h("button", { className: "pc-btn pc-btn-ghost", style: { fontSize: 12, padding: "4px 8px" }, title: "Remove line", onClick: () => removeLine(i) }, "×"))))) ),

            h("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginTop: 12, flexWrap: "wrap", gap: 10 } },
              h("button", { className: "pc-btn pc-btn-ghost", style: { fontSize: 12.5 }, onClick: addLine }, "+ Add line"),
              h("div", { style: { display: "flex", alignItems: "center", gap: 18 } },
                h("div", { style: { fontFamily: "ui-monospace,monospace", fontSize: 12.5 } },
                  h("span", { style: { color: "var(--text-3)" } }, "Debits "), money(totals.dr), h("span", { style: { color: "var(--text-3)", margin: "0 8px" } }, "Credits "), money(totals.cr)),
                h("span", { style: { fontSize: 11.5, fontWeight: 700, color: totals.balanced ? "#10b981" : "#c0392b" } }, totals.balanced ? "Balanced ✓" : `Out of balance ${money(totals.dr - totals.cr)}`),
                form.manual_ref ? h("button", { className: "pc-btn pc-btn-ghost", style: { fontSize: 12.5 }, onClick: () => { setForm(freshForm()); setMsg(null); } }, "Cancel edit") : null,
                h("button", { className: "pc-btn pc-btn-primary", style: { fontSize: 13 }, disabled: busy || !totals.balanced, onClick: submitSingle }, busy ? "Saving…" : (form.manual_ref ? "Update entry" : "Post entry")))))
        : h("div", { className: "pc-card", style: { padding: 18, marginBottom: 20 } },
            h("div", { style: { fontSize: 12.5, color: "var(--text-3)", marginBottom: 8 } },
              "Paste CSV with a header row. Columns: ", h("code", null, "entry_ref,date,account,debit,credit,description,memo"), ". Rows sharing an ", h("code", null, "entry_ref"), " group into one balanced entry."),
            h("textarea", { style: { width: "100%", minHeight: 180, fontFamily: "ui-monospace,monospace", fontSize: 12, padding: 10, border: "1px solid var(--border, #d8dee9)", borderRadius: 6, boxSizing: "border-box" }, placeholder: "entry_ref,date,account,debit,credit,description,memo\nJE-1001,2025-06-30,6000,1000,0,June rent,\nJE-1001,2025-06-30,1000,0,1000,June rent,", value: bulkText, onChange: (e) => setBulkText(e.target.value) }),
            h("div", { style: { display: "flex", justifyContent: "flex-end", marginTop: 12 } },
              h("button", { className: "pc-btn pc-btn-primary", style: { fontSize: 13 }, disabled: busy || !bulkText.trim(), onClick: submitBulk }, busy ? "Posting…" : "Validate & post batch"))),

      // ── history ───────────────────────────────────────────────────────────
      h("div", { className: "pc-card", style: { padding: 0 } },
        h("div", { className: "pc-card-hd" }, h("div", { className: "pc-card-title" }, "Manual entries")),
        history == null
          ? h("div", { style: { padding: 24, color: "var(--text-3)" } }, "Loading…")
          : (history.length === 0
            ? h("div", { style: { padding: 24, color: "var(--text-3)", fontSize: 13 } }, "No manual entries yet.")
            : h("div", { className: "pc-adm-table-wrap" },
              h("table", { className: "pc-adm-table" },
                h("thead", null, h("tr", null, ["Date", "Ref", "Description", "Lines", "Debits", "Status", ""].map((c, i) => h("th", { key: i }, c)))),
                h("tbody", null, history.map((r) => h("tr", { key: r.id },
                  h("td", { style: { fontSize: 12, color: "var(--text-3)", fontFamily: "ui-monospace,monospace" } }, r.entry_date || "—"),
                  h("td", { style: { fontFamily: "ui-monospace,monospace", fontSize: 12 } }, r.manual_ref),
                  h("td", { style: { fontWeight: 600 } }, r.description || (r.reversal_of ? "(reversal)" : "—")),
                  h("td", { style: { fontFamily: "ui-monospace,monospace", fontSize: 12.5 } }, r.line_count),
                  h("td", { style: { fontFamily: "ui-monospace,monospace", fontSize: 12.5 } }, money(r.total_debit)),
                  h("td", null, h(StatusPill, { status: r.status })),
                  h("td", { style: { whiteSpace: "nowrap" } },
                    r.status !== "reversed" && !r.reversal_of
                      ? R.createElement(R.Fragment, null,
                          h("button", { className: "pc-btn pc-btn-ghost", style: { fontSize: 11.5, padding: "3px 8px", marginRight: 6 }, onClick: () => startEdit(r) }, "Edit"),
                          h("button", { className: "pc-btn pc-btn-ghost", style: { fontSize: 11.5, padding: "3px 8px", color: "#c0392b" }, onClick: () => reverse(r) }, "Delete"))
                      : h("span", { style: { fontSize: 11, color: "var(--text-3)" } }, "—")))))))))
    );
  }

  window.ManualEntryPage = ManualEntryPage;
})();
