// Live data bridge — thin layer consumed by visual components.
// Reads from App context (liveData) and PropArenaApi.
// Provides hooks and helpers so components don't touch raw API.

function useLiveUser() {
  const { user } = useApp();
  if (!user) return { name: 'Trader', initials: 'TR', email: '', raw: null };
  const name = window.PropArenaApi?.userName?.(user) || 'Trader';
  const initials = window.PropArenaApi?.userInitials?.(user) || name.slice(0, 2).toUpperCase();
  const email = user.email || '';
  return { name, initials, email, raw: user };
}

function readLocalWalletRows() {
  try {
    const rows = JSON.parse(localStorage.getItem('proparena:staging-wallet-tx') || '[]');
    return Array.isArray(rows) ? rows : [];
  } catch {
    return [];
  }
}

function useLiveWallet() {
  const { liveData } = useApp();
  const local = readLocalWalletRows();
  const mapped = [...local, ...(liveData.walletMapped || [])];
  const successful = mapped.filter(t => t.status === 'done');
  const payouts = mapped.filter(t => t.kind === 'payout');
  const orders = mapped.filter(t => t.kind === 'order');
  const deposits = mapped.filter(t => t.kind === 'deposit');
  const withdrawals = mapped.filter(t => t.kind === 'withdraw');
  const refunds = mapped.filter(t => t.kind === 'refund');
  // Reference parity (deriveWalletBalance): deposits + transferIn (order-credit)
  // + refunds − transferOut (wallet-payment). Withdrawals do NOT subtract; they
  // settle outside the wallet to the user's bank/crypto account.
  const balance = successful.reduce((sum, tx) => {
    const amount = Math.abs(Number(tx.amount) || 0);
    if (tx.kind === 'deposit' || tx.kind === 'refund' || tx.kind === 'order-credit') return sum + amount;
    if (tx.kind === 'wallet-payment') return sum - amount;
    return sum;
  }, 0);
  const totalPaid = [...payouts, ...withdrawals].filter(t => t.status === 'done').reduce((s, t) => s + Math.abs(t.amount), 0);
  return { balance, mapped, payouts, orders, deposits, withdrawals, refunds, totalPaid, raw: liveData.walletTransactions };
}

function useLiveMessages() {
  const { liveData } = useApp();
  const raw = liveData.messages || [];
  return raw.map((m, i) => ({
    id: m.id || `msg-${i}`,
    from: m.from || m.sender || m.author || 'Prop Arena',
    subj: m.subject || m.title || 'Message',
    preview: m.body || m.content || m.preview || m.text || '',
    tm: m.date || m.createdAt || m.created || '',
    cat: m.category || m.type || 'support',
    unread: m.isRead === false || m.read === false || m.unread === true,
    raw: m,
  }));
}

function useLiveCountries() {
  const { liveData } = useApp();
  return liveData.countries || [];
}

// ── Account scope (Challenge / Funded / All) ────────────────────
// Reference parity: localStorage + custom-event broadcast so the scope
// follows the user across dashboard, accounts, journal (desktop & mobile).
const ACCOUNT_SCOPE_STORAGE = 'pa-account-scope';
const ACCOUNT_SCOPE_EVENT = 'pa-account-scope-change';

function readAccountScope() {
  if (typeof window === 'undefined') return 'all';
  const raw = window.localStorage.getItem(ACCOUNT_SCOPE_STORAGE);
  return raw === 'challenge' || raw === 'funded' || raw === 'all' ? raw : 'all';
}

function setStoredAccountScope(scope) {
  if (typeof window === 'undefined') return;
  window.localStorage.setItem(ACCOUNT_SCOPE_STORAGE, scope);
  window.dispatchEvent(new CustomEvent(ACCOUNT_SCOPE_EVENT, { detail: scope }));
}

