/* Overlay part A: EditLedgerLevelsModal + REASON_LABEL codes.
 * Load AFTER widgets-signals.jsx. PATCH /api/signals/:id — ledger only.
 * Also loads GET /api/signals/:id/events for the per-row Manual/Levels edit trail.
 */
Object.assign(REASON_LABEL, {
  SL_WRONG_SIDE_OF_ENTRY: REASON_LABEL.SL_WRONG_SIDE_OF_ENTRY || 'Stop on wrong side of entry',
  TP_WRONG_SIDE_OF_ENTRY: 'Take-profit on wrong side of entry',
  TPS_NOT_ORDERED: 'Take-profits not ordered',
  NO_FIELD_CHANGES: 'No field changes',
  NOT_FOUND: 'Signal not found',
});

function fmtTrailLevel(v) {
  if (v == null) return '—';
  if (Array.isArray(v)) {
    if (!v.length) return '—';
    return v.map(x => (x == null ? '—' : fmtNum(x))).join(', ');
  }
  return fmtNum(v);
}

function fmtTrailPair(pair) {
  if (!Array.isArray(pair) || pair.length < 2) return null;
  return fmtTrailLevel(pair[0]) + ' → ' + fmtTrailLevel(pair[1]);
}

function LedgerLevelsEventTrail({ signalId }) {
  const [events, setEvents] = useS(null); // null=loading, []=empty, [...] = rows
  const [err, setErr] = useS(null);

  useE(() => {
    if (!signalId) { setEvents(null); setErr(null); return; }
    let cancelled = false;
    setEvents(null); setErr(null);
    fetch('/api/signals/' + signalId + '/events?limit=30')
      .then(x => x.json())
      .then(r => {
        if (cancelled) return;
        if (!r || !r.ok) {
          setErr((r && (r.error || r.detail)) || 'Failed to load edit trail');
          setEvents([]);
          return;
        }
        setEvents(Array.isArray(r.data) ? r.data : []);
      })
      .catch(e => {
        if (cancelled) return;
        setErr(String(e.message || e));
        setEvents([]);
      });
    return () => { cancelled = true; };
  }, [signalId]);

  const labelStyle = {
    fontSize: 9.5, color: 'var(--text-4)', letterSpacing: '0.08em',
    textTransform: 'uppercase', marginBottom: 8, display: 'block',
  };

  return (
    <div style={{
      marginTop: 16, paddingTop: 14,
      borderTop: '1px solid var(--border-soft)',
    }}>
      <span style={{ ...labelStyle, marginBottom: 2 }}>Edit trail</span>
      <div style={{ fontSize: 10.5, color: 'var(--text-4)', marginBottom: 8, lineHeight: 1.35 }}>
        Ledger saves on this signal.
      </div>
      {events == null && !err && (
        <div style={{ fontSize: 11, color: 'var(--text-4)' }}>Loading…</div>
      )}
      {err && (
        <div style={{ fontSize: 11, color: 'var(--bear)', lineHeight: 1.4 }}>{err}</div>
      )}
      {events && !events.length && !err && (
        <div style={{ fontSize: 11, color: 'var(--text-4)' }}>No edits yet.</div>
      )}
      {events && events.length > 0 && (() => {
        const visible = events.filter(ev => {
          const fc = ev.field_changes || {};
          const hasLevels = !!(fmtTrailPair(fc.sl) || fmtTrailPair(fc.tps));
          const stateChanged = ev.from_state != null && ev.to_state != null
            && ev.from_state !== ev.to_state;
          return ev.is_manual || hasLevels || stateChanged || !!fc.note;
        });
        if (!visible.length) {
          return <div style={{ fontSize: 11, color: 'var(--text-4)' }}>No edits yet.</div>;
        }
        return (
        <div style={{
          maxHeight: 180, overflowY: 'auto',
          display: 'flex', flexDirection: 'column', gap: 8,
        }}>
          {visible.map(ev => {
            const fc = ev.field_changes || {};
            const slPair = fmtTrailPair(fc.sl);
            const tpsPair = fmtTrailPair(fc.tps);
            const who = fc.edited_by || null;
            const note = fc.note || null;
            const stateChanged = ev.from_state != null && ev.to_state != null
              && ev.from_state !== ev.to_state;
            const badge = ev.is_manual
              ? 'Manual'
              : (stateChanged ? (String(ev.from_state) + ' → ' + String(ev.to_state)) : 'Update');
            const hasLevels = !!(slPair || tpsPair);
            return (
              <div key={ev.id} style={{
                fontSize: 11, lineHeight: 1.45, padding: '7px 9px',
                background: 'var(--bg-elev)', borderRadius: 6,
                borderLeft: ev.is_manual
                  ? '3px solid var(--gold, #d4a017)'
                  : '3px solid var(--border-soft)',
              }}>
                <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, alignItems: 'baseline' }}>
                  <span style={{
                    fontSize: 9.5, fontWeight: 600, fontFamily: 'var(--font-sans)',
                    color: ev.is_manual ? 'var(--gold, #d4a017)' : 'var(--text-3)',
                    textTransform: 'uppercase', letterSpacing: '0.06em',
                  }}>{badge}</span>
                  <span style={{ color: 'var(--text-4)', fontFamily: 'var(--font-mono)', fontSize: 10 }}>
                    {agoFrom(ev.at)}
                  </span>
                  {who && (
                    <span style={{ color: 'var(--text-3)', fontSize: 10.5 }} title={who}>{who}</span>
                  )}
                </div>
                {slPair && (
                  <div style={{ marginTop: 3, fontFamily: 'var(--font-mono)', color: 'var(--text-2)' }}>
                    SL {slPair}
                  </div>
                )}
                {tpsPair && (
                  <div style={{ marginTop: 2, fontFamily: 'var(--font-mono)', color: 'var(--text-2)' }}>
                    TP {tpsPair}
                  </div>
                )}
                {note && (
                  <div style={{ marginTop: 3, color: 'var(--text-3)', fontStyle: 'italic' }}>
                    {note}
                  </div>
                )}
                {!ev.is_manual && stateChanged && !hasLevels && (
                  <div style={{ marginTop: 2, color: 'var(--text-4)', fontSize: 10.5 }}>
                    state {ev.from_state || '—'} → {ev.to_state || '—'}
                  </div>
                )}
              </div>
            );
          })}
        </div>
        );
      })()}
    </div>
  );
}

