import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';

import AppShell from './components/AppShell.jsx';
import RuntimeLogsModal from './components/RuntimeLogsModal.jsx';
import { AGENTS } from './config/constants';
import { useAgentDataRequests } from './hooks/useAgentDataRequests';
import { useFeedProcessor } from './hooks/useFeedProcessor';
import { useRuntimeControls } from './hooks/useRuntimeControls';
import { useStockDataRequests } from './hooks/useStockDataRequests';
import { useWebSocketConnection } from './hooks/useWebSocketConnection';
import { fetchRuntimeLogs } from './services/runtimeApi';
import { useAgentStore } from './store/agentStore';
import { useMarketStore } from './store/marketStore';
import { usePortfolioStore } from './store/portfolioStore';
import { useRuntimeStore } from './store/runtimeStore';
import { useOpenClawStore } from './store/openclawStore';
import { useUIStore } from './store/uiStore';

const EDITABLE_AGENT_WORKSPACE_FILES = [
  'SOUL.md',
  'PROFILE.md',
  'AGENTS.md',
  'MEMORY.md',
  'POLICY.md',
  'HEARTBEAT.md',
  'ROLE.md',
  'STYLE.md'
];

export default function LiveTradingApp() {
  const {
    isConnected,
    connectionStatus,
    serverMode,
    marketStatus,
    virtualTime,
    dataSources,
    currentDate,
    runtimeConfig,
  } = useRuntimeStore();

  const {
    currentView,
    chartTab,
    isInitialAnimating,
    lastUpdate,
    isUpdating,
    now,
    setNow,
    setLastUpdate,
    setIsUpdating,
    leftWidth,
    isResizing,
    bubbles,
  } = useUIStore();

  const {
    tickers,
    rollingTickers,
    priceHistoryByTicker,
    ohlcHistoryByTicker,
    explainEventsByTicker,
    newsByTicker,
    insiderTradesByTicker,
    technicalIndicatorsByTicker,
    selectedExplainSymbol,
    historySourceByTicker,
    setSelectedExplainSymbol,
  } = useMarketStore();

  const {
    portfolioData,
    holdings,
    trades,
    stats,
    leaderboard,
  } = usePortfolioStore();

  const {
    selectedSkillAgentId,
    agentProfilesByAgent,
    agentSkillsByAgent,
    skillDetailsByName,
    localSkillDraftsByKey,
    isAgentSkillsLoading,
    skillDetailLoadingKey,
    agentSkillsSavingKey,
    agentSkillsFeedback,
    selectedWorkspaceFile,
    workspaceFilesByAgent,
    workspaceDraftContent,
    isWorkspaceFileLoading,
    workspaceFileSavingKey,
    workspaceFileFeedback,
    setSelectedWorkspaceFile,
    setSelectedSkillAgentId,
    setWorkspaceDraftContent,
  } = useAgentStore();

  const { feed, processHistoricalFeed, processFeedEvent, addSystemMessage, clearFeed } = useFeedProcessor();
  const resetRuntimeViewState = useCallback(() => {
    clearFeed();

    useMarketStore.getState().setPriceHistoryByTicker({});
    useMarketStore.getState().setOhlcHistoryByTicker({});
    useMarketStore.getState().setHistorySourceByTicker({});
    useMarketStore.getState().setExplainEventsByTicker({});
    useMarketStore.getState().setNewsByTicker({});
    useMarketStore.getState().setInsiderTradesByTicker({});
    useMarketStore.getState().setTechnicalIndicatorsByTicker({});

    usePortfolioStore.getState().setHoldings([]);
    usePortfolioStore.getState().setTrades([]);
    usePortfolioStore.getState().setStats(null);
    usePortfolioStore.getState().setLeaderboard([]);
    usePortfolioStore.getState().setPortfolioData({
      netValue: 10000,
      pnl: 0,
      equity: [],
      baseline: [],
      baseline_vw: [],
      momentum: [],
      strategies: [],
      equity_return: 0,
      baseline_return: 0,
      baseline_vw_return: 0,
      momentum_return: 0,
    });

    useRuntimeStore.getState().setLastDayHistory([]);
    useUIStore.getState().setBubbles({});
  }, [clearFeed]);

  const {
    clientRef,
    setRequestStockHistory,
    setRequestStockNewsTimeline,
    setRequestStockNewsCategories,
  } = useWebSocketConnection({
    processHistoricalFeed,
    processFeedEvent,
    addSystemMessage,
  });

  // Make clientRef available to OpenClaw panel via store
  useEffect(() => {
    useOpenClawStore.getState().setClientRef(clientRef);
  }, [clientRef]);

  const runtimeControls = useRuntimeControls({
    clientRef,
    currentTickers: tickers,
    addSystemMessage,
    onRuntimeStarted: resetRuntimeViewState,
  });

  const stockRequests = useStockDataRequests(clientRef, {
    setRequestStockHistory,
    setRequestStockNewsTimeline,
    setRequestStockNewsCategories,
  });
  const {
    requestAgentSkills,
    requestAgentProfile,
    requestSkillDetail,
    handleCreateLocalSkill,
    handleLocalSkillDraftChange,
    handleLocalSkillSave,
    handleLocalSkillDelete,
    handleRemoveSharedSkill,
    handleAgentSkillToggle,
    handleSkillAgentChange,
    requestWorkspaceFile,
    handleWorkspaceFileChange,
    handleWorkspaceFileSave,
    handleUploadExternalSkill,
  } = useAgentDataRequests(clientRef);

  const [isRuntimeLogsOpen, setIsRuntimeLogsOpen] = useState(false);
  const [isRuntimeLogsLoading, setIsRuntimeLogsLoading] = useState(false);
  const [runtimeLogsPayload, setRuntimeLogsPayload] = useState(null);
  const [runtimeLogsError, setRuntimeLogsError] = useState(null);
  const agentFeedRef = useRef(null);
  const isSocketReady = isConnected && connectionStatus === 'connected';

  const selectedAgentId = selectedSkillAgentId || AGENTS[0]?.id || null;
  const selectedAgentProfile = selectedAgentId ? (agentProfilesByAgent[selectedAgentId] || null) : null;
  const selectedAgentSkills = selectedAgentId ? (agentSkillsByAgent[selectedAgentId] || []) : [];
  const selectedWorkspaceContent = selectedAgentId && selectedWorkspaceFile
    ? (workspaceFilesByAgent[selectedAgentId]?.[selectedWorkspaceFile] || '')
    : '';

  useEffect(() => {
    if (!selectedSkillAgentId && AGENTS.length > 0) {
      setSelectedSkillAgentId(AGENTS[0].id);
    }
  }, [selectedSkillAgentId, setSelectedSkillAgentId]);

  useEffect(() => {
    if (!selectedWorkspaceFile) {
      setSelectedWorkspaceFile('MEMORY.md');
    }
  }, [selectedWorkspaceFile, setSelectedWorkspaceFile]);

  useEffect(() => {
    if (!isSocketReady || !selectedAgentId || !clientRef.current) {
      return;
    }

    if (!agentProfilesByAgent[selectedAgentId]) {
      requestAgentProfile(selectedAgentId);
    }

    if (!Array.isArray(agentSkillsByAgent[selectedAgentId])) {
      requestAgentSkills(selectedAgentId);
    }

    if (
      selectedWorkspaceFile
      && workspaceFilesByAgent[selectedAgentId]?.[selectedWorkspaceFile] === undefined
    ) {
      requestWorkspaceFile(selectedAgentId, selectedWorkspaceFile);
    }
  }, [
    agentProfilesByAgent,
    agentSkillsByAgent,
    clientRef,
    isSocketReady,
    requestAgentProfile,
    requestAgentSkills,
    requestWorkspaceFile,
    selectedAgentId,
    selectedWorkspaceFile,
    workspaceFilesByAgent,
  ]);

  useEffect(() => {
    if (!isSocketReady || !clientRef.current) {
      return;
    }

    AGENTS.forEach((agent) => {
      if (!agent?.id) {
        return;
      }
      if (!agentProfilesByAgent[agent.id]) {
        requestAgentProfile(agent.id);
      }
    });
  }, [
    agentProfilesByAgent,
    clientRef,
    isSocketReady,
    requestAgentProfile,
  ]);

  useEffect(() => {
    const symbols = runtimeControls.displayTickers
      .map((ticker) => ticker.symbol)
      .filter((symbol) => typeof symbol === 'string' && symbol.trim());

    if (!symbols.length) {
      setSelectedExplainSymbol('');
      return;
    }

    if (!selectedExplainSymbol || !symbols.includes(selectedExplainSymbol)) {
      setSelectedExplainSymbol(symbols[0]);
    }
  }, [runtimeControls.displayTickers, selectedExplainSymbol, setSelectedExplainSymbol]);

  useEffect(() => {
    if (virtualTime) {
      setNow(new Date(virtualTime));
      const id = setInterval(() => setNow(new Date()), 1000);
      return () => clearInterval(id);
    }

    const id = setInterval(() => setNow(new Date()), 1000);
    return () => clearInterval(id);
  }, [setNow, virtualTime]);

  useEffect(() => {
    setLastUpdate(new Date());
    setIsUpdating(true);
    const timer = setTimeout(() => setIsUpdating(false), 500);
    return () => clearTimeout(timer);
  }, [holdings, stats, trades, portfolioData.netValue, setIsUpdating, setLastUpdate]);

  const marketStatusLabel = useMemo(() => {
    if (!marketStatus) return null;
    const raw = typeof marketStatus.status_text === 'string' ? marketStatus.status_text.trim() : '';
    const normalized = raw.toLowerCase();
    const byStatus = {
      open: '开盘',
      closed: '休市',
      premarket: '盘前',
      afterhours: '盘后',
    };
    const byText = {
      'market closed (non-trading day)': '休市',
      'market open': '开盘',
      'market closed': '收盘',
      'pre-market': '盘前',
      'after-hours': '盘后',
      'after hours': '盘后',
      'backtest mode': '回测模式',
    };
    if (normalized && byText[normalized]) return byText[normalized];
    if (marketStatus.status && byStatus[marketStatus.status]) return byStatus[marketStatus.status];
    return raw || '状态未知';
  }, [marketStatus]);

  const providerLabelMap = useMemo(() => ({
    yfinance: 'YFINANCE',
    finnhub: 'FINNHUB',
    financial_datasets: 'FINANCIAL DATASETS',
    local_csv: 'CSV',
    polygon: 'POLYGON',
    backtest: 'BACKTEST',
  }), []);

  const dataSourceLabel = useMemo(() => {
    const source = dataSources?.last_success?.prices
      || marketStatus?.live_quote_provider
      || (Array.isArray(dataSources?.preferred) ? dataSources.preferred[0] : null);
    if (!source) return null;
    const normalized = String(source).trim().toLowerCase();
    return `数据源 ${providerLabelMap[normalized] || String(source).trim()}`;
  }, [dataSources, marketStatus, providerLabelMap]);

  const bubbleFor = useCallback((idOrName) => {
    let bubble = bubbles[idOrName];
    if (bubble) return bubble;
    const agent = AGENTS.find((item) => item.name === idOrName || item.id === idOrName);
    if (agent) {
      bubble = bubbles[agent.id];
      if (bubble) return bubble;
    }
    return null;
  }, [bubbles]);

  const handleManualTrigger = useCallback(() => {
    if (!isSocketReady || !clientRef.current) {
      addSystemMessage('连接未就绪，无法手动触发');
      return;
    }
    const success = clientRef.current.send({ type: 'trigger_strategy' });
    if (!success) {
      addSystemMessage('手动触发发送失败，请检查连接状态');
      return;
    }
    addSystemMessage('已发送手动触发请求');
  }, [addSystemMessage, clientRef, isSocketReady]);

  const loadRuntimeLogs = useCallback(async () => {
    setIsRuntimeLogsLoading(true);
    setRuntimeLogsError(null);
    try {
      const payload = await fetchRuntimeLogs();
      setRuntimeLogsPayload(payload);
    } catch (error) {
      setRuntimeLogsError(error.message || '无法读取运行日志');
    } finally {
      setIsRuntimeLogsLoading(false);
    }
  }, []);

  const agentRequests = {
    agents: AGENTS,
    agentProfilesByAgent,
    agentSkillsByAgent,
    workspaceFilesByAgent,
    selectedAgentId,
    selectedAgentProfile,
    selectedAgentSkills,
    skillDetailsByName,
    localSkillDraftsByKey,
    skillDetailLoadingKey,
    editableFiles: EDITABLE_AGENT_WORKSPACE_FILES,
    selectedWorkspaceFile,
    workspaceFileContent: selectedWorkspaceContent,
    workspaceDraftContent,
    isConnected,
    isAgentSkillsLoading,
    agentSkillsSavingKey,
    agentSkillsFeedback,
    isWorkspaceFileLoading,
    workspaceFileSavingKey,
    workspaceFileFeedback,
    onAgentChange: handleSkillAgentChange,
    onCreateLocalSkill: handleCreateLocalSkill,
    onSkillDetailRequest: requestSkillDetail,
    onLocalSkillDraftChange: handleLocalSkillDraftChange,
    onLocalSkillDelete: handleLocalSkillDelete,
    onLocalSkillSave: handleLocalSkillSave,
    onRemoveSharedSkill: handleRemoveSharedSkill,
    onSkillToggle: handleAgentSkillToggle,
    onWorkspaceFileChange: handleWorkspaceFileChange,
    onWorkspaceDraftChange: setWorkspaceDraftContent,
    onWorkspaceFileSave: handleWorkspaceFileSave,
    onUploadExternalSkill: handleUploadExternalSkill,
    clientRef,
  };

  return (
    <>
      <AppShell
        isConnected={isConnected}
        virtualTime={virtualTime}
        now={now}
        marketStatus={marketStatus}
        serverMode={serverMode}
        marketStatusLabel={marketStatusLabel}
        dataSourceLabel={dataSourceLabel}
        runtimeSummaryLabel={runtimeControls.runtimeSummaryLabel}
        isUpdating={isUpdating}
        onManualTrigger={handleManualTrigger}
        onOpenRuntimeLogs={() => {
          setIsRuntimeLogsOpen(true);
          void loadRuntimeLogs();
        }}
        onRuntimeSettingsToggle={runtimeControls.handleRuntimeSettingsToggle}
        isRuntimeSettingsOpen={runtimeControls.isRuntimeSettingsOpen}
        isRuntimeConfigSaving={runtimeControls.isRuntimeConfigSaving}
        isWatchlistSaving={runtimeControls.isWatchlistSaving}
        runtimeConfigFeedback={runtimeControls.runtimeConfigFeedback}
        watchlistFeedback={runtimeControls.watchlistFeedback}
        launchModeDraft={runtimeControls.launchModeDraft}
        restoreRunIdDraft={runtimeControls.restoreRunIdDraft}
        runtimeHistoryRuns={runtimeControls.runtimeHistoryRuns}
        scheduleModeDraft={runtimeControls.scheduleModeDraft}
        intervalMinutesDraft={runtimeControls.intervalMinutesDraft}
        triggerTimeDraft={runtimeControls.triggerTimeDraft}
        maxCommCyclesDraft={runtimeControls.maxCommCyclesDraft}
        initialCashDraft={runtimeControls.initialCashDraft}
        marginRequirementDraft={runtimeControls.marginRequirementDraft}
        enableMemoryDraft={runtimeControls.enableMemoryDraft}
        modeDraft={runtimeControls.modeDraft}
        pollIntervalDraft={runtimeControls.pollIntervalDraft}
        startDateDraft={runtimeControls.startDateDraft}
        endDateDraft={runtimeControls.endDateDraft}
        watchlistDraftSymbols={runtimeControls.watchlistDraftSymbols}
        watchlistInputValue={runtimeControls.watchlistInputValue}
        watchlistSuggestions={runtimeControls.watchlistSuggestions}
        onLaunchModeChange={runtimeControls.setLaunchModeDraft}
        onRestoreRunIdChange={runtimeControls.setRestoreRunIdDraft}
        onScheduleModeChange={runtimeControls.setScheduleModeDraft}
        onIntervalMinutesChange={runtimeControls.setIntervalMinutesDraft}
        onTriggerTimeChange={runtimeControls.setTriggerTimeDraft}
        onMaxCommCyclesChange={runtimeControls.setMaxCommCyclesDraft}
        onInitialCashChange={runtimeControls.setInitialCashDraft}
        onMarginRequirementChange={runtimeControls.setMarginRequirementDraft}
        onEnableMemoryChange={runtimeControls.setEnableMemoryDraft}
        onModeChange={runtimeControls.setModeDraft}
        onPollIntervalChange={runtimeControls.setPollIntervalDraft}
        onStartDateChange={runtimeControls.setStartDateDraft}
        onEndDateChange={runtimeControls.setEndDateDraft}
        onWatchlistInputChange={runtimeControls.handleWatchlistInputChange}
        onWatchlistInputKeyDown={runtimeControls.handleWatchlistInputKeyDown}
        onWatchlistAdd={runtimeControls.handleWatchlistAdd}
        onWatchlistRemove={runtimeControls.handleWatchlistRemove}
        onWatchlistRestoreCurrent={runtimeControls.handleWatchlistRestoreCurrent}
        onWatchlistRestoreDefault={runtimeControls.handleWatchlistRestoreDefault}
        onWatchlistSuggestionClick={runtimeControls.handleWatchlistSuggestionClick}
        onLaunchConfigSave={runtimeControls.handleLaunchConfigSave}
        onRestoreDefaults={runtimeControls.handleRuntimeDefaultsRestore}
        displayTickers={runtimeControls.displayTickers}
        portfolioData={portfolioData}
        rollingTickers={rollingTickers}
        feed={feed}
        bubbles={bubbles}
        bubbleFor={bubbleFor}
        leaderboard={leaderboard}
        currentView={currentView}
        chartTab={chartTab}
        holdings={holdings}
        trades={trades}
        stats={stats}
        priceHistoryByTicker={priceHistoryByTicker}
        ohlcHistoryByTicker={ohlcHistoryByTicker}
        selectedExplainSymbol={selectedExplainSymbol}
        onSelectedExplainSymbolChange={setSelectedExplainSymbol}
        historySourceByTicker={historySourceByTicker}
        explainEventsByTicker={explainEventsByTicker}
        newsByTicker={newsByTicker}
        insiderTradesByTicker={insiderTradesByTicker}
        technicalIndicatorsByTicker={technicalIndicatorsByTicker}
        currentDate={currentDate}
        stockRequests={stockRequests}
        agentRequests={agentRequests}
        agentProfilesByAgent={agentProfilesByAgent}
        leftWidth={leftWidth}
        isResizing={isResizing}
        onMouseDown={() => useUIStore.getState().setIsResizing(true)}
        agentFeedRef={agentFeedRef}
      />

      <RuntimeLogsModal
        isOpen={isRuntimeLogsOpen}
        isLoading={isRuntimeLogsLoading}
        logPayload={runtimeLogsPayload}
        error={runtimeLogsError}
        onClose={() => setIsRuntimeLogsOpen(false)}
        onRefresh={loadRuntimeLogs}
      />
    </>
  );
}