function accountScopeForAccount(account) {
  if (!account) return 'funded';
  // Mirror reference: demo accounts → challenge, live accounts → funded.
  const raw = account.raw || account;
  const isDemo = (() => {
    const v = raw?.isDemo;
    if (typeof v === 'boolean') return v;
    if (typeof v === 'string') {
      const s = v.trim().toLowerCase();
      if (['true', '1', 'yes', 'y', 'demo'].includes(s)) return true;
      if (['false', '0', 'no', 'n', 'live', 'real'].includes(s)) return false;
    }
    if (typeof v === 'number') return v !== 0;
    return Boolean(v);
  })();
  return isDemo ? 'challenge' : 'funded';
}

function filterAccountsByScope(accounts, scope) {
  if (!Array.isArray(accounts)) return [];
  if (!scope || scope === 'all') return accounts;
  return accounts.filter(a => accountScopeForAccount(a) === scope);
}

function useAccountScope() {
  const [scope, setScopeState] = useState('all');
  useEffect(() => {
    setScopeState(readAccountScope());
    const onChange = (event) => {
      const detail = event?.detail;
      if (detail === 'challenge' || detail === 'funded' || detail === 'all') setScopeState(detail);
      else setScopeState(readAccountScope());
    };
    const onStorage = (event) => {
      if (event.key === ACCOUNT_SCOPE_STORAGE) setScopeState(readAccountScope());
    };
    window.addEventListener(ACCOUNT_SCOPE_EVENT, onChange);
    window.addEventListener('storage', onStorage);
    return () => {
      window.removeEventListener(ACCOUNT_SCOPE_EVENT, onChange);
      window.removeEventListener('storage', onStorage);
    };
  }, []);
  const setScope = (next) => {
    setScopeState(next);
    setStoredAccountScope(next);
  };
  return [scope, setScope];
}

// ── Live objectives / breach status / account data ──────────────
function useAccountObjectives(account) {
  const { liveData } = useApp();
  if (!account) return null;
  const keys = [account.id, account.brokerUserId, account.clientApiId, account.login, account.mtLogin].filter(Boolean).map(String);
  for (const key of keys) {
    const obj = liveData?.accountObjectives?.[key];
    if (obj) return obj;
  }
  return null;
}

function useAccountBreach(account) {
  const { liveData } = useApp();
  if (!account) return null;
  const keys = [account.id, account.brokerUserId, account.clientApiId, account.login, account.mtLogin].filter(Boolean).map(String);
  for (const key of keys) {
    const b = liveData?.accountBreach?.[key];
    if (b) return b;
  }
  return null;
}

function useAccountData(account) {
  const { liveData } = useApp();
  if (!account) return null;
  const keys = [account.id, account.brokerUserId, account.clientApiId, account.login, account.mtLogin].filter(Boolean).map(String);
  for (const key of keys) {
    const d = liveData?.accountData?.[key];
    if (d) return d;
  }
  return null;
}

function pickPositive(...values) {
  for (const v of values) {
    if (typeof v === 'number' && Number.isFinite(v) && v > 0) return v;
    if (typeof v === 'string' && v.trim() && Number.isFinite(Number(v)) && Number(v) > 0) return Number(v);
  }
  return null;
}