function EditLedgerLevelsModal({ target, onClose, onSaved }) {
  const [form, setForm] = useS(null);
  const [busy, setBusy] = useS(false);
  const [err, setErr] = useS(null);
  const [okMsg, setOkMsg] = useS(null);
  const [clearSl, setClearSl] = useS(false);
  const [clearTps, setClearTps] = useS(false);

  useE(() => {
    if (!target) { setForm(null); return; }
    setForm({
      sl: target.sl != null ? String(target.sl) : '',
      tps: (target.tps && target.tps.length) ? target.tps.map(String).join(', ') : '',
      note: '',
    });
    setErr(null); setOkMsg(null); setClearSl(false); setClearTps(false); setBusy(false);
  }, [target]);

  if (!target || !form) return null;

  const entry = target.entry_price != null ? target.entry_price
    : (target.reference_price != null ? target.reference_price : null);

  function parseTps(raw) {
    const parts = String(raw || '').split(/[\s,]+/).map(s => s.trim()).filter(Boolean);
    if (!parts.length) return [];
    const nums = parts.map(Number);
    if (nums.some(n => !Number.isFinite(n))) return null;
    return nums;
  }

  function buildBody() {
    const body = {};
    if (clearSl) {
      body.sl = null;
    } else if (form.sl.trim() !== '') {
      const n = Number(form.sl);
      if (!Number.isFinite(n)) return { error: 'Stop must be a number (or clear it).' };
      const orig = target.sl != null ? Number(target.sl) : null;
      if (orig === null || n !== orig) body.sl = n;
    }

    if (clearTps) {
      body.tps = null;
    } else if (form.tps.trim() !== '') {
      const nums = parseTps(form.tps);
      if (nums === null) return { error: 'Targets must be numbers, separated by commas or spaces.' };
      const orig = (target.tps || []).map(Number);
      const same = nums.length === orig.length && nums.every((n, i) => n === orig[i]);
      if (!same) body.tps = nums;
    }

    if (form.note.trim()) body.note = form.note.trim();
    return { body };
  }

  async function submit() {
    setErr(null); setOkMsg(null);
    const built = buildBody();
    if (built.error) { setErr(built.error); return; }
    const { body } = built;
    if (!Object.prototype.hasOwnProperty.call(body, 'sl') && !Object.prototype.hasOwnProperty.call(body, 'tps')) {
      setErr(reasonLabel('NO_FIELD_CHANGES') + ' — provide a stop and/or targets (or clear one). Note alone is not enough.');
      return;
    }
    setBusy(true);
    try {
      const r = await fetch(`/api/signals/${target.id}`, {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(body),
      }).then(x => x.json()).catch(e => ({ ok: false, error: String(e.message || e) }));
      setBusy(false);
      if (!r.ok) {
        const label = reasonLabel(r.error) || r.error || 'Save failed';
        setErr(r.detail ? `${label}: ${r.detail}` : label);
        return;
      }
      onSaved();
      if (r.changed === false) {
        setOkMsg('Already up to date — no ledger change.');
      } else {
        onClose();
      }
    } catch (e) {
      setBusy(false);
      setErr(String(e.message || e));
    }
  }

  const inp = {
    padding: '7px 9px', borderRadius: 6, fontSize: 12.5, width: '100%',
    background: 'var(--bg-elev)', border: '1px solid var(--border-soft)',
    color: 'var(--text)', fontFamily: 'var(--font-mono)', boxSizing: 'border-box',
  };
  const label = { fontSize: 9.5, color: 'var(--text-4)', letterSpacing: '0.08em',
                  textTransform: 'uppercase', marginBottom: 4, display: 'block' };
  const clearBtn = {
    padding: '2px 8px', borderRadius: 5, cursor: busy ? 'wait' : 'pointer', fontSize: 10,
    background: 'transparent', border: '1px solid var(--border-soft)', color: 'var(--text-3)',
    fontFamily: 'var(--font-sans)', marginLeft: 8,
  };

  return (
    <div style={{
      position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.55)', zIndex: 50,
      display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 20,
    }} onClick={() => !busy && onClose()}>
      <div onClick={e => e.stopPropagation()} className="card" style={{ width: 440, maxWidth: '100%', maxHeight: '92vh', overflowY: 'auto' }}>
        <div className="card-h" style={{ justifyContent: 'space-between' }}>
          <span style={{ fontWeight: 600, fontSize: 13 }}>
            Edit ledger levels · {target.symbol} {target.side}
          </span>
          <button onClick={onClose} disabled={busy} style={{
            background: 'transparent', border: 'none', cursor: busy ? 'wait' : 'pointer',
            color: 'var(--text-4)', fontSize: 16, padding: 0,
          }}>×</button>
        </div>
        <div className="card-b">
          <div style={{
            fontSize: 11.5, color: 'var(--text-2)', marginBottom: 14, lineHeight: 1.5,
            padding: '9px 11px', background: 'var(--bg-elev)', borderRadius: 7,
            borderLeft: '3px solid var(--gold, #d4a017)',
          }}>
            Saves to the ledger only. Does not push SL/TP to the exchange.
          </div>
          <div style={{ display: 'flex', gap: 16, marginBottom: 14, fontSize: 12 }}>
            <div>
              <span style={label}>Side</span>
              <span style={{ color: target.side === 'long' ? 'var(--bull)' : 'var(--bear)', fontWeight: 600 }}>
                {target.side || '—'}
              </span>
            </div>
            <div>
              <span style={label}>Entry</span>
              <span className="mono" style={{ color: 'var(--text-2)' }}>
                {entry != null ? fmtNum(entry) : '—'}
                {target.entry_price == null && target.reference_price != null && (
                  <span style={{ color: 'var(--text-4)', fontSize: 10, marginLeft: 6 }}>(ref)</span>
                )}
              </span>
            </div>
          </div>
          <div style={{ marginBottom: 12 }}>
            <div style={{ display: 'flex', alignItems: 'center', marginBottom: 4 }}>
              <span style={{ ...label, marginBottom: 0 }}>Stop loss</span>
              <button type="button" disabled={busy} onClick={() => {
                setClearSl(true); setForm({ ...form, sl: '' });
              }} style={clearBtn} title="Clear stop on the ledger (send null)">Clear</button>
              {clearSl && <span style={{ marginLeft: 8, fontSize: 10, color: 'var(--gold)' }}>will clear</span>}
            </div>
            <input style={inp} inputMode="decimal" value={form.sl} disabled={busy || clearSl}
                   placeholder="leave blank to keep current"
                   onChange={e => { setClearSl(false); setForm({ ...form, sl: e.target.value }); }} />
          </div>
          <div style={{ marginBottom: 12 }}>
            <div style={{ display: 'flex', alignItems: 'center', marginBottom: 4 }}>
              <span style={{ ...label, marginBottom: 0 }}>Targets</span>
              <button type="button" disabled={busy} onClick={() => {
                setClearTps(true); setForm({ ...form, tps: '' });
              }} style={clearBtn} title="Clear all targets on the ledger (send null)">Clear</button>
              {clearTps && <span style={{ marginLeft: 8, fontSize: 10, color: 'var(--gold)' }}>will clear</span>}
            </div>
            <input style={inp} inputMode="decimal" value={form.tps} disabled={busy || clearTps}
                   placeholder="e.g. 96000, 99000 — blank keeps current"
                   onChange={e => { setClearTps(false); setForm({ ...form, tps: e.target.value }); }} />
            <div style={{ fontSize: 10.5, color: 'var(--text-4)', marginTop: 4 }}>
              Comma or space separated. Long: ascending; short: descending.
            </div>
          </div>
          <div style={{ marginBottom: 14 }}>
            <span style={label}>Note (optional)</span>
            <input style={{ ...inp, fontFamily: 'var(--font-sans)' }} value={form.note} disabled={busy}
                   placeholder="Shown on the audit row"
                   onChange={e => setForm({ ...form, note: e.target.value })} />
          </div>
          {err && <div style={{ color: 'var(--bear)', fontSize: 11.5, marginBottom: 12, lineHeight: 1.45 }}>{err}</div>}
          {okMsg && <div style={{ color: 'var(--bull)', fontSize: 11.5, marginBottom: 12 }}>{okMsg}</div>}
          <div style={{ display: 'flex', gap: 8, justifyContent: 'flex-end' }}>
            <button onClick={onClose} disabled={busy} style={{
              padding: '8px 14px', borderRadius: 7, cursor: busy ? 'wait' : 'pointer', fontSize: 12,
              background: 'transparent', border: '1px solid var(--border-soft)', color: 'var(--text-2)',
              fontFamily: 'var(--font-sans)',
            }}>Cancel</button>
            <button onClick={submit} disabled={busy} style={{
              padding: '8px 14px', borderRadius: 7, cursor: busy ? 'wait' : 'pointer', fontSize: 12,
              background: 'var(--gold)', border: 'none', color: 'var(--bg-base)', fontWeight: 600,
              fontFamily: 'var(--font-sans)', opacity: busy ? 0.6 : 1,
            }}>{busy ? 'Saving…' : 'Save to ledger'}</button>
          </div>
          <LedgerLevelsEventTrail signalId={target.id} />
        </div>
      </div>
    </div>
  );
}
