/* global React, BrainMark, I, StepHead, NavFooter, IncomeFlow, incomeSummary */
const { useState: useEI, useEffect: useEIE } = React;

const EI_LS = 'bevri_income_v1';
const BI_LS_REF = 'bevri_borrower_v1';

function readBorrowers() {
  try {
    const bi = JSON.parse(localStorage.getItem(BI_LS_REF))?.data;
    if (!bi) return [{ key: 'primary', first: 'You', name: 'You', role: 'Primary Borrower' }];
    const list = [{ key: 'primary', first: bi.name.first || 'Primary', name: `${bi.name.first} ${bi.name.last}`.trim(), role: 'Primary Borrower' }];
    (bi.coBorrowers || []).forEach((c, i) => list.push({ key: 'co-' + i, first: c.name.first || 'Co-borrower', name: `${c.name.first} ${c.name.last}`.trim(), role: 'Co-Borrower' }));
    return list;
  } catch { return [{ key: 'primary', first: 'You', name: 'You', role: 'Primary Borrower' }]; }
}

function normalizeIdpIncomeRecord(r) {
  // Inject the type/payType/method discriminators that IncomeFlow needs to render a valid card.
  // Also map the legacy field names the backend writes (annual/monthly/base) to the names
  // IncomeFlow reads (salaryAmt/salaryFreq). Spread order: defaults first, then extracted
  // values override, so manually-entered records with explicit salaryAmt are unaffected.
  const toMoney = v => {
    const n = typeof v === 'number' ? v : Number(String(v || '').replace(/[$,\s]/g, ''));
    return Number.isFinite(n) && n > 0 ? n.toLocaleString('en-US') : '';
  };
  const out = { type: 'employment', payType: 'salary', method: 'manual', salaryFreq: 'Annually', ...r };
  if (!out.salaryAmt || out.salaryAmt === '') {
    if (r.annual)   { out.salaryAmt = toMoney(r.annual);  out.salaryFreq = 'Annually'; }
    else if (r.ytd) { out.salaryAmt = toMoney(r.ytd);     out.salaryFreq = 'Annually'; }
    else if (r.monthly) { out.salaryAmt = toMoney(r.monthly); out.salaryFreq = 'Monthly'; }
    else if (r.base)    { out.salaryAmt = toMoney(r.base);    out.salaryFreq = 'Annually'; }
  }
  return out;
}

function normalizeIncomeSources(incomes) {
  if (!incomes || typeof incomes !== 'object') return {};
  const result = {};
  for (const [key, val] of Object.entries(incomes)) {
    if (Array.isArray(val)) {
      result[key] = val.map(r => normalizeIdpIncomeRecord(r));
    } else if (val && typeof val === 'object') {
      // setNestedPath writes numeric-keyed objects (e.g. {"0": {annual:...}}) — convert to array
      result[key] = Object.keys(val)
        .sort((a, b) => Number(a) - Number(b))
        .map(k => normalizeIdpIncomeRecord({ id: 'idp-' + key + '-' + k, ...val[k] }));
    } else {
      result[key] = [];
    }
  }
  return result;
}

/* ===================================================================== */
/* ---- payroll connect (verify-first ordering, Jason item 7) ---- */
const ARGYLE_LINK_SRC = 'https://plugin.argyle.com/argyle.web.v5.js';
let eiArgyleScriptPromise = null;
function loadArgyleLink() {
  if (window.Argyle) return Promise.resolve();
  if (!eiArgyleScriptPromise) {
    eiArgyleScriptPromise = new Promise((resolve, reject) => {
      const s = document.createElement('script');
      s.src = ARGYLE_LINK_SRC;
      s.async = true;
      s.onload = resolve;
      s.onerror = () => { eiArgyleScriptPromise = null; reject(new Error('link_unavailable')); };
      document.head.appendChild(s);
    });
  }
  return eiArgyleScriptPromise;
}

/**
 * Verify-first hero: connect a payroll account (Argyle) instead of typing
 * income or uploading documents. Verified records arrive via webhook into the
 * server draft; this polls the draft after a connection, merges the imported
 * records into local storage (replacing only its own previous import), and
 * fires the standard autofill event so the list below updates live. Every
 * failure path quietly leaves manual entry available; nothing here blocks.
 */
