// ── Statement rules: telling the CRM what a bank line really is ─────────────
// A rule (src/statement-rules.js) says every bank line containing a phrase is sales, a funding from a named
// lender, a transfer from the merchant's own account, a payment to a named lender, or never a lender payment.
// It applies to every statement already read and every one read later; figures are worked out again from the
// saved rows (no new reading, no cost). Opened from a statement's rows ("Fix"), a position ("Not a lender
// payment"), a funding ("Not a funding") or the "Statement rules" link.
//
// The sheets render in their own small React root, so any screen can open one with openRuleSheet(...) or
// openRulesSheet(). When a rule is added or removed, "ef:statements-changed" is sent and the statements on
// screen are loaded again (statements.jsx useStatements).

const RULE_KIND_TEXT = {
  sales: { label: "Sales", say: "Money in from customers: counted as sales" },
  funding: { label: "Funding", say: "Money in from a lender: not sales, listed as a funding" },
  transfer: { label: "Own transfer", say: "Money in from the merchant's own account or a loan sweep: not sales" },
  position: { label: "Lender payment", say: "A payment to a lender: a position when it repeats" },
  "not-position": { label: "Not a lender payment", say: "Rent, vendors, payroll, a car or equipment loan…: never a position" },
  "debt-settlement": { label: "Debt settlement", say: "A payment to a debt settlement program: the file gets the DEBT SETTLEMENT label (not fundable)" },
};
const RULE_KINDS_FOR = { credit: ["sales", "funding", "transfer"], debit: ["position", "not-position", "debt-settlement"] };
// The same as the server's phrase matching (statements.js phraseMatches / suggestPhrase): every word of 3 or more
// letters in the phrase must appear in the line's letters run together; numbers and codes are ignored.
const ruleWords = (phrase) => String(phrase || "").toLowerCase().replace(/[^a-z ]+/g, " ").split(" ").filter(w => w.length >= 3);
const ruleMatches = (description, phrase) => {
  const w = ruleWords(phrase), text = String(description || "").toLowerCase().replace(/[^a-z]+/g, "");
  return w.length > 0 && w.some(x => x.length >= 4) && w.every(x => text.includes(x));
};
const RULE_NOISE_WORDS = new Set(["the", "and", "for", "inc", "llc", "corp", "ltd", "pmt", "pymt", "ppd", "ccd", "web", "des", "indn", "orig", "deposit", "dep", "wire", "incoming", "fedwire", "via", "bank", "originator", "name", "entry", "descr", "desc", "date", "customer", "type", "disburse", "credit", "debit", "recd", "real", "time", "aba", "contr", "bnk", "usd", "acct", "ach", "payment", "transfer", "today", "ind", "co", "id", "trn", "ref", "online", "mobile", "from", "card", "purchase", "pos"]);
const ruleSuggest = (description) => [...new Set(String(description || "").toLowerCase().replace(/[^a-z ]+/g, " ").split(" ").filter(w => w.length >= 3 && !RULE_NOISE_WORDS.has(w)))].slice(0, 3).join(" ");

// The rules, loaded once and shared by every screen; loaded again after a change.
const ruleStore = { data: null, loading: null, subs: new Set() };
function loadStatementRules(force) {
  if (ruleStore.loading && !force) return ruleStore.loading;
  ruleStore.loading = API.statementRules().then(d => { ruleStore.data = d; ruleStore.subs.forEach(f => f(d)); return d; }, err => { ruleStore.loading = null; throw err; });
  return ruleStore.loading;
}
function useStatementRules() {
  const [data, setData] = useState(ruleStore.data);
  useEffect(() => {
    ruleStore.subs.add(setData);
    if (!ruleStore.data) loadStatementRules().catch(() => {});
    return () => { ruleStore.subs.delete(setData); };
  }, []);
  return data;
}
const ruleChanged = () => { loadStatementRules(true).catch(() => {}); window.dispatchEvent(new CustomEvent("ef:statements-changed")); };

