/**
 * 가상투자 대시보드 — 모의투자 레이아웃 기반, 다종목 전략 모니터링
 */
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import {
  loadPaperSummary,
  loadPaperTrades,
  loadStrategies,
  saveLiveStrategySettings,
  pinCandleWatch,
  loadLiveStrategySettings,
  type PaperSummaryDto,
  type PaperTradeDto,
  type StrategyDto,
} from '../utils/backendApi';
import type { Theme, TradeOrderFillRequest } from '../types';
import type { TickerData } from '../hooks/useMarketTicker';
import { TradeSplitOrderPanel, TradeOrderbookPanelContent, TradeRightPanelBody } from './trade';
import { useUpbitOrderbook } from '../hooks/useUpbitOrderbook';
import { useUpbitRecentTrades } from '../hooks/useUpbitRecentTrades';
import PaperTradeHistoryList from './paper/PaperTradeHistoryList';
import BuilderPageShell from './layout/BuilderPageShell';
import VirtualLeftTargetPanel from './virtual/VirtualLeftTargetPanel';
import VirtualTargetGrid from './virtual/VirtualTargetGrid';
import {
  VirtualSessionHeaderControls,
  VirtualViewHeaderControls,
} from './virtual/VirtualGridUnifiedHeader';
import type { VirtualCardDisplayMode } from './virtual/VirtualTargetCard';
import { useVirtualIndicatorSnapshots } from '../hooks/useVirtualIndicatorSnapshots';
import { PAPER_TRADES_CHANGED_EVENT } from '../utils/paperTradeEvents';
import { useVirtualTargetLiveStatus } from '../hooks/useVirtualTargetLiveStatus';
import type { ChartRealtimeSource } from '../hooks/useChartRealtimeData';
import {
  loadVirtualSession,
  loadVirtualTargets,
  saveVirtualSession,
  saveVirtualTargets,
  loadVirtualCardViewMode,
  saveVirtualCardViewMode,
  loadVirtualGridPreset,
  saveVirtualGridPreset,
  type VirtualSessionConfig,
  type VirtualTargetItem,
  type VirtualCardViewMode,
} from '../utils/virtualTradingStorage';
import { useAppSettings, resolveAppDefaults } from '../hooks/useAppSettings';
import { coerceFiniteNumber } from '../utils/safeFormat';
import type { TradeNotificationGridPresetId } from '../utils/tradeNotificationGridPresets';
import {
  syncVirtualTargetsToBackend,
  stopVirtualLiveOnBackend,
} from '../utils/virtualLiveStrategySync';
import {
  syncStrategyTimeframesFromLayoutIfNeeded,
  warnStrategyTimeframeMismatch,
} from '../utils/strategyTimeframeSync';
import { pinStrategyEvaluationTimeframes } from '../utils/strategyTimeframePin';
import { persistVirtualTargetPinned } from '../utils/virtualTargetMutations';
import {
  freezeTargetsBeforeGlobalStrategyChange,
  resolveVirtualTargetStrategyId,
} from '../utils/virtualTargetStrategy';
import { virtualTargetLimitMessage } from '../utils/virtualTargetLimits';
import '../styles/virtualTradingDashboard.css';

type RightTab = 'trade' | 'orderbook' | 'history';

interface Props {
  theme?: Theme;
  tickers?: Map<string, TickerData>;
  defaultMarket?: string;
  paperTradingEnabled?: boolean;
  paperAutoTradeEnabled?: boolean;
  paperAutoTradeBudgetPct?: number;
  onPaperAutoTradeEnabled?: (enabled: boolean) => void;
  onPaperOrderFilled?: () => void;
}