function PayrollConnectHero() {
  const [state, setState] = useEI('idle'); // idle | opening | linking | importing | connected | connected-pending | error
  const [employers, setEmployers] = useEI([]);
  const pollRef = React.useRef(null);
  const loanIdOf = () => window.BevriPosStorage?.loanId || new URLSearchParams(window.location.search).get('loanId') || '';

  useEIE(() => () => { if (pollRef.current) clearInterval(pollRef.current); }, []);
  useEIE(() => {
    try {
      const recs = (JSON.parse(localStorage.getItem(EI_LS))?.incomes?.primary) || [];
      const emp = recs.filter(r => r && r.method === 'argyle').map(r => r.employer);
      if (emp.length) { setEmployers(emp); setState('connected'); }
    } catch {}
  }, []);

  function mergeServerIncome(data) {
    const serverRecs = ((data?.bevri_income_v1?.incomes?.primary) || []).filter(r => r && r.method === 'argyle');
    if (!serverRecs.length) return false;
    try {
      const local = JSON.parse(localStorage.getItem(EI_LS)) || {};
      const groups = local.incomes && typeof local.incomes === 'object' ? local.incomes : {};
      const kept = (Array.isArray(groups.primary) ? groups.primary : []).filter(r => !(r && r.method === 'argyle'));
      const withIds = serverRecs.map((r, i) => ({ id: 'argyle-' + i, ...r }));
      localStorage.setItem(EI_LS, JSON.stringify({ ...local, incomes: { ...groups, primary: [...kept, ...withIds] } }));
    } catch { return false; }
    try { window.__bevriPayroll = data.payroll || { provider: 'argyle', employers: serverRecs.map(r => r.employer) }; } catch {}
    window.dispatchEvent(new CustomEvent('bevri:fields-autofilled', { detail: { posKeys: [EI_LS] } }));
    setEmployers(serverRecs.map(r => r.employer));
    return true;
  }

  function startImportPoll() {
    setState('importing');
    let tries = 0;
    if (pollRef.current) clearInterval(pollRef.current);
    pollRef.current = setInterval(async () => {
      tries += 1;
      try {
        const res = await fetch('/api/pos/intake/session?loanId=' + encodeURIComponent(loanIdOf()), { credentials: 'same-origin' });
        const payload = res.ok ? await res.json() : null;
        if (payload && payload.success && mergeServerIncome(payload.draft?.data || {})) {
          clearInterval(pollRef.current); pollRef.current = null;
          setState('connected');
          return;
        }
      } catch {}
      if (tries >= 20) { clearInterval(pollRef.current); pollRef.current = null; setState('connected-pending'); }
    }, 6000);
  }

  async function connect() {
    setState('opening');
    try {
      const res = await fetch('/api/pos/intake/payroll/session', {
        method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'same-origin',
        body: JSON.stringify({ loanId: loanIdOf() }),
      });
      const init = await res.json().catch(() => ({}));
      if (!res.ok || !init.success) throw new Error(init.error || 'unavailable');
      await loadArgyleLink();
      if (!window.Argyle) throw new Error('unavailable');
      const options = init.connect_url
        ? { connectUrl: init.connect_url }
        : { userToken: init.user_token, sandbox: !!init.sandbox, ...(init.plugin_key ? { pluginKey: init.plugin_key } : {}) };
      const link = window.Argyle.create({
        ...options,
        onAccountConnected: () => startImportPoll(),
        onTokenExpired: async (updateToken) => {
          try {
            const r = await fetch('/api/pos/intake/payroll/token-refresh', {
              method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'same-origin',
              body: JSON.stringify({ loanId: loanIdOf() }),
            });
            const d = await r.json().catch(() => ({}));
            if (d.user_token) updateToken(d.user_token);
          } catch {}
        },
        onClose: () => setState(s => (s === 'linking' ? 'idle' : s)),
      });
      setState('linking');
      link.open();
    } catch {
      // Quiet fallback: manual entry stays right below.
      setState('error');
    }
  }

  if (state === 'connected' || state === 'connected-pending') {
    return (
      <div className="ei-payroll connected step-anim">
        <div className="ei-payroll-head">{I.check(16)}<b>Payroll connected</b></div>
        <p className="ei-payroll-sub">
          {state === 'connected'
            ? `Verified income${employers.length ? ' from ' + employers.join(', ') : ''} was imported below. You can edit it or add anything else.`
            : 'Your income is still importing. It will appear below shortly; you can keep going in the meantime.'}
        </p>
      </div>
    );
  }
  return (
    <div className="ei-payroll step-anim">
      <div className="ei-payroll-head">{I.shield ? I.shield(16) : I.check(16)}<b>Verify your income automatically</b></div>
      <p className="ei-payroll-sub">
        Connect your payroll account and your employment and income fill themselves, already verified.
        Takes about a minute. Read-only: your credentials never touch Bevri.
      </p>
      <div className="ei-payroll-actions">
        <button className="btn btn-primary" disabled={state === 'opening' || state === 'importing'} onClick={connect}>
          {state === 'opening' ? 'Opening…' : state === 'importing' ? 'Importing…' : 'Connect payroll'}
        </button>
        <span className="ei-payroll-alt">or add income manually below</span>
      </div>
      {state === 'error' && (
        <p className="ei-payroll-err">Payroll verification is unavailable right now. You can add your income manually below.</p>
      )}
    </div>
  );
}