// One rule: what this bank line (and every line like it) is.
// `line`: { description, direction ("credit" | "debit"), amount?, date?, kind?, lender? }
function RuleSheet({ line, onClose }) {
  const kinds = RULE_KINDS_FOR[line.direction] || RULE_KINDS_FOR.credit;
  const [kind, setKind] = useState(kinds.includes(line.kind) ? line.kind : kinds[0]);
  const [phrase, setPhrase] = useState(ruleSuggest(line.description));
  const [lender, setLender] = useState(line.lender || "");
  const [lenders, setLenders] = useState([]);
  const [done, setDone] = useState(null);
  const { busy, error, setError, run } = useSave(out => setDone(out));
  useEffect(() => { API.lenders().then(l => setLenders(l.map(x => x.name).filter(Boolean).sort()), () => {}); }, []);
  const needsLender = kind === "funding" || kind === "position";
  const matches = ruleMatches(line.description, phrase);
  const save = () => {
    if (!ruleWords(phrase).some(w => w.length >= 4)) { setError("The phrase needs at least one word of 4 or more letters"); return; }
    if (!matches) { setError("The phrase has to match this line: every word in it must appear in the line"); return; }
    if (needsLender && !lender.trim()) { setError("Name the lender"); return; }
    run(async () => { const out = await API.addStatementRule({ phrase, kind, lender: needsLender ? lender.trim() : "", sample: line.description }); ruleChanged(); return out; });
  };
  if (done) {
    return (
      <Sheet eyebrow="Statement rule" title="Rule saved" onClose={onClose}>
        <div className="form-section">
        <p style={{ marginTop: 0 }}>Every bank line containing <b>{done.rule.phrase}</b> is now <b>{RULE_KIND_TEXT[done.rule.kind].label.toLowerCase()}</b>{done.rule.lender ? <> ({done.rule.lender})</> : null}, on every statement in the CRM and every one read from now on.</p>
        <p className="dim" style={{ fontSize: 13 }}>{done.queued ? `${done.queued} statements are being worked out again; their figures and the leads' leverage update over the next few minutes. Nothing is read again, so it costs nothing.` : "The figures update in a moment."}</p>
        </div>
      </Sheet>
    );
  }
  return (
    <Sheet eyebrow="Statement rule" title="What is this line?" onClose={onClose} onSubmit={save} submitLabel="Save rule" busyLabel="Saving…" busy={busy} error={error}>
      <div className="form-section">
      <div className="card" style={{ padding: "10px 12px", marginBottom: 14, fontSize: 13 }}>
        <div className="tnum" style={{ fontWeight: 600 }}>{line.direction === "debit" ? "Money out" : "Money in"}{line.amount ? ` · ${fmt.money(line.amount)}` : ""}{line.date ? ` · ${fmt.day(line.date)}` : ""}</div>
        <div className="dim" style={{ wordBreak: "break-word" }}>{line.description}</div>
      </div>
      <div className="form-grid">
      <Field label="It is" full>
        <div role="radiogroup" aria-label="It is" style={{ display: "grid", gap: 6 }}>
          {kinds.map(k => (
            <label key={k} style={{ display: "flex", gap: 8, alignItems: "flex-start", cursor: "pointer", fontSize: 13.5 }}>
              <input type="radio" name="rule-kind" checked={kind === k} onChange={() => setKind(k)} style={{ marginTop: 3 }} />
              <span><b>{RULE_KIND_TEXT[k].label}</b><span className="dim"> · {RULE_KIND_TEXT[k].say}</span></span>
            </label>
          ))}
        </div>
      </Field>
      {needsLender ? (
        <Field label="Lender" full hint="As it should be listed; pick one of the office's lenders or type a name.">
          <input className="input" value={lender} onChange={e => setLender(e.target.value)} list="rule-lenders" autoComplete="off" />
          <datalist id="rule-lenders">{lenders.map(n => <option key={n} value={n} />)}</datalist>
        </Field>
      ) : null}
      <Field label="Bank lines containing" full hint={matches ? "Matches this line. Keep it specific: it applies to every statement for every lead and deal." : "Every word must appear in the line above (numbers and codes are ignored)."}>
        <input className="input" value={phrase} onChange={e => setPhrase(e.target.value)} autoComplete="off" spellCheck={false} />
      </Field>
      </div>
      </div>
    </Sheet>
  );
}