const VirtualTradingPage: React.FC<Props> = ({
  theme = 'dark',
  tickers,
  defaultMarket = 'KRW-BTC',
  paperTradingEnabled = true,
  paperAutoTradeEnabled = false,
  paperAutoTradeBudgetPct = 95,
  onPaperAutoTradeEnabled,
  onPaperOrderFilled,
}) => {
  const [targets, setTargets] = useState<VirtualTargetItem[]>(() => loadVirtualTargets());
  const [session, setSession] = useState<VirtualSessionConfig>(() => loadVirtualSession());
  const [strategies, setStrategies] = useState<StrategyDto[]>([]);
  const [summary, setSummary] = useState<PaperSummaryDto | null>(null);
  const [trades, setTrades] = useState<PaperTradeDto[]>([]);
  const [loading, setLoading] = useState(true);
  const [selectedMarket, setSelectedMarket] = useState(defaultMarket);
  const [rightTab, setRightTab] = useState<RightTab>('trade');
  const [fillBuy, setFillBuy] = useState<TradeOrderFillRequest | null>(null);
  const [fillSell, setFillSell] = useState<TradeOrderFillRequest | null>(null);
  const [viewMode, setViewMode] = useState<VirtualCardViewMode>(() => loadVirtualCardViewMode());
  const [gridPreset, setGridPreset] = useState<TradeNotificationGridPresetId>(() => loadVirtualGridPreset());
  const [globalDisplayMode, setGlobalDisplayMode] = useState<VirtualCardDisplayMode>('signal');
  const [focusMarket, setFocusMarket] = useState<string | null>(null);
  const orderAnchorRef = useRef<HTMLDivElement>(null);
  const { settings: appSettings } = useAppSettings();
  const appChartDefaults = resolveAppDefaults(appSettings);
  const chartRealtimeSource = (appChartDefaults.chartRealtimeSource
    ?? 'BACKEND_STOMP') as ChartRealtimeSource;
  const chartCandleAreaPriceLabels = appChartDefaults.chartCandleAreaPriceLabels;
  const chartSeriesPriceLabels = appChartDefaults.chartSeriesPriceLabels;
  const chartPaneSeparator = appChartDefaults.chartPaneSeparator;
  const chartLiveReceiveHighlight = appChartDefaults.chartLiveReceiveHighlight;
  const virtualTargetMaxCount = appChartDefaults.virtualTargetMaxCount;

  useEffect(() => {
    void Promise.all([
      loadStrategies()
        .then(list => {
          setStrategies(list);
          const validIds = new Set(
            list.map(s => s.id).filter((id): id is number => id != null && id > 0),
          );
          setSession(prev =>
            prev.globalStrategyId != null && !validIds.has(prev.globalStrategyId)
              ? { ...prev, globalStrategyId: null }
              : prev,
          );
          setTargets(prev =>
            prev.map(t =>
              t.strategyId != null && !validIds.has(t.strategyId)
                ? { ...t, strategyId: null }
                : t,
            ),
          );
        })
        .catch(() => setStrategies([])),
      loadPaperSummary().then(setSummary),
      loadPaperTrades().then(setTrades),
    ]).finally(() => setLoading(false));
  }, []);

  /** DB is_pinned → localStorage 투자대상 목록 동기화 (최초 1회) */
  useEffect(() => {
    let cancelled = false;
    const initial = loadVirtualTargets();
    if (initial.length === 0) return;
    void (async () => {
      const merged = await Promise.all(
        initial.map(async t => {
          try {
            const s = await loadLiveStrategySettings(t.market);
            const pinned = !!s.isPinned;
            if (!!t.pinned === pinned) return t;
            return { ...t, pinned };
          } catch {
            return t;
          }
        }),
      );
      if (!cancelled) setTargets(merged);
    })();
    return () => { cancelled = true; };
  }, []);

  useEffect(() => { saveVirtualTargets(targets); }, [targets]);
  useEffect(() => { saveVirtualSession(session); }, [session]);

  useEffect(() => { saveVirtualCardViewMode(viewMode); }, [viewMode]);
  useEffect(() => { saveVirtualGridPreset(gridPreset); }, [gridPreset]);

  const strategyNames = useMemo(
    () => Object.fromEntries(strategies.map(s => [s.id, s.name])),
    [strategies],
  );

  const refreshPaperData = useCallback(async (opts?: { focusHistory?: boolean }) => {
    try {
      const [sum, tr] = await Promise.all([loadPaperSummary(), loadPaperTrades()]);
      setSummary(sum);
      setTrades(tr);
    } catch { /* ignore */ }
    onPaperOrderFilled?.();
    if (opts?.focusHistory) setRightTab('history');
  }, [onPaperOrderFilled]);

  const handleGlobalDisplayModeChange = useCallback((mode: VirtualCardDisplayMode) => {
    setGlobalDisplayMode(mode);
  }, []);

  const handleGridPresetChange = useCallback((preset: TradeNotificationGridPresetId) => {
    setGridPreset(preset);
  }, []);

  const targetRefs = useMemo(() =>
    targets.map(t => ({
      market: t.market,
      strategyId: resolveVirtualTargetStrategyId(t, session.globalStrategyId),
    })),
  [targets, session.globalStrategyId]);

  const { snapshots, loadingByMarket: snapshotLoadingByMarket } = useVirtualIndicatorSnapshots(
    targetRefs,
    strategies,
    session.running,
  );
  const { statusByMarket: liveStatusByMarket, lastTickAtByMarket } = useVirtualTargetLiveStatus(
    targetRefs,
    session.running,
  );

  /** 백엔드 자동 체결 후 UI 갱신(프론트는 주문 실행 안 함) */
  useEffect(() => {
    if (!session.running) return;
    const onTradesChanged = () => { void refreshPaperData(); };
    window.addEventListener(PAPER_TRADES_CHANGED_EVENT, onTradesChanged);
    const pollId = window.setInterval(onTradesChanged, 20_000);
    return () => {
      window.removeEventListener(PAPER_TRADES_CHANGED_EVENT, onTradesChanged);
      window.clearInterval(pollId);
    };
  }, [session.running, refreshPaperData]);

  const mergedLiveStatus = useMemo(() => {
    if (!session.running) return liveStatusByMarket;
    const merged = { ...liveStatusByMarket };
    const now = Date.now();
    for (const [market, snap] of Object.entries(snapshots)) {
      if (snap?.updatedAt && now - snap.updatedAt < 8000) {
        merged[market] = 'live';
      }
    }
    return merged;
  }, [liveStatusByMarket, snapshots, session.running]);

  const posQty = useMemo(() => {
    const p = summary?.positions?.find(x => x.symbol === selectedMarket);
    return coerceFiniteNumber(p?.quantity) ?? 0;
  }, [summary?.positions, selectedMarket]);

  const tradePrice = useMemo(
    () => coerceFiniteNumber(tickers?.get(selectedMarket)?.tradePrice),
    [tickers, selectedMarket],
  );

  const { orderbook, wsStatus: obWsStatus, spread } = useUpbitOrderbook(selectedMarket);
  const { trades: recentTrades, strength: tradeStrength } = useUpbitRecentTrades(selectedMarket);
  const selectedTicker = tickers?.get(selectedMarket);
  const orderbookPrevClose = selectedTicker
    ? (selectedTicker.tradePrice ?? 0) - (selectedTicker.changePrice ?? 0)
    : 0;
  const orderbookTickerInfo = selectedTicker ? {
    tradePrice: selectedTicker.tradePrice,
    changeRate: selectedTicker.changeRate,
    changePrice: selectedTicker.changePrice,
    accTradePrice24: selectedTicker.accTradePrice24,
    accTradeVolume24: selectedTicker.accTradeVolume24,
    highPrice: selectedTicker.highPrice,
    lowPrice: selectedTicker.lowPrice,
    openingPrice: selectedTicker.openingPrice,
  } : undefined;

  const handleSelectMarket = useCallback((market: string) => {
    setSelectedMarket(market);
    const price = tickers?.get(market)?.tradePrice;
    if (price != null && price > 0) {
      const seq = Date.now();
      setFillBuy({ market, price, side: 'buy', seq });
      setFillSell({ market, price, side: 'sell', seq: seq + 1 });
    }
    setRightTab('trade');
  }, [tickers]);

  const handleTargetsChange = useCallback((items: VirtualTargetItem[]) => {
    if (items.length > virtualTargetMaxCount) {
      window.alert(virtualTargetLimitMessage(virtualTargetMaxCount));
      return;
    }
    setTargets(items);
    if (items.length > 0 && !items.some(t => t.market === selectedMarket)) {
      handleSelectMarket(items[0].market);
    }
  }, [selectedMarket, handleSelectMarket, virtualTargetMaxCount]);

  const handleTogglePin = useCallback((market: string) => {
    setTargets(prev => {
      const item = prev.find(t => t.market === market);
      if (!item) return prev;
      const pinned = !item.pinned;
      void persistVirtualTargetPinned(market, pinned).catch(() => {
        window.alert('고정 설정 저장에 실패했습니다.');
      });
      return prev.map(t => (t.market === market ? { ...t, pinned } : t));
    });
  }, []);

  /** 상단 기본전략 — 신규 추가 종목 템플릿만 변경, 기존 종목 전략은 유지 */
  const handleGlobalStrategyChange = useCallback((strategyId: number | null) => {
    setTargets(prev => freezeTargetsBeforeGlobalStrategyChange(prev, session.globalStrategyId));
    setSession(prev => ({ ...prev, globalStrategyId: strategyId }));
  }, [session.globalStrategyId]);

  const handleStart = useCallback(async () => {
    if (targets.length === 0) {
      window.alert('투자대상 종목을 먼저 추가하세요.');
      return;
    }
    if (!session.globalStrategyId) {
      window.alert('투자전략을 선택하세요.');
      return;
    }
    const globalStrat = strategies.find(s => s.id === session.globalStrategyId);
    const synced = await syncStrategyTimeframesFromLayoutIfNeeded(
      session.globalStrategyId,
      globalStrat,
    );
    if (!synced) return;
    const ok = await warnStrategyTimeframeMismatch(session.globalStrategyId, globalStrat);
    if (!ok) return;
    try {
      await syncVirtualTargetsToBackend(targets, session, true);
      setSession(s => ({ ...s, running: true }));
    } catch {
      window.alert('가상투자 시작 설정 저장에 실패했습니다.');
    }
  }, [targets, session, strategies]);

  const handleStop = useCallback(async () => {
    try {
      await stopVirtualLiveOnBackend(targets, session);
    } catch { /* ignore */ }
    setSession(s => ({ ...s, running: false }));
  }, [targets, session]);

  /** 실시간 차트 우측 호가와 동일: 매수 호가(bid) → 매수 탭, 매도 호가(ask) → 매도 탭 */
  const handleOrderbookRowClick = useCallback((price: number, rowType: 'ask' | 'bid') => {
    setRightTab('trade');
    const side = rowType === 'bid' ? 'buy' : 'sell';
    const req: TradeOrderFillRequest = { market: selectedMarket, price, side, seq: Date.now() };
    if (side === 'buy') setFillBuy(req);
    else setFillSell(req);
  }, [selectedMarket]);

  const handleTargetStrategyChange = useCallback(async (market: string, strategyId: number | null) => {
    setTargets(prev => prev.map(t =>
      t.market === market ? { ...t, strategyId } : t,
    ));
    if (!session.running) return;
    const effectiveId = resolveVirtualTargetStrategyId({ strategyId }, session.globalStrategyId);
    if (effectiveId == null) return;
    try {
      await saveLiveStrategySettings({
        market,
        strategyId: effectiveId,
        isLiveCheck: true,
        executionType: session.executionType,
        positionMode: session.positionMode,
        skipWatchlistSync: true,
        skipGlobalTemplate: true,
      });
      await pinStrategyEvaluationTimeframes(market, effectiveId);
    } catch {
      window.alert('종목 전략 변경 저장에 실패했습니다.');
    }
  }, [session]);

  const resyncRunningSession = useCallback(async (nextSession: VirtualSessionConfig) => {
    if (!session.running || targets.length === 0) return;
    try {
      await syncVirtualTargetsToBackend(targets, nextSession, true);
    } catch {
      window.alert('가상투자 설정 동기화에 실패했습니다.');
    }
  }, [session.running, targets]);

  const handleExecutionTypeChange = useCallback((executionType: VirtualSessionConfig['executionType']) => {
    const next = { ...session, executionType };
    setSession(next);
    void resyncRunningSession(next);
  }, [session, resyncRunningSession]);

  const handlePositionModeChange = useCallback((positionMode: VirtualSessionConfig['positionMode']) => {
    const next = { ...session, positionMode };
    setSession(next);
    void resyncRunningSession(next);
  }, [session, resyncRunningSession]);

  const rightTabs = (
    <div className="bps-right-tabs">
      <button type="button" className={`bps-right-tab${rightTab === 'trade' ? ' bps-right-tab--on' : ''}`} onClick={() => setRightTab('trade')}>매매</button>
      <button type="button" className={`bps-right-tab${rightTab === 'orderbook' ? ' bps-right-tab--on' : ''}`} onClick={() => setRightTab('orderbook')}>호가</button>
      <button type="button" className={`bps-right-tab${rightTab === 'history' ? ' bps-right-tab--on' : ''}`} onClick={() => setRightTab('history')}>
        거래{trades.length > 0 ? ` (${trades.length})` : ''}
      </button>
    </div>
  );

  return (
    <BuilderPageShell
      theme={theme}
      title="가상매매"
      subtitle="Virtual Trading"
      pageClassName="bps-page--vtd"
      loading={loading}
      loadingText="가상매매 대시보드 로딩…"
      collapsiblePanels
      headerCenter={(
        <VirtualSessionHeaderControls
          session={session}
          strategies={strategies}
          onStrategyChange={handleGlobalStrategyChange}
          onExecutionTypeChange={handleExecutionTypeChange}
          onPositionModeChange={handlePositionModeChange}
          onStart={() => void handleStart()}
          onStop={() => void handleStop()}
          paperAutoTradeEnabled={paperAutoTradeEnabled}
          onPaperAutoTradeEnabled={onPaperAutoTradeEnabled}
          paperTradingEnabled={paperTradingEnabled}
        />
      )}
      headerActions={(
        <VirtualViewHeaderControls
          session={session}
          viewMode={viewMode}
          onViewModeChange={setViewMode}
          globalDisplayMode={globalDisplayMode}
          onGlobalDisplayModeChange={handleGlobalDisplayModeChange}
          showSignalChartToggle={!focusMarket}
          gridPreset={gridPreset}
          onGridPresetChange={handleGridPresetChange}
          showGridLayoutPicker={!focusMarket}
        />
      )}
      leftStorageKey="vtd-left-width"
      leftDefaultWidth={380}
      leftCollapsedStorageKey="vtd-left-open"
      rightStorageKey="vtd-right-width"
      rightDefaultWidth={440}
      rightCollapsedStorageKey="vtd-right-open"
      left={(
        <VirtualLeftTargetPanel
          targets={targets}
          globalStrategyId={session.globalStrategyId}
          strategies={strategies}
          selectedMarket={selectedMarket}
          tickers={tickers}
          onTargetsChange={handleTargetsChange}
          onSelectMarket={handleSelectMarket}
          onTargetStrategyChange={(market, strategyId) => void handleTargetStrategyChange(market, strategyId)}
          onTogglePin={handleTogglePin}
          virtualTargetMaxCount={virtualTargetMaxCount}
        />
      )}
      center={(
        <VirtualTargetGrid
          targets={targets}
          strategies={strategies}
          snapshots={snapshots}
          snapshotLoadingByMarket={snapshotLoadingByMarket}
          session={session}
          onTargetStrategyChange={(market, strategyId) => void handleTargetStrategyChange(market, strategyId)}
          liveStatusByMarket={mergedLiveStatus}
          lastTickAtByMarket={lastTickAtByMarket}
          selectedMarket={selectedMarket}
          onSelectMarket={handleSelectMarket}
          theme={theme}
          chartRealtimeSource={chartRealtimeSource}
          chartCandleAreaPriceLabels={chartCandleAreaPriceLabels}
          chartSeriesPriceLabels={chartSeriesPriceLabels}
          chartPaneSeparator={chartPaneSeparator}
          chartLiveReceiveHighlight={chartLiveReceiveHighlight}
          tickers={tickers}
          viewMode={viewMode}
          globalDisplayMode={globalDisplayMode}
          onFocusMarketChange={setFocusMarket}
          gridPreset={gridPreset}
        />
      )}
      rightTabs={rightTabs}
      right={(
        <TradeRightPanelBody anchorRef={orderAnchorRef} embeddedInShell>
          {rightTab === 'trade' ? (
            <TradeSplitOrderPanel
              market={selectedMarket}
              tradePrice={tradePrice}
              availableKrw={coerceFiniteNumber(summary?.cashBalance) ?? 0}
              fillBuy={fillBuy}
              fillSell={fillSell}
              searchAnchorRef={orderAnchorRef}
              onMarketSelect={handleSelectMarket}
              paperTradingEnabled={paperTradingEnabled}
              paperAutoTradeEnabled={paperAutoTradeEnabled}
              onPaperOrderFilled={() => void refreshPaperData({ focusHistory: true })}
            />
          ) : rightTab === 'orderbook' ? (
            <TradeOrderbookPanelContent
              market={selectedMarket}
              asks={orderbook.asks}
              bids={orderbook.bids}
              totalAskSize={orderbook.totalAskSize}
              totalBidSize={orderbook.totalBidSize}
              wsStatus={obWsStatus}
              bestAsk={spread.bestAsk}
              bestBid={spread.bestBid}
              spread={spread.spread}
              spreadPct={spread.pct}
              prevClose={orderbookPrevClose}
              tickerInfo={orderbookTickerInfo}
              recentTrades={recentTrades}
              tradeStrength={tradeStrength ?? undefined}
              onRowClick={handleOrderbookRowClick}
            />
          ) : (
            <PaperTradeHistoryList
              trades={trades}
              strategyNames={strategyNames}
              tickers={tickers}
              onSelectMarket={handleSelectMarket}
              emptyText="수동·자동 매매 체결 내역이 없습니다."
              className="ptd-trade-history--fill"
            />
          )}
        </TradeRightPanelBody>
      )}
    />
  );
};

export default VirtualTradingPage;
