/* === Salesforce CSV upload (admin) ===
   Accepts the RS Salesforce "Export Details" CSVs saved as Unicode (UTF-8):
     - Registrations (All Active Registrations Public) → merged onto
       jurisdictions as `registrations` (snapshot semantics: states absent
       from the report are cleared)
     - Meetings (CFY+PFY Meetings Retirement Savings) → `meeting` entities,
       state inferred from the account/subject text and reviewable before
       commit (additive: never deletes)
     - Lobby Time Entries → recognized but DEFERRED: the report has no
       Jurisdiction column yet, which is the field the GR report needs
   Report type is detected from the header row, so several files can be
   dropped at once. Every change lands in the event log like any edit. */

function sfDetect(rows) {
  const h = (rows[0] || []).map((x) => x.trim());
  if (h.includes("Jurisdiction Name") && h.includes("Pew Lobbyist")) return "registrations";
  if (h.includes("Contact") && h.includes("Subject") && h.includes("Assigned")) return "meetings";
  if (h.some((x) => /lobby time/i.test(x))) return "lte";
  return null;
}

function sfNameToAbbr(name) {
  const n = (name || "").trim().toLowerCase();
  if (!n) return null;
  if (/^(us|united states|federal)$/.test(n)) return "US";
  for (const [abbr, full] of Object.entries(window.STATE_NAMES)) {
    if (full.toLowerCase() === n) return abbr;
  }
  return null;
}

function sfBuildRegPlan(rows) {
  const header = rows[0].map((x) => x.trim());
  const col = (n) => header.indexOf(n);
  const byState = {};
  const skipped = new Set();
  for (const r of rows.slice(1)) {
    if (r.length < 3) continue;
    const get = (n) => (r[col(n)] || "").trim();
    const abbr = sfNameToAbbr(get("Jurisdiction Name"));
    if (!abbr) { if (get("Jurisdiction Name")) skipped.add(get("Jurisdiction Name")); continue; }
    if (get("Termination Date")) continue;
    (byState[abbr] = byState[abbr] || []).push({
      project: get("Pew Project"), name: get("Pew Lobbyist"),
      registered: get("Registration Date") || null, expires: get("Expiration Date") || null,
    });
  }
  // Snapshot semantics: the report is "all active" — clear states not in it.
  const events = [];
  for (const [abbr, j] of Object.entries(window.JURISDICTIONS)) {
    const next = byState[abbr] || [];
    const cur = j.registrations || [];
    if (JSON.stringify(next) !== JSON.stringify(cur)) {
      events.push({
        entityType: "jurisdiction", entityId: abbr, action: "update",
        patch: { registrations: next }, prev: { registrations: cur },
        note: "Salesforce registrations sync",
      });
    }
  }
  const rsStates = Object.entries(byState).filter(([, a]) => a.some((x) => x.project === "Retirement Savings")).map(([s]) => s);
  return { kind: "registrations", events, states: Object.keys(byState).length, rsStates, skipped: [...skipped] };
}

