import type { CSSProperties } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { useLocalStorageState } from './app/hooks/useLocalStorageState';
import AppShell from './layout/AppShell';
import ChartPanel from './features/chart/ChartPanel';
import { useChartData } from './features/chart/useChartData';
import TickerBar from './features/tickerbar/TickerBar';
import Card from './ui/Card';
import Tabs from './ui/Tabs';
import MarketHeader from './features/market/MarketHeader';
import Button from './ui/Button';
import TopNav from './layout/TopNav';
import AuthStatus from './layout/AuthStatus';
import LoginScreen from './layout/LoginScreen';
import { useDlobStats } from './features/market/useDlobStats';
import { useDlobL2 } from './features/market/useDlobL2';
import { useDlobSlippage } from './features/market/useDlobSlippage';
import { useDlobDepthBands } from './features/market/useDlobDepthBands';
import DlobDashboard from './features/market/DlobDashboard';

function envNumber(name: string, fallback: number): number {
  const v = (import.meta as any).env?.[name];
  if (v == null) return fallback;
  const n = Number(v);
  return Number.isFinite(n) ? n : fallback;
}

function envString(name: string, fallback: string): string {
  const v = (import.meta as any).env?.[name];
  return v == null ? fallback : String(v);
}

function formatUsd(v: number | null | undefined): string {
  if (v == null || !Number.isFinite(v)) return '—';
  if (v >= 1000) return `$${v.toFixed(0)}`;
  if (v >= 1) return `$${v.toFixed(2)}`;
  return `$${v.toPrecision(4)}`;
}

function formatQty(v: number | null | undefined, decimals: number): string {
  if (v == null || !Number.isFinite(v)) return '—';
  return v.toLocaleString(undefined, { minimumFractionDigits: decimals, maximumFractionDigits: decimals });
}

function orderbookBarStyle(scale: number): CSSProperties {
  const s = Number.isFinite(scale) && scale > 0 ? Math.min(1, scale) : 0;
  return { ['--ob-bar-scale' as any]: s } as CSSProperties;
}

type WhoamiResponse = {
  ok?: boolean;
  user?: string | null;
  mode?: string;
};

export default function App() {
  const [user, setUser] = useState<string | null>(null);
  const [authLoading, setAuthLoading] = useState(true);

  useEffect(() => {
    let cancelled = false;
    setAuthLoading(true);
    fetch('/whoami', { cache: 'no-store' })
      .then(async (res) => {
        const json = (await res.json().catch(() => null)) as WhoamiResponse | null;
        const u = typeof json?.user === 'string' ? json.user.trim() : '';
        return u || null;
      })
      .then((u) => {
        if (cancelled) return;
        setUser(u);
      })
      .catch(() => {
        if (cancelled) return;
        setUser(null);
      })
      .finally(() => {
        if (cancelled) return;
        setAuthLoading(false);
      });

    return () => {
      cancelled = true;
    };
  }, []);

  const logout = async () => {
    try {
      await fetch('/auth/logout', { method: 'POST' });
    } finally {
      setUser(null);
    }
  };

  if (authLoading) {
    return (
      <div className="loginScreen">
        <div className="loginCard" role="status" aria-label="Ładowanie">
          Ładowanie…
        </div>
      </div>
    );
  }

  if (!user) {
    return <LoginScreen onLoggedIn={setUser} />;
  }

  return <TradeApp user={user} onLogout={() => void logout()} />;
}