// Live-aware progress. Prefers objectives.profitTarget / maxLoss / dailyMaxLoss
// over the data.js template percentages. Falls back to template when objectives
// not yet loaded.
function useLiveAccountProgress(a) {
  const objectives = useAccountObjectives(a);
  const breach = useAccountBreach(a);
  const fallback = useMemo(() => window.computeProgress(a), [a]);
  return useMemo(() => {
    if (!objectives && !breach) return fallback;
    const profit = a.balance - a.size;
    const profitTargetUsd = pickPositive(
      objectives?.profitTarget,
      objectives?.profitTargetUsd,
      objectives?.profitTargetAmount,
      fallback.profitTargetUsd,
    ) ?? fallback.profitTargetUsd;
    const profitPct = profitTargetUsd > 0 ? profit / profitTargetUsd : fallback.profitPct;
    const profitTargetReached = pickPositive(
      objectives?.profitTargetReached,
      objectives?.profitTargetUsedAmount,
      objectives?.profitTargetCurrent,
    );
    const usedPct = typeof profitTargetReached === 'number' && profitTargetUsd > 0
      ? profitTargetReached / profitTargetUsd
      : profitPct;

    const maxLossUsd = pickPositive(
      objectives?.maxLoss,
      objectives?.maxLossUsd,
      objectives?.maxLossAmount,
      fallback.maxLossUsd,
    ) ?? fallback.maxLossUsd;
    const maxLossUsed = pickPositive(
      objectives?.maxLossUsed,
      objectives?.maxLossCurrent,
      fallback.ddSoFar,
    ) ?? fallback.ddSoFar;
    const ddPct = maxLossUsd > 0 ? Math.min(1, maxLossUsed / maxLossUsd) : fallback.ddPct;

    const dailyMaxUsd = pickPositive(
      objectives?.dailyMaxLoss,
      objectives?.dailyMaxLossUsd,
      objectives?.dailyLoss,
      fallback.dailyMaxUsd,
    ) ?? fallback.dailyMaxUsd;
    const dailyUsed = pickPositive(
      objectives?.dailyMaxLossUsed,
      objectives?.dailyLossUsed,
      objectives?.dailyMaxLossCurrent,
      fallback.dailyUsed,
    ) ?? fallback.dailyUsed;
    const dailyPct = dailyMaxUsd > 0 ? Math.min(1, dailyUsed / dailyMaxUsd) : fallback.dailyPct;

    const minProfitDays = pickPositive(
      objectives?.minProfitableDays,
      objectives?.minProfitDays,
      fallback.minProfitDays,
    ) ?? fallback.minProfitDays;
    const profitableDays = pickPositive(
      objectives?.profitableDays,
      objectives?.profitableDaysCurrent,
      a.profitableDays,
    ) ?? (a.profitableDays || 0);
    const profitDaysPct = minProfitDays > 0 ? Math.min(1, profitableDays / minProfitDays) : 1;

    return {
      ...fallback,
      profit,
      profitTargetUsd,
      profitPct: usedPct,
      maxLossUsd,
      ddSoFar: maxLossUsed,
      ddPct,
      dailyMaxUsd,
      dailyUsed,
      dailyPct,
      profitableDays,
      minProfitDays,
      profitDaysPct,
      live: true,
      payoutEligible: breach?.payoutEligible === true || breach?.eligibleForPayout === true,
      breached: breach?.breached === true || breach?.isBreached === true,
      nextPayoutAt: breach?.nextPayoutAt || breach?.nextPayoutDate || null,
      nextPayoutInDays: pickPositive(breach?.nextPayoutInDays, breach?.daysUntilNextPayout),
    };
  }, [a, objectives, breach, fallback]);
}

// Aggregate floating P/L from bridge open positions (preferred when available)
// otherwise Client_API open positions. Returns 0 when no data is loaded yet.
function sumOpenPnl(rows) {
  if (!Array.isArray(rows)) return 0;
  return rows.reduce((sum, r) => {
    if (!r) return sum;
    const raw = Number(r.profit ?? r.pnl ?? r.pl ?? r.unrealizedPl ?? r.floatingProfit ?? 0);
    if (!Number.isFinite(raw)) return sum;
    return sum + (r.__moneyMajor ? raw : raw / 100);
  }, 0);
}

