import React, { useCallback, useEffect, useMemo, useRef, useState, type MutableRefObject } from 'react';
import TradingChart from '../TradingChart';
import type { BacktestSignal, StrategyDto } from '../../utils/backendApi';
import { loadStrategyForNotification } from '../../utils/backendApi';
import { isStrategyMissingOnServer } from '../../utils/strategyMissingCache';
import type { ChartType, IndicatorConfig, OHLCVBar, Theme, Timeframe } from '../../types';
import { loadAnalysisCandles } from '../../utils/analysisChartData';
import type { ChartManager } from '../../utils/ChartManager';
import { useHistoryLoader, LOAD_MORE_TRIGGER } from '../../hooks/useHistoryLoader';
import { isUpbitMarket } from '../../utils/upbitApi';
import { DEFAULT_DISPLAY_TIMEZONE } from '../../utils/timezone';
import {
  formatChartTypeKo,
  formatTimeframeKo,
  normalizeChartTimeframe,
  timeframeBarSeconds,
} from '../../utils/backtestUiUtils';
import { getKoreanName } from '../../utils/marketNameCache';
import { useIndicatorSettings } from '../../hooks/useIndicatorSettings';
import { useAppSettings } from '../../hooks/useAppSettings';
import { getIndicatorDef } from '../../utils/indicatorRegistry';
import {
  chartPaneFlexRatio,
  countNonOverlayIndicatorPanes,
} from '../../utils/strategyOscillatorSeries';
import { buildChartIndicatorConfig } from '../../utils/indicatorPaneMerge';
import {
  applyPaperOverlayVisibility,
  DEFAULT_PAPER_OVERLAY_VISIBILITY,
  listPaperOverlayToggleItems,
  paperOverlayVisibilityToChart,
  type PaperOverlayToggleKey,
  type PaperOverlayVisibility,
} from '../../utils/paperChartOverlayVisibility';
import {
  buildVirtualTradingChartIndicators,
  ensurePaperChartOverlays,
  resolveStrategyPrimaryTimeframe,
} from '../../utils/strategyToChartIndicators';
import type { ChartOverlayToggleKey } from '../../utils/chartOverlayVisibility';

interface Props {
  symbol: string;
  timeframe: string;
  toTimeSec: number;
  fromTimeSec?: number;
  barCount: number;
  signals: BacktestSignal[];
  strategyId?: number | null;
  theme?: Theme;
  /** 과거 캔들 추가 로드 후 (차트에 반영된 전체 봉) */
  onHistoryBarsLoaded?: (bars: OHLCVBar[]) => void;
  /** 과거 로드·시그널 재계산 중 UI */
  onHistoryLoadingChange?: (loading: boolean) => void;
  /** 투자관리 차트 탭 — 캔들 오버레이 표시/숨김 (세션만, DB 미저장) */
  overlayVisibility?: PaperOverlayVisibility;
  onToggleOverlay?: (key: PaperOverlayToggleKey) => void;
  /** 분석 레포트 팝업 (백테스팅 상단 툴바) */
  onOpenReport?: () => void;
  reportDisabled?: boolean;
  /** 거래 로그 클릭 시 해당 시점으로 차트 이동 */
  focusTimeSec?: number | null;
}

const noop = () => {};

let _indSeq = 0;
function newIndId() {
  _indSeq += 1;
  return `btd_${_indSeq}_${Date.now()}`;
}

const ALL_TF_VISIBLE = { '1m': true, '3m': true, '5m': true, '10m': true, '15m': true, '30m': true, '1h': true, '4h': true, '1D': true, '1W': true, '1M': true };

function defaultOverlayIndicators(
  getParams: (t: string, d?: Record<string, number | string | boolean>) => Record<string, number | string | boolean>,
  getVisualConfig: ReturnType<typeof useIndicatorSettings>['getVisualConfig'],
): IndicatorConfig[] {
  const cfg = buildChartIndicatorConfig('SMA', newIndId(), getParams, getVisualConfig, {
    timeframeVisibility: ALL_TF_VISIBLE,
  });
  return cfg ? [cfg] : [];
}