function TradeApp({ user, onLogout }: { user: string; onLogout: () => void }) {
  const markets = useMemo(() => ['PUMP-PERP', 'SOL-PERP', '1MBONK-PERP', 'BTC-PERP', 'ETH-PERP'], []);

  const [symbol, setSymbol] = useLocalStorageState('trade.symbol', envString('VITE_SYMBOL', 'SOL-PERP'));
  const [source, setSource] = useLocalStorageState('trade.source', envString('VITE_SOURCE', ''));
  const [tf, setTf] = useLocalStorageState('trade.tf', envString('VITE_TF', '1m'));
  const [pollMs, setPollMs] = useLocalStorageState('trade.pollMs', envNumber('VITE_POLL_MS', 1000));
  const [limit, setLimit] = useLocalStorageState('trade.limit', envNumber('VITE_LIMIT', 300));
  const [showIndicators, setShowIndicators] = useLocalStorageState('trade.showIndicators', true);
  const [showBuild, setShowBuild] = useLocalStorageState('trade.showBuild', false);
  const [tab, setTab] = useLocalStorageState<'orderbook' | 'trades'>('trade.sidebarTab', 'orderbook');
  const [bottomTab, setBottomTab] = useLocalStorageState<
    'dlob' | 'positions' | 'orders' | 'balances' | 'orderHistory' | 'positionHistory'
  >('trade.bottomTab', 'positions');
  const [tradeSide, setTradeSide] = useLocalStorageState<'long' | 'short'>('trade.form.side', 'long');
  const [tradeOrderType, setTradeOrderType] = useLocalStorageState<'market' | 'limit' | 'other'>(
    'trade.form.type',
    'market'
  );
  const [tradePrice, setTradePrice] = useLocalStorageState<number>('trade.form.price', 0);
  const [tradeSize, setTradeSize] = useLocalStorageState<number>('trade.form.size', 0.1);

  useEffect(() => {
    if (symbol === 'BONK-PERP') {
      setSymbol('1MBONK-PERP');
      return;
    }
    if (!markets.includes(symbol)) {
      setSymbol('SOL-PERP');
    }
  }, [markets, setSymbol, symbol]);

  const { candles, indicators, meta, loading, error, refresh } = useChartData({
    symbol,
    source: source.trim() ? source : undefined,
    tf,
    limit,
    pollMs,
  });

  const { stats: dlob, connected: dlobConnected, error: dlobError } = useDlobStats(symbol);
  const { l2: dlobL2, connected: dlobL2Connected, error: dlobL2Error } = useDlobL2(symbol, { levels: 14 });
  const { rows: slippageRows, connected: slippageConnected, error: slippageError } = useDlobSlippage(symbol);
  const { rows: depthBands, connected: depthBandsConnected, error: depthBandsError } = useDlobDepthBands(symbol);

  const latest = candles.length ? candles[candles.length - 1] : null;
  const first = candles.length ? candles[0] : null;
  const changePct =
    first && latest && first.close > 0 ? ((latest.close - first.close) / first.close) * 100 : null;

  const orderbook = useMemo(() => {
    if (dlobL2) return { asks: dlobL2.asks, bids: dlobL2.bids, mid: dlobL2.mid as number | null };
    if (!latest) return { asks: [], bids: [], mid: null as number | null };
    const mid = latest.close;
    const step = Math.max(mid * 0.00018, 0.0001);
    const levels = 14;

    const asksRaw = Array.from({ length: levels }, (_, i) => ({
      price: mid + (i + 1) * step,
      size: 0.1 + ((i * 7) % 15) * 0.1,
    }));
    const bidsRaw = Array.from({ length: levels }, (_, i) => ({
      price: mid - (i + 1) * step,
      size: 0.1 + ((i * 5) % 15) * 0.1,
    }));

    let askTotal = 0;
    const asks = asksRaw
      .slice()
      .reverse()
      .map((r) => {
        askTotal += r.size;
        return { ...r, total: askTotal };
      });

    let bidTotal = 0;
    const bids = bidsRaw.map((r) => {
      bidTotal += r.size;
      return { ...r, total: bidTotal };
    });

    return { asks, bids, mid };
  }, [dlobL2, latest]);

  const maxAskTotal = useMemo(() => {
    let max = 0;
    for (const r of orderbook.asks) max = Math.max(max, r.total || 0);
    return max;
  }, [orderbook.asks]);

  const maxBidTotal = useMemo(() => {
    let max = 0;
    for (const r of orderbook.bids) max = Math.max(max, r.total || 0);
    return max;
  }, [orderbook.bids]);

  const trades = useMemo(() => {
    const slice = candles.slice(-24).reverse();
    return slice.map((c) => {
      const isBuy = c.close >= c.open;
      return {
        time: c.time,
        price: c.close,
        size: c.volume ?? null,
        side: isBuy ? ('buy' as const) : ('sell' as const),
      };
    });
  }, [candles]);

  const effectiveTradePrice = useMemo(() => {
    if (tradeOrderType === 'limit') return tradePrice;
    return latest?.close ?? tradePrice;
  }, [latest?.close, tradeOrderType, tradePrice]);

  const orderValueUsd = useMemo(() => {
    if (!Number.isFinite(tradeSize) || tradeSize <= 0) return null;
    if (!Number.isFinite(effectiveTradePrice) || effectiveTradePrice <= 0) return null;
    const v = effectiveTradePrice * tradeSize;
    return Number.isFinite(v) && v > 0 ? v : null;
  }, [effectiveTradePrice, tradeSize]);

  const dynamicSlippage = useMemo(() => {
    if (orderValueUsd == null) return null;
    const side = tradeSide === 'short' ? 'sell' : 'buy';
    const rows = slippageRows.filter((r) => r.side === side).slice();
    rows.sort((a, b) => a.sizeUsd - b.sizeUsd);
    if (!rows.length) return null;
    const biggest = rows[rows.length - 1];
    const match = rows.find((r) => r.sizeUsd >= orderValueUsd) || biggest;
    return match;
  }, [orderValueUsd, slippageRows, tradeSide]);

  const topItems = useMemo(
    () => [
      { key: 'BTC', label: 'BTC', changePct: 1.28, active: false },
      { key: 'SOL', label: 'SOL', changePct: 1.89, active: false },
    ],
    []
  );

  const stats = useMemo(() => {
    return [
      {
        key: 'last',
        label: 'Last',
        value: formatUsd(latest?.close),
        sub:
          changePct == null ? (
            '—'
          ) : (
            <span className={changePct >= 0 ? 'pos' : 'neg'}>
              {changePct >= 0 ? '+' : ''}
              {changePct.toFixed(2)}%
            </span>
          ),
      },
      { key: 'oracle', label: 'Oracle', value: formatUsd(latest?.oracle ?? null) },
      { key: 'bid', label: 'Bid', value: formatUsd(dlob?.bestBid ?? null) },
      { key: 'ask', label: 'Ask', value: formatUsd(dlob?.bestAsk ?? null) },
      {
        key: 'spread',
        label: 'Spread',
        value: dlob?.spreadBps == null ? '—' : `${dlob.spreadBps.toFixed(1)} bps`,
        sub: formatUsd(dlob?.spreadAbs ?? null),
      },
      {
        key: 'dlob',
        label: 'DLOB',
        value: dlobConnected ? 'live' : '—',
        sub: dlobError ? <span className="neg">{dlobError}</span> : dlob?.updatedAt || '—',
      },
      {
        key: 'l2',
        label: 'L2',
        value: dlobL2Connected ? 'live' : '—',
        sub: dlobL2Error ? <span className="neg">{dlobL2Error}</span> : dlobL2?.updatedAt || '—',
      },
    ];
  }, [latest?.close, latest?.oracle, changePct, dlob, dlobConnected, dlobError, dlobL2, dlobL2Connected, dlobL2Error]);

  const seriesLabel = useMemo(() => `Candles: Mark (oracle overlay)`, []);
  const seriesKey = useMemo(() => `${symbol}|${source}|${tf}`, [symbol, source, tf]);
  const bucketSeconds = meta?.bucketSeconds ?? 60;

  return (
    <AppShell
      header={<TopNav active="trade" rightEndSlot={<AuthStatus user={user} onLogout={onLogout} />} />}
      top={<TickerBar items={topItems} />}
      main={
        <div className="tradeMain">
          <Card
            className="marketCard"
            title={
              <MarketHeader
                market={symbol}
                markets={markets}
                onMarketChange={setSymbol}
                leftSlot={
                  <label className="inlineField">
                    <span className="inlineField__label">Source</span>
                    <input
                      className="inlineField__input"
                      value={source}
                      onChange={(e) => setSource(e.target.value)}
                      placeholder="(any)"
                    />
                  </label>
                }
                stats={stats}
                rightSlot={
                  <div className="marketHeader__actions">
                    <label className="inlineField">
                      <span className="inlineField__label">Poll</span>
                      <input
                        className="inlineField__input"
                        value={pollMs}
                        type="number"
                        min={250}
                        step={250}
                        onChange={(e) => setPollMs(Number(e.target.value))}
                      />
                    </label>
                    <label className="inlineField">
                      <span className="inlineField__label">Limit</span>
                      <input
                        className="inlineField__input"
                        value={limit}
                        type="number"
                        min={50}
                        step={50}
                        onChange={(e) => setLimit(Number(e.target.value))}
                      />
                    </label>
                    <Button onClick={() => void refresh()} disabled={loading} type="button">
                      Refresh
                    </Button>
                  </div>
                }
              />
            }
          >
            {error ? <div className="uiError">{error}</div> : null}
          </Card>

          <ChartPanel
            candles={candles}
            indicators={indicators}
            timeframe={tf}
            bucketSeconds={bucketSeconds}
            seriesKey={seriesKey}
            onTimeframeChange={setTf}
            showIndicators={showIndicators}
            onToggleIndicators={() => setShowIndicators((v) => !v)}
            showBuild={showBuild}
            onToggleBuild={() => setShowBuild((v) => !v)}
            seriesLabel={seriesLabel}
            dlobQuotes={{ bid: dlob?.bestBid ?? null, ask: dlob?.bestAsk ?? null, mid: dlob?.mid ?? null }}
          />

          <Card className="bottomCard">
            <Tabs
              items={[
                {
                  id: 'dlob',
                  label: 'DLOB',
                  content: (
                    <DlobDashboard
                      market={symbol}
                      stats={dlob}
                      statsConnected={dlobConnected}
                      statsError={dlobError}
                      depthBands={depthBands}
                      depthBandsConnected={depthBandsConnected}
                      depthBandsError={depthBandsError}
                      slippageRows={slippageRows}
                      slippageConnected={slippageConnected}
                      slippageError={slippageError}
                    />
                  ),
                },
                { id: 'positions', label: 'Positions', content: <div className="placeholder">Positions (next)</div> },
                { id: 'orders', label: 'Orders', content: <div className="placeholder">Orders (next)</div> },
                { id: 'balances', label: 'Balances', content: <div className="placeholder">Balances (next)</div> },
                {
                  id: 'orderHistory',
                  label: 'Order History',
                  content: <div className="placeholder">Order history (next)</div>,
                },
                {
                  id: 'positionHistory',
                  label: 'Position History',
                  content: <div className="placeholder">Position history (next)</div>,
                },
              ]}
              activeId={bottomTab}
              onChange={setBottomTab}
            />
          </Card>
        </div>
      }
      sidebar={
        <Card
          className="orderbookCard"
          title={
            <div className="sideHead">
              <div className="sideHead__title">Orderbook</div>
              <div className="sideHead__subtitle">{loading ? 'loading…' : orderbook.mid != null ? formatUsd(orderbook.mid) : latest ? formatUsd(latest.close) : '—'}</div>
            </div>
          }
        >
          <Tabs
            items={[
              {
                id: 'orderbook',
                label: 'Orderbook',
                content: (
                  <div className="orderbook">
                    <div className="orderbook__header">
                      <span>Price</span>
                      <span className="orderbook__num">Size</span>
                      <span className="orderbook__num">Total</span>
                    </div>
                    <div className="orderbook__rows">
                      {orderbook.asks.map((r) => (
                        <div
                          key={`a-${r.price}`}
                          className="orderbookRow orderbookRow--ask"
                          style={orderbookBarStyle(maxAskTotal > 0 ? r.total / maxAskTotal : 0)}
                        >
                          <span className="orderbookRow__price">{formatQty(r.price, 3)}</span>
                          <span className="orderbookRow__num">{formatQty(r.size, 2)}</span>
                          <span className="orderbookRow__num">{formatQty(r.total, 2)}</span>
                        </div>
                      ))}
                      <div className="orderbookMid">
                        <span className="orderbookMid__price">{formatQty(orderbook.mid, 3)}</span>
                        <span className="orderbookMid__label">mid</span>
                      </div>
                      {orderbook.bids.map((r) => (
                        <div
                          key={`b-${r.price}`}
                          className="orderbookRow orderbookRow--bid"
                          style={orderbookBarStyle(maxBidTotal > 0 ? r.total / maxBidTotal : 0)}
                        >
                          <span className="orderbookRow__price">{formatQty(r.price, 3)}</span>
                          <span className="orderbookRow__num">{formatQty(r.size, 2)}</span>
                          <span className="orderbookRow__num">{formatQty(r.total, 2)}</span>
                        </div>
                      ))}
                    </div>
                  </div>
                ),
              },
              {
                id: 'trades',
                label: 'Recent Trades',
                content: (
                  <div className="trades">
                    <div className="trades__header">
                      <span>Time</span>
                      <span className="trades__num">Price</span>
                      <span className="trades__num">Size</span>
                    </div>
                    <div className="trades__rows">
                      {trades.map((t) => (
                        <div key={`${t.time}-${t.price}`} className="tradeRow">
                          <span className="tradeRow__time">
                            {new Date(t.time * 1000).toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit', second: '2-digit' })}
                          </span>
                          <span className={['tradeRow__price', t.side === 'buy' ? 'pos' : 'neg'].join(' ')}>
                            {formatQty(t.price, 3)}
                          </span>
                          <span className="tradeRow__num">{t.size == null ? '—' : formatQty(t.size, 2)}</span>
                        </div>
                      ))}
                    </div>
                  </div>
                ),
              },
            ]}
            activeId={tab}
            onChange={setTab}
          />
        </Card>
      }
      rightbar={
        <Card
          className="tradeFormCard"
          title={
            <div className="tradeFormHead">
              <div className="tradeFormHead__left">
                <button className="chipBtn" type="button">
                  Cross
                </button>
                <button className="chipBtn" type="button">
                  20x
                </button>
              </div>
              <div className="tradeFormHead__right">{symbol}</div>
            </div>
          }
        >
          <div className="tradeForm">
            <div className="segmented">
              <button
                className={['segmented__btn', tradeSide === 'long' ? 'segmented__btn--activeLong' : ''].filter(Boolean).join(' ')}
                type="button"
                onClick={() => setTradeSide('long')}
              >
                Long
              </button>
              <button
                className={['segmented__btn', tradeSide === 'short' ? 'segmented__btn--activeShort' : ''].filter(Boolean).join(' ')}
                type="button"
                onClick={() => setTradeSide('short')}
              >
                Short
              </button>
            </div>

            <div className="tradeTabs">
              <button
                type="button"
                className={['tradeTabs__btn', tradeOrderType === 'market' ? 'tradeTabs__btn--active' : ''].filter(Boolean).join(' ')}
                onClick={() => setTradeOrderType('market')}
              >
                Market
              </button>
              <button
                type="button"
                className={['tradeTabs__btn', tradeOrderType === 'limit' ? 'tradeTabs__btn--active' : ''].filter(Boolean).join(' ')}
                onClick={() => setTradeOrderType('limit')}
              >
                Limit
              </button>
              <button
                type="button"
                className={['tradeTabs__btn', tradeOrderType === 'other' ? 'tradeTabs__btn--active' : ''].filter(Boolean).join(' ')}
                onClick={() => setTradeOrderType('other')}
              >
                Others
              </button>
            </div>

            <div className="tradeFields">
              <label className="formField">
                <span className="formField__label">Price</span>
                <input
                  className="formField__input"
                  value={tradeOrderType === 'market' ? '' : String(tradePrice)}
                  placeholder={tradeOrderType === 'market' ? formatQty(latest?.close ?? null, 3) : '0'}
                  disabled={tradeOrderType !== 'limit'}
                  onChange={(e) => setTradePrice(Number(e.target.value))}
                  inputMode="decimal"
                />
              </label>
              <label className="formField">
                <span className="formField__label">Size</span>
                <input
                  className="formField__input"
                  value={String(tradeSize)}
                  onChange={(e) => setTradeSize(Number(e.target.value))}
                  inputMode="decimal"
                />
              </label>
            </div>

            <Button className="tradeCta" type="button" disabled>
              Enable Trading
            </Button>

            <div className="tradeMeta">
              <div className="tradeMeta__row">
                <span className="tradeMeta__label">Order Value</span>
                <span className="tradeMeta__value">{effectiveTradePrice ? formatUsd(effectiveTradePrice * tradeSize) : '—'}</span>
              </div>
              <div className="tradeMeta__row">
                <span className="tradeMeta__label">Slippage (Dynamic)</span>
                <span className="tradeMeta__value">
                  {slippageError ? (
                    <span className="neg">{slippageError}</span>
                  ) : dynamicSlippage?.impactBps == null ? (
                    slippageConnected ? (
                      '—'
                    ) : (
                      'offline'
                    )
                  ) : (
                    <>
                      {dynamicSlippage.impactBps.toFixed(1)} bps{' '}
                      <span className="muted">
                        ({dynamicSlippage.sizeUsd.toLocaleString()} USD)
                        {dynamicSlippage.fillPct != null && dynamicSlippage.fillPct < 99.9
                          ? `, ${dynamicSlippage.fillPct.toFixed(0)}% fill`
                          : ''}
                      </span>
                    </>
                  )}
                </span>
              </div>
              <div className="tradeMeta__row">
                <span className="tradeMeta__label">Margin Required</span>
                <span className="tradeMeta__value">—</span>
              </div>
              <div className="tradeMeta__row">
                <span className="tradeMeta__label">Liq. Price</span>
                <span className="tradeMeta__value">—</span>
              </div>
            </div>
          </div>
        </Card>
      }
    />
  );
}