function useLiveAccountStats(accountOrLogin) {
  const { accounts, liveData } = useApp();
  let account = null;
  if (accountOrLogin && typeof accountOrLogin === 'object') {
    account = accountOrLogin;
  } else if (accountOrLogin !== undefined && accountOrLogin !== null) {
    const key = String(accountOrLogin);
    account = (accounts || []).find(a => String(a.login) === key || String(a.id) === key) || null;
  }
  const login = account?.login;
  const bridge = (login && liveData?.accountBridge?.[login]) || null;
  const margin = bridge?.margin || null;
  const pl = bridge?.pl || null;
  // Prefer bridge equity/margin (already major-units) over Client_API mapped fields.
  const equity = (margin && margin.equity !== null && margin.equity !== undefined)
    ? margin.equity
    : (account ? account.equity : 0);
  const balance = (margin && margin.balance !== null && margin.balance !== undefined)
    ? margin.balance
    : (account ? account.balance : 0);
  const marginUsed = margin?.margin ?? account?.margin ?? null;
  const freeMargin = margin?.freeMargin ?? account?.freeMargin ?? null;
  const marginLevel = margin?.marginLevel ?? null;
  const floatingFromBridge = (margin && margin.floatingPnl !== null && margin.floatingPnl !== undefined)
    ? margin.floatingPnl
    : null;
  const openRows = (bridge?.openPositions?.length ? bridge.openPositions.map(r => ({ ...r, __moneyMajor: true })) : (login && liveData?.accountOpenPositions?.[login]) || []);
  const floatingPnl = floatingFromBridge !== null ? floatingFromBridge : sumOpenPnl(openRows);
  const status = (() => {
    if (!login) return 'unknown';
    if (!liveData?.accountBridge) return 'pending';
    if (!bridge) return liveData?.loadingAccounts?.[login] ? 'loading' : 'pending';
    if (margin && (margin.equity !== null || margin.balance !== null)) return 'live';
    if (bridge?.openPositions?.length || bridge?.deals?.length) return 'live';
    return 'unavailable';
  })();
  return {
    account,
    bridge,
    equity,
    balance,
    marginUsed,
    freeMargin,
    marginLevel,
    floatingPnl,
    today: pl?.daily ?? null,
    week: pl?.weekly ?? null,
    month: pl?.monthly ?? null,
    allTime: pl?.allTime ?? null,
    status,
  };
}

function useAccountTrades(login) {
  const { liveData, loadAccountDetail } = useApp();
  const bridge = liveData.accountBridge[login] || null;
  const trades = (bridge?.deals?.length ? bridge.deals.map(row => ({ ...row, __moneyMajor: true })) : liveData.accountTrades[login]) || null;
  const openPositions = (bridge?.openPositions?.length ? bridge.openPositions.map(row => ({ ...row, __moneyMajor: true })) : liveData.accountOpenPositions[login]) || null;
  const loading = liveData.loadingAccounts[login] || false;

  useEffect(() => {
    if (login && trades === null && !loading) {
      loadAccountDetail(login);
    }
  }, [login]);

  const mappedTrades = useMemo(() => {
    if (!trades) return [];
    return trades.map((t, i) => {
      const symbol = t.symbol || t.instrument || t.pair || '—';
      const side = (t.side || t.type || t.action || '').toUpperCase();
      const normalSide = /buy|long/i.test(side) ? 'BUY' : /sell|short/i.test(side) ? 'SELL' : side;
      const vol = Number(t.volume || t.lots || t.size || 0);
      const entry = Number(t.openPrice || t.entryPrice || t.priceOpen || 0);
      const exit = Number(t.closePrice || t.exitPrice || t.priceClose || 0);
      const rawPl = Number(t.profit ?? t.pnl ?? t.pl ?? t.netProfit ?? 0);
      const pl = t.__moneyMajor ? rawPl : rawPl / 100;
      const rawDate = t.closeTime || t.closedAt || t.date || t.openTime || '';
      const dateStr = rawDate ? new Date(rawDate).toLocaleDateString('en-US', { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }) : '—';
      return {
        id: t.id || t.ticket || `t-${i}`,
        d: dateStr,
        s: symbol,
        side: normalSide,
        vol,
        e: entry,
        x: exit,
        pl,
        dur: '',
        pip: 0,
        rr: 0,
        tag: '',
        raw: t,
      };
    });
  }, [trades]);

  const mappedOpen = useMemo(() => {
    if (!openPositions) return [];
    return openPositions.map((t, i) => {
      const symbol = t.symbol || t.instrument || t.pair || '—';
      const side = (t.side || t.type || t.action || '').toUpperCase();
      const normalSide = /buy|long/i.test(side) ? 'BUY' : /sell|short/i.test(side) ? 'SELL' : side;
      const vol = Number(t.volume || t.lots || t.size || 0);
      const entry = Number(t.openPrice || t.entryPrice || t.priceOpen || 0);
      const rawPl = Number(t.profit ?? t.pnl ?? t.pl ?? t.unrealizedPl ?? 0);
      const pl = t.__moneyMajor ? rawPl : rawPl / 100;
      return {
        id: t.id || t.ticket || `op-${i}`,
        s: symbol,
        side: normalSide,
        vol,
        e: entry,
        pl,
        raw: t,
      };
    });
  }, [openPositions]);

  return { trades: mappedTrades, openPositions: mappedOpen, bridge, loading, raw: trades };
}