// Every rule the office has made, and the built-in ones.
function RulesSheet({ onClose }) {
  const data = useStatementRules();
  const [busyId, setBusyId] = useState(null);
  const [error, setError] = useState("");
  const [showBuiltIn, setShowBuiltIn] = useState(false);
  useEffect(() => { loadStatementRules(true).catch(err => setError(err.message)); }, []);
  const remove = async (r) => {
    setBusyId(r.id); setError("");
    try { await API.deleteStatementRule(r.id); ruleChanged(); }
    catch (err) { setError(err.message); }
    setBusyId(null);
  };
  const rules = (data && data.rules) || [];
  return (
    <Sheet eyebrow="Bank statements" title="Statement rules" onClose={onClose} error={error}>
      <div className="form-section">
      <p className="dim" style={{ marginTop: 0, fontSize: 13 }}>What the CRM counts a bank line as, decided once and applied to every statement, past and future. Add one with <b>Fix</b> on a statement's rows, <b>Not a funding</b> on a funding or <b>Not a lender payment</b> on a position.</p>
      {!data ? <div className="muted">Loading…</div> : rules.length ? (
        <div className="card card-flush">
          {rules.map(r => (
            <div className="row" key={r.id}>
              <div className="grow">
                <div className="title">{r.phrase}<span className="pill plain s-neutral">{(RULE_KIND_TEXT[r.kind] || {}).label || r.kind}</span>{r.lender ? <span className="dim" style={{ fontWeight: 400 }}>{r.lender}</span> : null}</div>
                {r.sample ? <div className="when" title={r.sample} style={{ wordBreak: "break-word" }}>{r.sample}</div> : null}
                <div className="when">{r.by ? `${r.by} · ` : ""}{fmt.day(r.at)} ’{String(r.at).slice(2, 4)}</div>
              </div>
              {data.canEdit ? <div className="row-actions"><button type="button" className="btn btn-sm" onClick={() => remove(r)} disabled={busyId === r.id}>{busyId === r.id ? "Removing…" : "Remove"}</button></div> : null}
            </div>
          ))}
        </div>
      ) : <div className="card"><div className="card-body dim" style={{ fontSize: 13 }}>No rules yet.</div></div>}
      {data ? (
        <div style={{ marginTop: 14 }}>
          <button type="button" className="link-btn" onClick={() => setShowBuiltIn(!showBuiltIn)} aria-expanded={showBuiltIn}>{showBuiltIn ? "Hide" : "Show"} the {data.builtIn.length} built-in rules</button>
          {showBuiltIn ? (
            <div className="dim" style={{ fontSize: 12.5, marginTop: 8 }}>
              <p style={{ margin: "0 0 6px" }}>Money in that only the reader called a funding, with nothing in its wording, the lender list or the known funders to back it, is counted as sales.</p>
              {data.builtIn.map((r, i) => <div key={i}><b>{r.phrase}</b> · {(RULE_KIND_TEXT[r.kind] || {}).label} · {r.note}</div>)}
            </div>
          ) : null}
        </div>
      ) : null}
      </div>
    </Sheet>
  );
}

// The host: its own root at the end of the page, showing whichever sheet was asked for.
function RuleHost() {
  const [open, setOpen] = useState(null);
  useEffect(() => {
    const on = (e) => setOpen(e.detail || null);
    window.addEventListener("ef:rule-sheet", on);
    return () => window.removeEventListener("ef:rule-sheet", on);
  }, []);
  if (!open) return null;
  const close = () => setOpen(null);
  return open.kind === "list" ? <RulesSheet onClose={close} /> : <RuleSheet key={open.line.description + open.line.direction} line={open.line} onClose={close} />;
}
const openRuleSheet = (line) => window.dispatchEvent(new CustomEvent("ef:rule-sheet", { detail: { kind: "rule", line } }));
const openRulesSheet = () => window.dispatchEvent(new CustomEvent("ef:rule-sheet", { detail: { kind: "list" } }));
(() => { const el = document.createElement("div"); el.id = "rule-host"; document.body.appendChild(el); ReactDOM.createRoot(el).render(<RuleHost />); })();

Object.assign(window, { openRuleSheet, openRulesSheet, useStatementRules, RULE_KIND_TEXT });