function sfInferState(texts) {
  const joined = texts.join(" ");
  // Longer names first — "West Virginia" must not match "Virginia".
  const byLen = Object.entries(window.STATE_NAMES).sort((a, b) => b[1].length - a[1].length);
  for (const [abbr, full] of byLen) {
    if (joined.includes(full)) return abbr;
  }
  const m = joined.match(/\(([A-Z]{2})\d/);           // "(MA035 D)" district codes
  if (m && window.STATE_NAMES[m[1]]) return m[1];
  const m2 = joined.match(/\b([A-Z]{2})\s+State\b/);  // "MA State Sen."
  if (m2 && window.STATE_NAMES[m2[1]]) return m2[1];
  return "";
}

function sfBuildMeetingRows(rows) {
  const header = rows[0].map((x) => x.trim());
  const col = (n) => header.indexOf(n);
  const out = [];
  for (const r of rows.slice(1)) {
    if (r.length < 3) continue;
    const get = (n) => (r[col(n)] || "").trim();
    if (!get("Date") || !get("Subject")) continue;
    const id = "mt-" + (get("Date") + "-" + get("Contact")).toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
    out.push({
      id,
      date: window.fmtUsDate(get("Date")),
      contact: get("Contact"),
      account: get("Company / Account"),
      subject: get("Subject"),
      assigned: get("Assigned"),
      state: sfInferState([get("Location"), get("Company / Account"), get("Subject")]),
    });
  }
  return out;
}

function SalesforceUpload({ onClose }) {
  const [plans, setPlans] = React.useState([]);       // reg/lte plans + info
  const [meetings, setMeetings] = React.useState([]); // reviewable meeting rows
  const [err, setErr] = React.useState(null);
  const [busy, setBusy] = React.useState(false);
  const [done, setDone] = React.useState(null);

  React.useEffect(() => {
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [onClose]);

  function handleFiles(e) {
    setErr(null); setDone(null);
    const files = [...e.target.files];
    files.forEach((f) => {
      const reader = new FileReader();
      reader.onload = () => {
        try {
          const rows = parseCsvText(String(reader.result).replace(/^﻿/, ""));
          const kind = sfDetect(rows);
          if (kind === "registrations") {
            setPlans((p) => [...p.filter((x) => x.kind !== "registrations"), { ...sfBuildRegPlan(rows), file: f.name }]);
          } else if (kind === "meetings") {
            setMeetings(sfBuildMeetingRows(rows));
          } else if (kind === "lte") {
            setPlans((p) => [...p.filter((x) => x.kind !== "lte"), { kind: "lte", file: f.name, events: [] }]);
          } else {
            setErr(`"${f.name}" doesn't look like one of the Salesforce reports (registrations / meetings / LTEs). Export as CSV, Unicode (UTF-8).`);
          }
        } catch (ex) { setErr(`${f.name}: ${String(ex.message || ex)}`); }
      };
      reader.readAsText(f, "utf-8");
    });
    e.target.value = "";
  }

  const existingMeetings = Object.fromEntries((window.MEETINGS || []).map((m) => [m.id, m]));
  const meetingEvents = meetings
    .map((m) => {
      const ex = existingMeetings[m.id];
      if (!ex) return { entityType: "meeting", entityId: m.id, action: "create", patch: m, note: "Salesforce meetings sync" };
      if (JSON.stringify(ex) !== JSON.stringify(m)) {
        return { entityType: "meeting", entityId: m.id, action: "update", patch: m, prev: ex, note: "Salesforce meetings sync" };
      }
      return null;
    })
    .filter(Boolean);

  const regPlan = plans.find((p) => p.kind === "registrations");
  const ltePlan = plans.find((p) => p.kind === "lte");
  const totalEvents = (regPlan ? regPlan.events.length : 0) + meetingEvents.length;

  async function commit() {
    setBusy(true); setErr(null);
    try {
      const events = [
        ...(regPlan ? regPlan.events : []),
        ...meetingEvents,
        { entityType: "meta", entityId: "app", action: "update",
          patch: { lastSalesforceUpload: new Date().toISOString() },
          prev: { lastSalesforceUpload: (window.META && window.META.lastSalesforceUpload) || null } },
      ];
      for (let i = 0; i < events.length; i += 40) await window.Store.commit(events.slice(i, i + 40));
      setDone(`Synced: ${regPlan ? `registrations for ${regPlan.states} jurisdictions` : ""}${regPlan && meetingEvents.length ? " · " : ""}${meetingEvents.length ? `${meetingEvents.length} meeting change(s)` : ""}${totalEvents === 0 ? "no changes — already up to date" : ""}.`);
      setPlans([]); setMeetings([]);
    } catch (ex) { setErr(String(ex.message || ex)); }
    finally { setBusy(false); }
  }

  const input = {
    width: "100%", padding: "6px 8px", borderRadius: 4, border: "1px solid var(--border)",
    background: "var(--surface-2)", font: "500 12px/1.3 var(--sans)", outline: "none",
  };

  return (
    <>
      <div className="scrim is-open" onClick={onClose}></div>
      <aside className="side-panel is-open" style={{ width: 620 }}>
        <div className="side-panel__head">
          <button className="side-panel__close" onClick={onClose} title="Close">
            <svg width="14" height="14" viewBox="0 0 14 14"><path d="M3 3 L11 11 M11 3 L3 11" stroke="currentColor" strokeWidth="1.5" fill="none" strokeLinecap="round" /></svg>
          </button>
          <div className="side-panel__eyebrow">Salesforce sync</div>
          <div className="side-panel__title" style={{ fontSize: 19 }}>Upload Salesforce reports</div>
        </div>

        <div className="side-panel__section">
          <div className="muted" style={{ fontSize: 12, lineHeight: 1.6, marginBottom: 10 }}>
            Drop one or more of the three RS reports, exported as <strong>CSV · Unicode (UTF-8)</strong> with
            "Export Details" (flat rows). The report type is detected automatically.
          </div>
          <input type="file" accept=".csv" multiple onChange={handleFiles}
            style={{ fontSize: 12, fontFamily: "var(--mono)" }} />
        </div>

        {regPlan ? (
          <div className="side-panel__section">
            <h4>Registrations — {regPlan.file}</h4>
            <div style={{ fontSize: 12, color: "var(--ink-2)", lineHeight: 1.6 }}>
              Active registrations in <strong>{regPlan.states}</strong> jurisdictions ·
              RS registered in: <strong>{regPlan.rsStates.join(", ") || "none"}</strong> ·
              {regPlan.events.length} jurisdiction(s) will change.
              {regPlan.skipped.length ? (
                <div className="mono faint" style={{ fontSize: 10, marginTop: 4 }}>Unrecognized jurisdictions skipped: {regPlan.skipped.join(", ")}</div>
              ) : null}
            </div>
          </div>
        ) : null}

        {meetings.length ? (
          <div className="side-panel__section">
            <h4>Meetings — review states before sync</h4>
            <div className="muted" style={{ fontSize: 11, marginBottom: 8 }}>
              State is inferred from the account/subject text — fix any that are wrong (federal meetings → US).
              {meetingEvents.length === 0 ? " All rows match what's already stored." : ""}
            </div>
            {meetings.map((m, i) => (
              <div key={m.id} style={{ display: "grid", gridTemplateColumns: "78px 1fr 86px", gap: 8, alignItems: "start", padding: "6px 0", borderTop: "1px solid var(--rule)" }}>
                <span className="mono" style={{ fontSize: 11 }}>{m.date}</span>
                <span style={{ fontSize: 12 }}>
                  <strong>{m.contact}</strong> <span className="muted">· {m.assigned}</span>
                  <span className="muted" style={{ display: "block", fontSize: 11 }}>{m.subject}</span>
                </span>
                <select style={input} value={m.state}
                  onChange={(e) => setMeetings(meetings.map((x, xi) => xi === i ? { ...x, state: e.target.value } : x))}>
                  <option value="">— state?</option>
                  <option value="US">US (federal)</option>
                  {Object.keys(window.STATE_NAMES).sort().map((a) => <option key={a} value={a}>{a}</option>)}
                </select>
              </div>
            ))}
          </div>
        ) : null}

        {ltePlan ? (
          <div className="side-panel__section">
            <h4>Lobby time entries — {ltePlan.file}</h4>
            <div className="muted" style={{ fontSize: 12, lineHeight: 1.6 }}>
              Recognized but not ingested yet: this report has no <strong>Jurisdiction</strong> column, which
              is the field the GR report needs. Ask the Salesforce admin to add Jurisdiction (and the
              actual-contact flag) to the LTE report — ingestion is ready to build once it has them.
            </div>
          </div>
        ) : null}

        {(regPlan || meetings.length) ? (
          <div className="side-panel__section" style={{ borderBottom: 0 }}>
            <button className="btn btn--primary" disabled={busy || totalEvents === 0} onClick={commit}>
              {busy ? "Syncing…" : totalEvents === 0 ? "Nothing to change" : `Sync ${totalEvents} change(s)`}
            </button>
          </div>
        ) : null}

        {err ? <div style={{ margin: "14px 22px", padding: "8px 12px", borderRadius: 4, background: "var(--red-bg)", color: "var(--red)", fontSize: 12 }}>{err}</div> : null}
        {done ? <div style={{ margin: "14px 22px", padding: "8px 12px", borderRadius: 4, background: "var(--green-bg)", color: "var(--green)", fontSize: 12 }}>{done}</div> : null}
      </aside>
    </>
  );
}

Object.assign(window, { SalesforceUpload });