function LiveEmptyState({ icon, title, sub }) {
  const Ic = I[icon] || I.info;
  return (
    <div style={{ padding: '36px 20px', textAlign: 'center' }}>
      <Ic size={28} style={{ color: 'var(--text-faint)', marginBottom: 10 }}/>
      <div style={{ fontSize: 14, fontWeight: 600, color: 'var(--text-dim)' }}>{title}</div>
      {sub && <div style={{ fontSize: 12, color: 'var(--text-faint)', marginTop: 4 }}>{sub}</div>}
    </div>
  );
}

function LiveLoadingState({ text }) {
  return (
    <div style={{ padding: '30px 20px', textAlign: 'center' }}>
      <div style={{ width: 32, height: 32, borderRadius: 999, border: '2px solid var(--line)', borderTopColor: 'var(--magenta)', margin: '0 auto', animation: 'spin 0.8s linear infinite' }}/>
      {text && <div style={{ marginTop: 10, fontSize: 12, color: 'var(--text-dim)' }}>{text}</div>}
      <style>{`@keyframes spin { to { transform: rotate(360deg); } }`}</style>
    </div>
  );
}

function LiveComingSoon({ label }) {
  return (
    <div style={{ padding: '24px 16px', textAlign: 'center', background: 'rgba(255,255,255,0.02)', borderRadius: 12, border: '1px dashed var(--line-3)' }}>
      <I.clock size={18} style={{ color: 'var(--text-faint)', marginBottom: 6 }}/>
      <div style={{ fontSize: 12, color: 'var(--text-dim)' }}>{label || 'Coming soon'}</div>
    </div>
  );
}

// Bridge status pill — visual hook used by account cards/rows. status is one of
// 'live' | 'loading' | 'pending' | 'unavailable' | 'unknown'.
function BridgeStatusPill({ status, compact }) {
  const tone =
    status === 'live'        ? { bg: 'color-mix(in oklab, var(--positive) 14%, transparent)', fg: 'var(--positive)', label: compact ? 'LIVE' : 'BRIDGE LIVE' } :
    status === 'loading'     ? { bg: 'color-mix(in oklab, var(--cyan) 12%, transparent)',     fg: 'var(--cyan)',     label: compact ? '…'    : 'BRIDGE LOADING' } :
    status === 'unavailable' ? { bg: 'color-mix(in oklab, var(--negative) 14%, transparent)', fg: 'var(--negative)', label: compact ? 'OFF'  : 'BRIDGE UNAVAILABLE' } :
                                { bg: 'rgba(255,255,255,0.05)',                                fg: 'var(--text-dim)', label: compact ? '—'   : 'BRIDGE PENDING' };
  return (
    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 4, fontSize: 9, fontWeight: 700, letterSpacing: '0.06em', padding: '3px 7px', borderRadius: 999, background: tone.bg, color: tone.fg }}>
      {tone.label}
    </span>
  );
}

Object.assign(window, {
  useLiveUser, useLiveWallet, useLiveMessages, useLiveCountries, useAccountTrades,
  useAccountScope, setStoredAccountScope, accountScopeForAccount, filterAccountsByScope,
  useAccountObjectives, useAccountBreach, useAccountData, useLiveAccountProgress, useLiveAccountStats,
  sumOpenPnl,
  LiveEmptyState, LiveLoadingState, LiveComingSoon, BridgeStatusPill,
});