/** 전략 미선택 시 기본 오실레이터(RSI, MACD)를 TradingChart sub-pane 으로 추가 */
function defaultOscillatorIndicators(
  getParams: (t: string, d?: Record<string, number | string | boolean>) => Record<string, number | string | boolean>,
  getVisualConfig: ReturnType<typeof useIndicatorSettings>['getVisualConfig'],
): IndicatorConfig[] {
  return ['RSI', 'MACD']
    .map(type => buildChartIndicatorConfig(type, newIndId(), getParams, getVisualConfig, {
      timeframeVisibility: ALL_TF_VISIBLE,
    }))
    .filter((c): c is IndicatorConfig => c != null);
}

const isOverlayType = (type: string) => getIndicatorDef(type)?.overlay === true;

/**
 * 백테스트·투자관리 차트 — TradingChart 단일 엔진(실시간 차트와 동일 크로스헤어·시간축·우측 가격)
 */
const BacktestAnalysisChart: React.FC<Props> = ({
  symbol,
  timeframe: timeframeRaw,
  toTimeSec,
  fromTimeSec,
  barCount,
  signals,
  strategyId,
  theme = 'dark',
  onHistoryBarsLoaded,
  onHistoryLoadingChange,
  overlayVisibility,
  onToggleOverlay,
  onOpenReport,
  reportDisabled = false,
  focusTimeSec = null,
}) => {
  const { getParams, getVisualConfig } = useIndicatorSettings();
  const { defaults: appDefaults } = useAppSettings();
  const [bars, setBars] = useState<OHLCVBar[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [chartType] = useState<ChartType>('candlestick');
  const [strategy, setStrategy] = useState<StrategyDto | undefined>();
  const [strategyLoading, setStrategyLoading] = useState(false);
  /** 개별 지표 숨김 ID 집합 (실시간 차트와 동일한 per-indicator 숨김 지원) */
  const [hiddenIndicatorIds, setHiddenIndicatorIds] = useState<ReadonlySet<string>>(new Set());
  /** 백테스트·분석 레포트 — 세션 전용 오버레이 on/off (투자관리 탭은 부모 props 사용) */
  const [internalOverlayVisibility, setInternalOverlayVisibility] = useState<PaperOverlayVisibility>(
    () => ({ ...DEFAULT_PAPER_OVERLAY_VISIBILITY }),
  );
  const effectiveOverlayVisibility = overlayVisibility ?? internalOverlayVisibility;
  const managerRef = useRef<ChartManager | null>(null);
  const overlayVisibilityRef = useRef(effectiveOverlayVisibility);
  const logicalRangeUnsubRef = useRef<(() => void) | null>(null);
  const viewportUnsubRef = useRef<(() => void) | null>(null);
  const loadMoreRefRef = useRef<MutableRefObject<() => void>>({ current: () => {} });
  overlayVisibilityRef.current = effectiveOverlayVisibility;
  const signalsRef = useRef(signals);
  signalsRef.current = signals;
  const onHistoryBarsLoadedRef = useRef(onHistoryBarsLoaded);
  onHistoryBarsLoadedRef.current = onHistoryBarsLoaded;
  const [loadedBarCount, setLoadedBarCount] = useState<number | null>(null);
  const market = symbol.startsWith('KRW-') ? symbol : `KRW-${symbol}`;

  const chartTimeframe = useMemo((): Timeframe => {
    // 백테스트 결과: 실행 타임프레임 우선 (전략 DSL TF와 실행 TF 불일치 시 차트·시그널 어긋남 방지)
    const tf = timeframeRaw || (strategy ? resolveStrategyPrimaryTimeframe(strategy) : '1m');
    return normalizeChartTimeframe(tf) as Timeframe;
  }, [strategy, timeframeRaw]);

  useEffect(() => {
    if (!strategyId || isStrategyMissingOnServer(strategyId)) {
      setStrategy(undefined);
      setStrategyLoading(false);
      return;
    }
    let cancelled = false;
    setStrategyLoading(true);
    void loadStrategyForNotification(strategyId).then(s => {
      if (!cancelled) {
        setStrategy(s ?? undefined);
        setStrategyLoading(false);
      }
    });
    return () => { cancelled = true; };
  }, [strategyId]);

  /**
   * 백테스트 화면(overlayVisibility 없음)에서 보조지표를 TradingChart sub-pane 으로 통합.
   * - 기존: 오실레이터를 SVG 기반 StrategyOscillatorPanes 에서 별도 렌더링
   *   → 캔들 차트와 시간 축(x축) 불일치, 우측 가격 라벨 없음
   * - 변경: 모든 지표를 TradingChart sub-pane 으로 통합
   *   → 시간 축 완전 동기화, 우측 가격 라벨 표시
   */
  const showOscillatorPanel = !overlayVisibility;

  const baseIndicators = useMemo(() => {
    if (strategyId && strategyLoading) return [];

    let inds: IndicatorConfig[];
    if (strategy) {
      inds = buildVirtualTradingChartIndicators(strategy, getParams, getVisualConfig);
      inds = inds.filter(i => i.timeframeVisibility?.[chartTimeframe] !== false);
      if (showOscillatorPanel && inds.every(i => isOverlayType(i.type))) {
        inds = [...inds, ...defaultOscillatorIndicators(getParams, getVisualConfig)];
      }
    } else {
      inds = defaultOverlayIndicators(getParams, getVisualConfig);
      if (showOscillatorPanel) {
        inds = [...inds, ...defaultOscillatorIndicators(getParams, getVisualConfig)];
      }
    }
    inds = ensurePaperChartOverlays(inds, getParams, getVisualConfig);
    return inds;
  }, [strategy, strategyId, strategyLoading, getParams, getVisualConfig, chartTimeframe, overlayVisibility, showOscillatorPanel]);

  const indicators = useMemo(() => {
    let inds = applyPaperOverlayVisibility(baseIndicators, effectiveOverlayVisibility);
    // 개별 지표 숨김 상태 반영 (per-indicator hidden toggle)
    if (hiddenIndicatorIds.size > 0) {
      inds = inds.map(ind =>
        hiddenIndicatorIds.has(ind.id) ? { ...ind, hidden: true } : ind,
      );
    }
    return inds;
  }, [baseIndicators, effectiveOverlayVisibility, hiddenIndicatorIds]);

  const candleOverlayToggles = useMemo(
    () => listPaperOverlayToggleItems(baseIndicators, effectiveOverlayVisibility),
    [baseIndicators, effectiveOverlayVisibility],
  );

  /**
   * 캔들 오버레이 그룹 토글 — 실시간 차트와 동일하게 ChartManager 를 즉시 호출한 뒤
   * 부모 state 도 업데이트한다.
   */
  const handleToggleCandleOverlay = useCallback((key: ChartOverlayToggleKey) => {
    const pKey = key as PaperOverlayToggleKey;
    const prev = overlayVisibilityRef.current;
    if (pKey in prev) {
      const newVisible = !prev[pKey];
      managerRef.current?.setChartOverlayGroupVisible(key, newVisible);
    }
    if (onToggleOverlay) {
      onToggleOverlay(pKey);
    } else {
      setInternalOverlayVisibility(prev => ({ ...prev, [pKey]: !prev[pKey] }));
    }
  }, [onToggleOverlay]);

  /**
   * 개별 보조지표 숨김/표시 — 실시간 차트와 동일한 per-indicator 토글
   */
  const handleToggleIndicatorHidden = useCallback((id: string) => {
    setHiddenIndicatorIds(prev => {
      const next = new Set(prev);
      const willHide = !next.has(id);
      if (willHide) {
        next.add(id);
      } else {
        next.delete(id);
      }
      // ChartManager 즉시 반영
      managerRef.current?.toggleIndicatorVisible(id, !willHide);
      return next;
    });
  }, []);

  const auxPaneCount = useMemo(
    () => countNonOverlayIndicatorPanes(indicators, isOverlayType),
    [indicators],
  );

  const paneAreaRatio = useMemo(() => {
    if (auxPaneCount <= 0) return null;
    const flex = chartPaneFlexRatio(auxPaneCount);
    return { candle: flex.candle, aux: flex.aux };
  }, [auxPaneCount]);

  const effectiveBarCount = useMemo(() => {
    const barSec = timeframeBarSeconds(timeframeRaw);
    if (fromTimeSec && toTimeSec > fromTimeSec) {
      return Math.max(barCount, Math.min(500, Math.ceil((toTimeSec - fromTimeSec) / barSec) + 50));
    }
    return barCount;
  }, [barCount, fromTimeSec, toTimeSec, timeframeRaw]);

  useEffect(() => {
    let cancelled = false;
    setLoading(true);
    setError(null);
    setLoadedBarCount(null);
    void loadAnalysisCandles(market, chartTimeframe, toTimeSec, effectiveBarCount)
      .then(data => {
        if (cancelled) return;
        setBars(data);
      })
      .catch(e => {
        if (cancelled) return;
        setError(e instanceof Error ? e.message : '차트 로드 실패');
        setBars([]);
      })
      .finally(() => {
        if (!cancelled) setLoading(false);
      });
    return () => { cancelled = true; };
  }, [market, chartTimeframe, toTimeSec, effectiveBarCount]);

  const applyMarkers = useCallback((attempt = 0) => {
    const mgr = managerRef.current;
    if (!mgr?.hasMainSeries()) {
      if (attempt < 80) requestAnimationFrame(() => applyMarkers(attempt + 1));
      return;
    }
    mgr.setBacktestMarkers(signalsRef.current, true);
  }, []);

  const applyPaneRatio = useCallback((mgr: ChartManager) => {
    if (paneAreaRatio && paneAreaRatio.aux > 0) {
      mgr.setPaneAreaRatio(paneAreaRatio.candle, paneAreaRatio.aux);
    } else {
      mgr.setPaneAreaRatio(0, 0);
    }
  }, [paneAreaRatio]);

  const handleBarsPrepended = useCallback((_added: number, total: number) => {
    setLoadedBarCount(total);
    const mgr = managerRef.current;
    if (!mgr) return;
    onHistoryBarsLoadedRef.current?.(mgr.getRawBars());
    // 왼쪽 끝에 붙어 있으면 연속 로드
    requestAnimationFrame(() => {
      const lr = mgr.getVisibleLogicalRange();
      if (lr && lr.from < LOAD_MORE_TRIGGER) {
        loadMoreRefRef.current.current();
      }
    });
  }, []);

  const { isLoadingMore, loadMoreRef } = useHistoryLoader({
    symbol: market,
    timeframe: chartTimeframe,
    isUpbit: isUpbitMarket(market),
    managerRef,
    onBarsPrepended: handleBarsPrepended,
  });

  loadMoreRefRef.current = loadMoreRef;

  const setupHistoryScroll = useCallback((mgr: ChartManager) => {
    logicalRangeUnsubRef.current?.();
    viewportUnsubRef.current?.();

    const tryLoadMore = () => {
      if (!mgr.hasMainSeries()) return;
      const lr = mgr.getVisibleLogicalRange();
      if (lr && lr.from < LOAD_MORE_TRIGGER) {
        loadMoreRefRef.current.current();
      }
    };

    logicalRangeUnsubRef.current = mgr.subscribeVisibleLogicalRange(r => {
      if (r && r.from < LOAD_MORE_TRIGGER) {
        loadMoreRefRef.current.current();
      }
    });
    // 커스텀 패닝은 LWC 논리범위 이벤트를 안 낼 수 있어 뷰포트 알림에도 연결
    viewportUnsubRef.current = mgr.subscribeViewport(tryLoadMore);
    requestAnimationFrame(tryLoadMore);
  }, []);

  useEffect(() => {
    onHistoryLoadingChange?.(isLoadingMore);
  }, [isLoadingMore, onHistoryLoadingChange]);

  useEffect(() => () => {
    logicalRangeUnsubRef.current?.();
    logicalRangeUnsubRef.current = null;
    viewportUnsubRef.current?.();
    viewportUnsubRef.current = null;
  }, []);

  const onManagerReady = useCallback((mgr: ChartManager) => {
    managerRef.current = mgr;
    // 실시간 차트와 동일하게: 마운트 시 오버레이 가시성 ChartManager 에 동기화
    if (overlayVisibilityRef.current) {
      mgr.setChartOverlayVisibility(paperOverlayVisibilityToChart(overlayVisibilityRef.current));
    }
    setupHistoryScroll(mgr);
    setLoadedBarCount(mgr.getRawBarsLength());
    applyPaneRatio(mgr);
    applyMarkers();
  }, [applyMarkers, applyPaneRatio, setupHistoryScroll]);

  const onCandlesReady = useCallback(() => {
    const mgr = managerRef.current;
    if (!mgr) return;
    setupHistoryScroll(mgr);
    applyPaneRatio(mgr);
    applyMarkers();
    setLoadedBarCount(mgr.getRawBarsLength());
  }, [applyMarkers, applyPaneRatio, setupHistoryScroll]);

  useEffect(() => {
    applyMarkers();
  }, [signals, bars, applyMarkers]);

  useEffect(() => {
    if (focusTimeSec == null) return;
    const mgr = managerRef.current;
    if (!mgr) return;
    const barSec = timeframeBarSeconds(timeframeRaw);
    const pad = Math.max(barSec * 48, 3600);
    mgr.applyVisibleTimeRange(focusTimeSec - pad, focusTimeSec + pad);
  }, [focusTimeSec, timeframeRaw]);

  useEffect(() => {
    const mgr = managerRef.current;
    if (mgr) applyPaneRatio(mgr);
  }, [paneAreaRatio, applyPaneRatio]);

  const koName = getKoreanName(market);
  const tfKo = formatTimeframeKo(chartTimeframe);
  const chartTypeKo = formatChartTypeKo(chartType);
  const stratTf = chartTimeframe;

  const priceLabels = appDefaults.chartSeriesPriceLabels !== false;

  return (
    <div className="btd-analysis-chart">
      <div className="btd-analysis-toolbar">
        <div className="btd-analysis-toolbar-left">
          <span className="btd-analysis-select btd-analysis-select--static">{koName}</span>
          <span className="btd-analysis-select btd-analysis-select--static">{tfKo}</span>
          <span className="btd-analysis-select btd-analysis-select--static">{chartTypeKo}</span>
        </div>
        <div className="btd-analysis-toolbar-right">
          {onOpenReport && (
            <button
              type="button"
              className="btd-analysis-tool btd-analysis-tool--report"
              title={reportDisabled ? '분석 결과를 선택하세요' : '투자분석 상세 레포트 (인쇄·PDF)'}
              aria-label="분석 레포트"
              disabled={reportDisabled}
              onClick={onOpenReport}
            >
              <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden>
                <path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
                <polyline points="14 2 14 8 20 8" />
                <line x1="16" y1="13" x2="8" y2="13" />
                <line x1="16" y1="17" x2="8" y2="17" />
              </svg>
            </button>
          )}
          <span className="btd-analysis-tool" title="자석 모드">⊕</span>
          <span className="btd-analysis-tool" title="드로잉">✎</span>
          <span className="btd-analysis-tool" title="삭제">⌫</span>
          <span className="btd-analysis-count">
            {(loadedBarCount ?? effectiveBarCount).toLocaleString()}봉
            {isLoadingMore && ' · 과거 로드…'}
          </span>
        </div>
      </div>
      <div className="btd-analysis-main">
        <div className="btd-analysis-canvas-wrap btd-analysis-canvas-wrap--live">
          {loading && <div className="btd-analysis-loading">차트 로딩…</div>}
          {error && !loading && <div className="btd-analysis-error">{error}</div>}
          {!loading && !error && bars.length > 0 && (
            <TradingChart
              key={`${market}-${chartTimeframe}-${toTimeSec}-${effectiveBarCount}-${indicators.map(i => i.id).join(',')}`}
              bars={bars}
              barsMarket={market}
              market={market}
              timeframe={stratTf}
              chartType={chartType}
              theme={theme}
              mode="chart"
              indicators={indicators}
              drawingTool="cursor"
              drawings={[]}
              logScale={false}
              onCrosshair={noop}
              onManagerReady={onManagerReady}
              onCandlesReady={onCandlesReady}
              onAddDrawing={noop}
              displayTimezone={DEFAULT_DISPLAY_TIMEZONE}
              volumeVisible={false}
              paneSeparatorOptions={appDefaults.chartPaneSeparator}
              paneLayoutClamp
              showPaneResizeHandle={auxPaneCount > 0}
              paneAreaRatio={paneAreaRatio}
              showPaneLegend
              crosshairInfoVisible
              candleAreaPriceLabelsEnabled={priceLabels}
              indicatorAreaPriceLabelsEnabled={priceLabels}
              seriesPriceLabelsEnabled={priceLabels}
              showHoverToolbar
              candleOverlayToggles={candleOverlayToggles}
              onToggleCandleOverlay={handleToggleCandleOverlay}
              showCandleOverlayControls
              onToggleIndicatorHidden={handleToggleIndicatorHidden}
            />
          )}
          {!loading && !error && bars.length === 0 && (
            <div className="btd-analysis-error">표시할 캔들 데이터가 없습니다.</div>
          )}
        </div>
        {/* 오실레이터 pane 은 TradingChart 내부 sub-pane 으로 통합 (시간 축 동기화) */}
      </div>
    </div>
  );
};

export default BacktestAnalysisChart;