function EmploymentIncome({ loanType = 'purchase', onBack, onContinue, onProgress }) {
  const saved = (() => { try { return JSON.parse(localStorage.getItem(EI_LS)); } catch { return null; } })();
  const [incomes, setIncomes] = useEI(normalizeIncomeSources(saved?.incomes));  // { borrowerKey: [source,...] }
  const [flow, setFlow] = useEI(null);     // { borrowerKey, first, initial }
  const [confirmRemove, setConfirmRemove] = useEI(null); // { key, id }
  const borrowers = readBorrowers();

  useEIE(() => { try { localStorage.setItem(EI_LS, JSON.stringify({ incomes })); } catch {} }, [incomes]);
  useEIE(() => {
    function onAutoFill(e) {
      if (!(e.detail?.posKeys || []).includes(EI_LS)) return;
      const updated = (() => { try { return JSON.parse(localStorage.getItem(EI_LS)); } catch { return null; } })();
      if (!updated?.incomes) return;
      setIncomes(prev => {
        const next = { ...prev };
        for (const [bKey, sources] of Object.entries(updated.incomes)) {
          if (Array.isArray(sources)) { next[bKey] = sources.map(r => normalizeIdpIncomeRecord(r)); continue; }
          // setNestedPath created object-keyed format; convert to array only if no sources exist yet
          if (sources && typeof sources === 'object' && !(prev[bKey] || []).length) {
            next[bKey] = Object.keys(sources)
              .sort((a, b) => Number(a) - Number(b))
              .map(k => normalizeIdpIncomeRecord({ id: 'idp-' + bKey + '-' + k, ...sources[k] }));
          }
        }
        return next;
      });
    }
    window.addEventListener('bevri:fields-autofilled', onAutoFill);
    return () => window.removeEventListener('bevri:fields-autofilled', onAutoFill);
  }, []);
  useEIE(() => { onProgress && onProgress({ fill: flow ? 50 : 10, stepTitle: 'income' }); }, [flow]);

  const totalSources = Object.values(incomes).reduce((n, arr) => n + (arr ? arr.length : 0), 0);

  function openAdd(b) { setFlow({ borrowerKey: b.key, first: b.first, initial: null }); }
  function openEdit(b, src) { setFlow({ borrowerKey: b.key, first: b.first, initial: src }); }
  function saveSource(src) {
    setIncomes(prev => {
      const list = [...(prev[flow.borrowerKey] || [])];
      const i = list.findIndex(s => s.id === src.id);
      if (i >= 0) list[i] = src; else list.push(src);
      return { ...prev, [flow.borrowerKey]: list };
    });
    setFlow(null);
    const el = document.querySelector('.flow'); if (el) el.scrollTop = 0;
  }
  function removeSource(key, id) {
    setIncomes(prev => ({ ...prev, [key]: (prev[key] || []).filter(s => s.id !== id) }));
    setConfirmRemove(null);
  }

  // sub-flow
  if (flow) {
    return (
      <IncomeFlow borrowerName={flow.first} initial={flow.initial}
        onCancel={() => { setFlow(null); const el = document.querySelector('.flow'); if (el) el.scrollTop = 0; }}
        onSave={saveSource} onProgress={onProgress} />
    );
  }

  const removeName = (() => {
    if (!confirmRemove) return '';
    const src = (incomes[confirmRemove.key] || []).find(s => s.id === confirmRemove.id);
    return src ? incomeSummary(src).title : 'this income';
  })();

  return (
    <main className="flow">
      <div className="flow-col">
        <div className="step">
          <StepHead title="Employment & income"
            sub="Verify your income automatically through your payroll provider, or add income sources for each borrower manually." />

          <PayrollConnectHero />

          <div className="ei-list">
            {borrowers.map(b => {
              const sources = incomes[b.key] || [];
              return (
                <div className="ei-borrower" key={b.key}>
                  <div className="ei-bhead">
                    <span className="co-card-ava">{(b.name[0] || '') + (b.name.split(' ')[1]?.[0] || '')}</span>
                    <div className="ei-bmeta">
                      <div className="co-card-name">{b.name}</div>
                      <div className="person-bar-tag">{b.role}</div>
                    </div>
                  </div>

                  {sources.length === 0
                    ? <div className="ei-empty-state">
                        <div className="ei-empty">No income added yet</div>
                        <button className="ei-upload-cta" onClick={() => window.dispatchEvent(new CustomEvent('bevri:open-upload'))}>
                          {I.doc(15)} Upload a pay stub or W-2 to auto-fill
                        </button>
                      </div>
                    : <div className="ei-sources">
                        {sources.map(src => {
                          const sm = incomeSummary(src);
                          return (
                            <div className="ei-source" key={src.id}>
                              <span className="ei-source-icon">{I.cash(18)}</span>
                              <div className="ei-source-body">
                                <div className="ei-source-title">
                                  {sm.title}
                                  {sm.verified && <span className="ei-verified">{I.check(11)} Verified</span>}
                                  {!sm.verified && src.id?.startsWith('idp-') && <span className="ei-autofill-badge">{I.doc(11)} Auto-filled</span>}
                                </div>
                                <div className="ei-source-detail">{sm.detail}</div>
                              </div>
                              <div className="ei-source-actions">
                                <button className="co-act" onClick={() => openEdit(b, src)}>{I.pencil(13)}Edit</button>
                                <button className="co-act danger" onClick={() => setConfirmRemove({ key: b.key, id: src.id })}>{I.trash(13)}Remove</button>
                              </div>
                            </div>
                          );
                        })}
                      </div>}

                  <button className="ei-add" onClick={() => openAdd(b)}>
                    <span className="co-add-icon">{I.plus(18)}</span>
                    Add Income
                  </button>
                </div>
              );
            })}
          </div>

          {totalSources === 0 && <p className="ei-prompt">Add at least one income source to continue.</p>}

          <NavFooter onBack={onBack} onNext={() => onContinue && onContinue()} nextLabel="Confirm and Continue" nextDisabled={totalSources === 0} />
        </div>
      </div>

      {confirmRemove && (
        <div className="modal-scrim" onClick={() => setConfirmRemove(null)}>
          <div className="modal" onClick={e => e.stopPropagation()}>
            <div className="modal-title">Remove this income source?</div>
            <div className="modal-body">This will remove this {removeName.toLowerCase()} information from the application.</div>
            <div className="modal-actions">
              <button className="btn btn-secondary" onClick={() => setConfirmRemove(null)}>Cancel</button>
              <button className="btn btn-danger" onClick={() => removeSource(confirmRemove.key, confirmRemove.id)}>Remove Income</button>
            </div>
          </div>
        </div>
      )}
    </main>
  );
}

window.EmploymentIncome = EmploymentIncome;
