/**
 * 멀티차트 대시보드 - 그리드 갤러리 형태
 * 차트 카드는 멀티차트2와 동일하게 ChartItemInvestment(WS 지표 병합·analyze 페이로드 등) 사용
 */
import React, { useCallback, useState, useEffect, useMemo } from 'react';
import { Box, FormControl, Select, MenuItem, Chip, CircularProgress, IconButton, Tooltip, Button, Menu, ListItemText, Checkbox, Divider, Snackbar, Alert, Popover, LinearProgress, Typography, ListItemIcon } from '@mui/material';
import { Refresh as RefreshIcon, Fullscreen as FullscreenIcon, FullscreenExit as FullscreenExitIcon, ShowChart as ShowChartIcon, Timeline as TimelineIcon, KeyboardArrowDown, Settings as SettingsIcon, FileDownload as FileDownloadIcon, FileUpload as FileUploadIcon } from '@mui/icons-material';
import type { MALineConfig } from '../../contexts/IndicatorVisibilityContext';
import MASettingsDialog from '../MASettingsDialog';
import IchimokuSettingsDialog from '../IchimokuSettingsDialog';
import BollingerSettingsDialog from '../BollingerSettingsDialog';
import 'react-grid-layout/css/styles.css';
import 'react-resizable/css/styles.css';
import {
  DndContext,
  DragEndEvent,
  DragOverEvent,
  DragStartEvent,
  DragOverlay,
  useSensor,
  useSensors,
  PointerSensor,
  closestCenter,
  useDroppable,
} from '@dnd-kit/core';
import {
  mergeBollingerPersisted,
  mergeIchimokuPersisted,
  useDashboardStore,
  type StockInfo,
  type ChartType,
} from '../../stores/useDashboardStore';
import { useNavigation } from '../../contexts/NavigationContext';
import { MultiChartMarketDataProvider, useMultiChartMarketData } from '../../contexts/MultiChartMarketDataContext';
import type { ConnectionStatus } from '../../hooks/useWebSocketConnectionStatus';
import { ChartItemInvestment } from './ChartItemInvestment';
import { ChartDragOverlayPreview } from './ChartDragOverlayPreview';
import { MultiChartWebSocketStatus } from './MultiChartWebSocketStatus';
import { StockSelectDropdown } from './StockSelectDropdown';
import { StockSelectDialog } from './StockSelectDialog';
import { MARKET_LIST } from '../../constants/marketList';
import upbitApiService from '../../services/upbitApi';
import { getTopRisingStocks } from '../../services/risingStockApi';
import { getNotifications } from '../../services/alertApi';
import { extractAssetNamesFromMessage, resolveSymbols } from '../../utils/alertMessageParser';
import {
  countStocksInSlots,
  gridColsForPreset,
  gridRowsForPreset,
  MAX_MULTI_CHART_COUNT,
  MULTI_CHART_GRID_PRESETS,
  normalizeMultiChartGridPreset,
  presetSlotCount,
  sortSlotsByGridIndex,
} from '../../utils/multiChartLayoutUtils';
import type { MultiChartGridPreset } from '../../utils/multiChartLayoutUtils';
import {
  bollingerStoreToColorOverrides,
  ichimokuStoreToChartSettings,
  ichimokuStoreToColorOverrides,
} from '../../utils/multiChartToolbarSettings';

const GRID_LAYOUT_OPTIONS: { value: MultiChartGridPreset; label: string }[] = MULTI_CHART_GRID_PRESETS.map((p) => ({
  value: p,
  label: p,
}));

/** 2·3열 및 1열 최소 보장 높이 */
const MIN_CHART_CELL_HEIGHT = 280;
/** 1열: 차트 영역 세로 패딩 p:1 상하 (theme.spacing(1)×2) */
const CHART_AREA_PAD_Y = 16;
/** 1열 뷰포트 기반 높이 하한 (매우 작은 창 대비) */
const ONE_COL_CELL_MIN_HEIGHT = 200;

const GRID_ADD_DROP_ID = 'multi-chart-grid-add';

/** 그리드 공용: 첫 빈 칸에 종목 추가 (클릭 / 드롭) */
const GridAddDropStrip: React.FC<{ onClickPick: () => void }> = ({ onClickPick }) => {
  const { setNodeRef, isOver } = useDroppable({ id: GRID_ADD_DROP_ID });
  return (
    <Box
      ref={setNodeRef}
      onClick={onClickPick}
      role="button"
      tabIndex={0}
      onKeyDown={(e) => {
        if (e.key === 'Enter' || e.key === ' ') {
          e.preventDefault();
          onClickPick();
        }
      }}
      sx={{
        minHeight: 96,
        border: '2px dashed',
        borderColor: isOver ? 'primary.main' : 'divider',
        borderRadius: 1,
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        cursor: 'pointer',
        bgcolor: isOver ? 'action.hover' : 'action.hover',
        flexShrink: 0,
      }}
    >
      <Typography variant="body2" color="text.secondary">
        + 차트 추가 (클릭 또는 종목 드래그)
      </Typography>
    </Box>
  );
};

/** 차트 유형: 사용자 / 상승추세 순 / 거래량 순 / 알림종목 */
const CHART_TYPE_OPTIONS: { value: ChartType; label: string }[] = [
  { value: 'user', label: '사용자' },
  { value: 'uptrend', label: '상승추세' },
  { value: 'volume', label: '거래량' },
  { value: 'alert', label: '알림종목' },
];

/** 투자화면(CandlestickTestPage)과 동일한 시간봉 목록 */
const TIMEFRAME_OPTIONS = [
  { value: '1m', label: '1분' },
  { value: '3m', label: '3분' },
  { value: '5m', label: '5분' },
  { value: '10m', label: '10분' },
  { value: '15m', label: '15분' },
  { value: '30m', label: '30분' },
  { value: '1h', label: '1시간' },
  { value: '4h', label: '4시간' },
  { value: '1d', label: '일봉' },
];

/** 멀티차트 위치별 기본 시간봉 (첫번째=3분, 두번째=5분, 세번째=10분, ...) */
const DEFAULT_SLOT_TIMEFRAMES = ['3m', '5m', '10m', '15m', '30m', '1h', '4h', '1d', '1m'];

/** 슬롯 인덱스에 따라 기본 시간봉 반환 */
const getDefaultTimeframeForSlot = (slotIndex: number): string => {
  return DEFAULT_SLOT_TIMEFRAMES[slotIndex % DEFAULT_SLOT_TIMEFRAMES.length];
};

export const MultiChartDashboard: React.FC = () => {
  const [selectDialogOpen, setSelectDialogOpen] = React.useState(false);
  const [selectTargetIndex, setSelectTargetIndex] = React.useState<number | null>(null);
  const [stockMenuOpen, setStockMenuOpen] = React.useState(false);
  const [isDraggingFromMenu, setIsDraggingFromMenu] = React.useState(false);
  const [activeDraggedStock, setActiveDraggedStock] = React.useState<StockInfo | null>(null);
  const [chartDragPreview, setChartDragPreview] = React.useState<StockInfo | null>(null);
  const [loadingChartType, setLoadingChartType] = React.useState(false);
  const [refreshTrigger, setRefreshTrigger] = React.useState(0);
  const [settingsMenuAnchor, setSettingsMenuAnchor] = React.useState<null | HTMLElement>(null);
  const importInputRef = React.useRef<HTMLInputElement | null>(null);
  const [isFullscreen, setIsFullscreen] = React.useState(false);
  const [chartFullscreenSlotId, setChartFullscreenSlotId] = React.useState<string | null>(null);

  const [maMenuAnchor, setMaMenuAnchor] = React.useState<null | HTMLElement>(null);
  const [ichimokuMenuAnchor, setIchimokuMenuAnchor] = React.useState<null | HTMLElement>(null);
  const [bollingerMenuAnchor, setBollingerMenuAnchor] = React.useState<null | HTMLElement>(null);
  const [maSettingsOpen, setMaSettingsOpen] = React.useState(false);
  const [ichimokuSettingsOpen, setIchimokuSettingsOpen] = React.useState(false);
  const [bollingerSettingsOpen, setBollingerSettingsOpen] = React.useState(false);
  const [snackbar, setSnackbar] = React.useState<{ open: boolean; message: string; severity: 'success' | 'error' | 'info' }>({ open: false, message: '', severity: 'success' });
  const [latestAlertSymbols, setLatestAlertSymbols] = React.useState<Set<string>>(new Set());

  const {
    layout,
    slots,
    timeframe,
    slotTimeframes,
    slotIndicatorSettings,
    slotIndicatorOrder,
    slotCandleHeightRatio,
    chartType,
    gridLayout,
    multiChartMaLines,
    multiChartIchimoku,
    multiChartBollinger,
    setGridLayout,
    appendStockOneColumn,
    setStockAtGridIndex,
    setTimeframe,
    setSlotTimeframe,
    setSlotIndicatorSettings,
    setSlotIndicatorOrder,
    setSlotCandleHeightRatio,
    setChartType,
    setSlotsFromStocks,
    clearSlot,
    swapSlots,
    exportConfig,
    importConfig,
    setMultiChartMaLines,
    setMultiChartIchimoku,
    setMultiChartBollinger,
  } = useDashboardStore();
  const { navigateToPage } = useNavigation();

  /** 차트유형 이전 값 — 사용자 모드 첫 진입과 '자동→사용자' 전환 구분 */
  const prevChartTypeRef = React.useRef<ChartType | null>(null);

  const sortedSlots = useMemo(() => sortSlotsByGridIndex(slots), [slots]);
  const stockCountInGrid = useMemo(() => countStocksInSlots(slots), [slots]);
  const ichimokuChartForCards = useMemo(() => ichimokuStoreToChartSettings(multiChartIchimoku), [multiChartIchimoku]);
  const mcChartColorOverrides = useMemo(
    () => ({
      ...ichimokuStoreToColorOverrides(multiChartIchimoku),
      ...bollingerStoreToColorOverrides(multiChartBollinger),
    }),
    [multiChartIchimoku, multiChartBollinger]
  );
  const mcMaDialogToolbar = useMemo(
    () => ({ maLines: multiChartMaLines, setMaLines: setMultiChartMaLines }),
    [multiChartMaLines, setMultiChartMaLines]
  );
  const mcIchimokuDialogToolbar = useMemo(
    () => ({ ichimoku: multiChartIchimoku, setIchimoku: setMultiChartIchimoku }),
    [multiChartIchimoku, setMultiChartIchimoku]
  );
  const mcBollingerDialogToolbar = useMemo(
    () => ({ bollinger: multiChartBollinger, setBollinger: setMultiChartBollinger }),
    [multiChartBollinger, setMultiChartBollinger]
  );
  const slotCount = MAX_MULTI_CHART_COUNT;

  const cancelledRef = React.useRef(false);
  const loadAndApply = useCallback(async (ignoreCancelled = false) => {
    const targetType = chartType;
    if (targetType === 'user') {
      setLatestAlertSymbols(new Set());
      return;
    }
    if (targetType !== 'alert') setLatestAlertSymbols(new Set());
    setLoadingChartType(true);
    try {
      type SortItem = { symbol: string; koreanName: string; changeRate?: number; volume?: number };
      let sorted: SortItem[] = [];

      // 상승추세/거래량: RisingStock API 사용 (trendScore/volumeScore - 차트 추세와 일치)
      if (targetType === 'uptrend' || targetType === 'volume') {
        const risingStocks = await getTopRisingStocks(
          slotCount,
          targetType === 'uptrend' ? 'trendScore' : 'volumeScore'
        );
        if (!ignoreCancelled && cancelledRef.current) return;
        if (risingStocks.length > 0) {
          sorted = risingStocks.map((s) => ({
            symbol: s.code,
            koreanName: s.name ?? s.code,
            changeRate: s.changeRate != null ? Number(s.changeRate) : undefined,
            volume: s.volume != null ? Number(s.volume) : undefined,
          }));
        }
      }

      // 알림종목: 통합 알림 메시지에서 모든 종목 추출, 최신순, symbol 중복 제거
      if (targetType === 'alert') {
        const [notifRes, cryptos] = await Promise.all([
          getNotifications(undefined, 0, 80),
          upbitApiService.getActiveCryptos(),
        ]);
        if (!ignoreCancelled && cancelledRef.current) return;
        const koreanToSymbol = new Map<string, string>();
        cryptos.forEach((c) => koreanToSymbol.set(c.koreanName, c.symbol));

        type AlertItem = { symbol: string; koreanName: string; triggeredAt: number };
        const items: AlertItem[] = [];
        const sortedByTime = [...notifRes.content].sort((a, b) => {
          const at = a.triggeredAt ? new Date(a.triggeredAt).getTime() : 0;
          const bt = b.triggeredAt ? new Date(b.triggeredAt).getTime() : 0;
          return bt - at;
        });

        for (const n of sortedByTime) {
          const triggeredAt = n.triggeredAt ? new Date(n.triggeredAt).getTime() : 0;
          const assetNames = extractAssetNamesFromMessage(n.message || '');
          if (assetNames && assetNames.length > 0) {
            const resolved = resolveSymbols(assetNames, koreanToSymbol);
            resolved.forEach((r) => items.push({ ...r, triggeredAt }));
          } else if (n.symbol && n.koreanName) {
            items.push({
              symbol: n.symbol,
              koreanName: n.koreanName,
              triggeredAt,
            });
          }
        }

        const alertSorted: SortItem[] = [];
        const seen = new Set<string>();
        for (const it of items) {
          if (!seen.has(it.symbol)) {
            seen.add(it.symbol);
            alertSorted.push({ symbol: it.symbol, koreanName: it.koreanName });
          }
        }
        if (alertSorted.length > 0) sorted = alertSorted;

        const latest = new Set<string>();
        const firstNotif = sortedByTime[0];
        if (firstNotif) {
          const assetNames = extractAssetNamesFromMessage(firstNotif.message || '');
          if (assetNames && assetNames.length > 0) {
            const resolved = resolveSymbols(assetNames, koreanToSymbol);
            resolved.forEach((r) => latest.add(r.symbol));
          } else if (firstNotif.symbol) {
            latest.add(firstNotif.symbol);
          }
        }
        setLatestAlertSymbols(latest);
      }

      // RisingStock/알림종목 결과 없으면 기존 Upbit ticker 폴백 (알림종목은 건너뜀)
      if (sorted.length === 0 && targetType !== 'alert') {
        const list = await upbitApiService.getTopTickers(targetType as 'uptrend' | 'volume', slotCount);
        if (!ignoreCancelled && cancelledRef.current) return;
        if (list.length > 0) {
          const tickers = await upbitApiService.getTickers(list.map((t) => t.market));
          if (!ignoreCancelled && cancelledRef.current) return;
          const tickerMap = new Map(tickers.map((t) => [t.market, t]));
          sorted = list.map((t) => {
            const tk = tickerMap.get(t.market);
            return {
              symbol: t.market,
              koreanName: t.koreanName ?? t.market,
              changeRate: tk ? (tk.signedChangeRate ?? 0) * 100 : undefined,
              volume: tk?.accTradeVolume24h,
            };
          });
        } else {
          const markets = MARKET_LIST.map((m) => m.symbol);
          const tickers = await upbitApiService.getTickers(markets);
          if (!ignoreCancelled && cancelledRef.current) return;
          const marketMap = new Map(MARKET_LIST.map((m) => [m.symbol, m]));
          if (targetType === 'uptrend') {
            sorted = tickers
              .sort((a, b) => (b.signedChangeRate ?? 0) - (a.signedChangeRate ?? 0))
              .slice(0, slotCount)
              .map((t) => ({
                symbol: t.market,
                koreanName: marketMap.get(t.market)?.koreanName ?? t.market,
                changeRate: (t.signedChangeRate ?? 0) * 100,
                volume: t.accTradeVolume24h,
              }));
          } else {
            sorted = tickers
              .sort((a, b) => (b.accTradeVolume24h ?? 0) - (a.accTradeVolume24h ?? 0))
              .slice(0, slotCount)
              .map((t) => ({
                symbol: t.market,
                koreanName: marketMap.get(t.market)?.koreanName ?? t.market,
                changeRate: (t.signedChangeRate ?? 0) * 100,
                volume: t.accTradeVolume24h,
              }));
          }
        }
      }

      // 알림종목 외: 데이터 없을 때 MARKET_LIST 폴백 (알림종목은 알림 없으면 빈 슬롯 유지)
      if (sorted.length === 0 && targetType !== 'alert') {
        sorted = MARKET_LIST.slice(0, slotCount).map((m) => ({
          symbol: m.symbol,
          koreanName: m.koreanName,
        }));
      }

      if (!ignoreCancelled && cancelledRef.current) return;
      const currentType = useDashboardStore.getState().chartType;
      if (currentType !== targetType) return;

      const stocks: StockInfo[] = sorted.map((s) => ({
        symbol: s.symbol,
        koreanName: s.koreanName,
        changeRate: s.changeRate,
        volume: s.volume,
      }));
      setSlotsFromStocks(stocks);
    } catch (e) {
      if (!ignoreCancelled && cancelledRef.current) return;
      console.error('[MultiChart] 차트유형 데이터 로드 실패:', e);
      const currentType = useDashboardStore.getState().chartType;
      if (currentType !== targetType) return;
      const fallback = MARKET_LIST.slice(0, slotCount).map((m) => ({
        symbol: m.symbol,
        koreanName: m.koreanName,
      }));
      setSlotsFromStocks(fallback);
    } finally {
      if (!cancelledRef.current) setLoadingChartType(false);
    }
  }, [chartType, setSlotsFromStocks]);

  useEffect(() => {
    if (chartType !== 'user') {
      prevChartTypeRef.current = chartType;
      cancelledRef.current = false;
      loadAndApply();
      return () => {
        cancelledRef.current = true;
      };
    }

    const switchedFromAutoToUser =
      prevChartTypeRef.current != null && prevChartTypeRef.current !== 'user';
    prevChartTypeRef.current = chartType;

    // persist 재수화 전에 setMultiChart* 등이 호출되면 setItem이 초기 슬롯으로 저장해 기존 종목이 지워질 수 있음 → 재수화 완료 후에만 실행
    const afterHydrate = () => {
      if (useDashboardStore.getState().chartType !== 'user') return;
      try {
        const rawMa = localStorage.getItem('multiChartMaLines');
        if (rawMa) {
          const p = JSON.parse(rawMa) as unknown;
          if (Array.isArray(p)) useDashboardStore.getState().setMultiChartMaLines(p as MALineConfig[]);
          localStorage.removeItem('multiChartMaLines');
        }
        const rawIch = localStorage.getItem('multiChartIchimoku');
        if (rawIch) {
          const ich = JSON.parse(rawIch) as Record<string, unknown>;
          useDashboardStore.getState().setMultiChartIchimoku(mergeIchimokuPersisted(ich));
          localStorage.removeItem('multiChartIchimoku');
        }
        const rawBol = localStorage.getItem('multiChartBollinger');
        if (rawBol) {
          const bol = JSON.parse(rawBol) as Record<string, unknown>;
          useDashboardStore.getState().setMultiChartBollinger(mergeBollingerPersisted(bol));
          localStorage.removeItem('multiChartBollinger');
        }
      } catch (_) {}
      // 슬롯은 persist merge로 이미 복원됨. 매 마운트마다 restoreUserSlots를 호출하면 userSlots 없을 때 빈 그리드로 덮어쓰므로, '자동→사용자' 전환 때만 복원
      if (switchedFromAutoToUser) {
        useDashboardStore.getState().restoreUserSlots();
      }
    };

    if (useDashboardStore.persist.hasHydrated()) {
      afterHydrate();
      return undefined;
    }
    return useDashboardStore.persist.onFinishHydration(afterHydrate);
  }, [chartType, loadAndApply]);

  const handleRefresh = useCallback(() => {
    setRefreshTrigger((prev) => prev + 1);
    if (chartType === 'uptrend' || chartType === 'volume' || chartType === 'alert') {
      loadAndApply(true);
    }
  }, [chartType, loadAndApply]);

  const handleExportConfig = useCallback(() => {
    const json = exportConfig();
    const blob = new Blob([json], { type: 'application/json' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `multi-chart-config-${new Date().toISOString().slice(0, 10)}.json`;
    a.click();
    URL.revokeObjectURL(url);
    setSettingsMenuAnchor(null);
    setSnackbar({ open: true, message: '설정 파일이 다운로드되었습니다.', severity: 'success' });
  }, [exportConfig]);

  const handleImportConfig = useCallback(() => {
    importInputRef.current?.click();
    setSettingsMenuAnchor(null);
  }, []);

  const handleImportFileChange = useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      const file = e.target.files?.[0];
      if (!file) return;
      const reader = new FileReader();
      reader.onload = () => {
        try {
          const text = reader.result as string;
          const parsed = JSON.parse(text) as Record<string, unknown>;
          if (!parsed || typeof parsed !== 'object') {
            setSnackbar({ open: true, message: '잘못된 설정 파일 형식입니다.', severity: 'error' });
            return;
          }
          if (importConfig(JSON.stringify(parsed))) {
            setRefreshTrigger((prev) => prev + 1);
            setSnackbar({ open: true, message: '설정이 적용되었습니다.', severity: 'success' });
          } else {
            setSnackbar({ open: true, message: '설정 적용에 실패했습니다.', severity: 'error' });
          }
        } catch {
          setSnackbar({ open: true, message: '설정 파일을 읽을 수 없습니다.', severity: 'error' });
        }
        (e.target as HTMLInputElement).value = '';
      };
      reader.onerror = () => {
        setSnackbar({ open: true, message: '파일을 읽는 중 오류가 발생했습니다.', severity: 'error' });
        (e.target as HTMLInputElement).value = '';
      };
      reader.readAsText(file);
    },
    [importConfig]
  );

  const handleFullscreenToggle = useCallback(() => {
    setIsFullscreen((prev) => !prev);
  }, []);

  const handleChartFullscreenToggle = useCallback((slotId: string) => {
    setChartFullscreenSlotId((prev) => (prev === slotId ? null : slotId));
  }, []);

  const handleDetailClick = useCallback(
    (stock: StockInfo, slotTimeframe?: string) => {
      navigateToPage('candlestick-test', {
        symbol: stock.symbol,
        koreanName: stock.koreanName,
        timeInterval: slotTimeframe ?? timeframe,
      });
    },
    [navigateToPage, timeframe]
  );

  const openGridAdd = useCallback(() => {
    const st = useDashboardStore.getState();
    if (countStocksInSlots(st.slots) >= MAX_MULTI_CHART_COUNT) {
      setSnackbar({ open: true, message: `차트는 최대 ${MAX_MULTI_CHART_COUNT}개까지 추가할 수 있습니다.`, severity: 'info' });
      return;
    }
    const sorted = sortSlotsByGridIndex(st.slots);
    const firstEmpty = sorted.find((s) => s.stock == null);
    if (firstEmpty) {
      setSelectTargetIndex(firstEmpty.gridIndex);
      setSelectDialogOpen(true);
      return;
    }
    setSnackbar({ open: true, message: '빈 차트 칸이 없습니다. 종목을 제거한 뒤 추가하세요.', severity: 'info' });
  }, []);

  const handleAddClick = (gridIndex: number) => {
    setSelectTargetIndex(gridIndex);
    setSelectDialogOpen(true);
  };

  /** 종목 선택 드롭다운에서 종목 클릭 시 - 기존 차트 제거하고 선택한 종목을 여러 시간봉으로 표시 */
  const handleStockSelectFromDropdown = (stock: StockInfo) => {
    // 선택한 종목을 여러 시간봉으로 표시 (컬럼 수만큼)
    // 예: 2열 모드면 2개, 3열 모드면 3개... 최대 9개(DEFAULT_SLOT_TIMEFRAMES 길이)
    const cap = presetSlotCount(gridLayout);
    const numCharts = Math.min(cap, DEFAULT_SLOT_TIMEFRAMES.length, MAX_MULTI_CHART_COUNT);
    const stocksArray = Array(numCharts).fill(stock);
    
    // 기존 차트를 모두 제거하고 선택한 종목의 차트들로 교체
    // setSlotsFromStocks는 자동으로 위치별 시간봉을 할당함
    setSlotsFromStocks(stocksArray);
  };

  const handleSelectStock = (stock: StockInfo) => {
    if (selectTargetIndex !== null) {
      const st = useDashboardStore.getState();
      const slot = st.slots.find((s) => s.gridIndex === selectTargetIndex);
      const emptyTarget = slot?.stock == null;
      if (emptyTarget && countStocksInSlots(st.slots) >= MAX_MULTI_CHART_COUNT) {
        setSnackbar({ open: true, message: `차트는 최대 ${MAX_MULTI_CHART_COUNT}개까지 추가할 수 있습니다.`, severity: 'info' });
        setSelectDialogOpen(false);
        setSelectTargetIndex(null);
        return;
      }
      setStockAtGridIndex(selectTargetIndex, stock);
      setSelectTargetIndex(null);
    }
  };

  const sensors = useSensors(
    useSensor(PointerSensor, {
      activationConstraint: { distance: 8 },
    })
  );

  const handleDragStart = (e: DragStartEvent) => {
    const activeId = String(e.active.id);
    if (activeId.startsWith('chart-title-')) {
      const stock = e.active.data.current?.stock as StockInfo | undefined;
      if (stock) setChartDragPreview(stock);
      return;
    }
    if (activeId.startsWith('stock-')) {
      setIsDraggingFromMenu(true);
      const stock = e.active.data.current?.stock as StockInfo | undefined;
      setActiveDraggedStock(stock ?? null);
    }
  };

  const handleDragOver = (_e: DragOverEvent) => {
    // optional: highlight drop target
  };

  const handleDragCancel = () => {
    setIsDraggingFromMenu(false);
    setStockMenuOpen(false);
    setActiveDraggedStock(null);
    setChartDragPreview(null);
  };

  const handleDragEnd = (e: DragEndEvent) => {
    const { active, over } = e;
    const activeId = String(active.id);
    if (activeId.startsWith('stock-')) {
      setIsDraggingFromMenu(false);
      setStockMenuOpen(false);
      setActiveDraggedStock(null);
    }
    if (activeId.startsWith('chart-title-')) {
      setChartDragPreview(null);
      if (over) {
        const overId = String(over.id);
        const slotId = active.data.current?.slotId as string | undefined;
        if (slotId && overId !== GRID_ADD_DROP_ID && slotId !== overId) {
          const sorted = sortSlotsByGridIndex(useDashboardStore.getState().slots);
          const fromIdx = sorted.findIndex((s) => s.id === slotId);
          const toIdx = sorted.findIndex((s) => s.id === overId);
          if (fromIdx >= 0 && toIdx >= 0) {
            swapSlots(fromIdx, toIdx);
          }
        }
      }
      return;
    }
    if (!over) return;

    const overId = String(over.id);

    if (activeId.startsWith('stock-')) {
      const stock = active.data.current?.stock as StockInfo | undefined;
      if (!stock) return;
      if (overId === GRID_ADD_DROP_ID) {
        if (countStocksInSlots(useDashboardStore.getState().slots) >= MAX_MULTI_CHART_COUNT) {
          setSnackbar({ open: true, message: `차트는 최대 ${MAX_MULTI_CHART_COUNT}개까지 추가할 수 있습니다.`, severity: 'info' });
          return;
        }
        appendStockOneColumn(stock);
        return;
      }
      const hitSlot = useDashboardStore.getState().slots.find((s) => s.id === overId);
      if (hitSlot) {
        const stDrop = useDashboardStore.getState();
        if (hitSlot.stock == null && countStocksInSlots(stDrop.slots) >= MAX_MULTI_CHART_COUNT) {
          setSnackbar({ open: true, message: `차트는 최대 ${MAX_MULTI_CHART_COUNT}개까지 추가할 수 있습니다.`, severity: 'info' });
          return;
        }
        setStockAtGridIndex(hitSlot.gridIndex, stock);
      }
    }
  };


  const [gridWidth, setGridWidth] = useState(1200);
  const [chartAreaHeight, setChartAreaHeight] = useState(400);
  const chartAreaRef = React.useRef<HTMLDivElement | null>(null);

  useEffect(() => {
    const updateWidth = () => {
      const w = window.innerWidth || 1200;
      setGridWidth(Math.max(400, isFullscreen ? w - 24 : w - 24));
    };
    updateWidth();
    window.addEventListener('resize', updateWidth);
    return () => window.removeEventListener('resize', updateWidth);
  }, [isFullscreen]);

  useEffect(() => {
    const el = chartAreaRef.current;
    if (!el) return;
    const ro = new ResizeObserver(() => {
      setChartAreaHeight(el.clientHeight);
    });
    ro.observe(el);
    setChartAreaHeight(el.clientHeight);
    return () => ro.disconnect();
  }, [chartFullscreenSlotId]);

  useEffect(() => {
    if (isFullscreen) {
      document.body.style.overflow = 'hidden';
    } else {
      document.body.style.overflow = '';
    }
    return () => { document.body.style.overflow = ''; };
  }, [isFullscreen]);

  useEffect(() => {
    if (!isFullscreen && !chartFullscreenSlotId) return;
    const handleEsc = (e: KeyboardEvent) => {
      if (e.key === 'Escape') {
        if (chartFullscreenSlotId) setChartFullscreenSlotId(null);
        else setIsFullscreen(false);
      }
    };
    window.addEventListener('keydown', handleEsc);
    return () => window.removeEventListener('keydown', handleEsc);
  }, [isFullscreen, chartFullscreenSlotId]);

  // 차트 전체화면 중 해당 슬롯이 제거되면 전체화면 해제
  useEffect(() => {
    if (chartFullscreenSlotId && !slots.some((s) => s.id === chartFullscreenSlotId && s.stock)) {
      setChartFullscreenSlotId(null);
    }
  }, [chartFullscreenSlotId, slots]);

  /** 1행 그리드: 세로 공간을 한 행에 몰아 줌. 그 외: 셀 최소 높이 */
  const chartCellHeight = useMemo(() => {
    if (gridRowsForPreset(gridLayout) !== 1) return MIN_CHART_CELL_HEIGHT;
    const padY = chartFullscreenSlotId ? 0 : CHART_AREA_PAD_Y;
    const inner = Math.max(0, chartAreaHeight - padY);
    return Math.max(ONE_COL_CELL_MIN_HEIGHT, inner);
  }, [gridLayout, chartAreaHeight, chartFullscreenSlotId]);

  /** 차트 타이틀 드래그 오버레이 크기 (실제 그리드 셀에 맞춤) */
  const chartOverlaySize = useMemo(
    () => ({
      width: Math.max(200, Math.min(560, (gridWidth - 48) / Math.max(1, gridColsForPreset(gridLayout)) - 12)),
      height: Math.max(200, Math.min(480, chartCellHeight)),
    }),
    [gridWidth, gridLayout, chartCellHeight]
  );

  // ✅ 레이아웃/종목/시간봉 변경 시 구독 목록 안정적 계산 (useMemo로 참조 안정화)
  const marketSubscriptions = useMemo(() => {
    const subs = slots
      .filter((s) => s.stock && s.stock.symbol)
      .map((s) => ({
        market: s.stock!.symbol,
        timeframe: slotTimeframes[s.id] ?? timeframe,
        koreanName: s.stock!.koreanName,
      }));
    console.log('📊 [MultiChart] marketSubscriptions 생성:', { subs, slotsCount: slots.length, slotsWithStock: slots.filter(s => s.stock).length });
    return subs;
  }, [slots, slotTimeframes, timeframe]);

  return (
    <MultiChartMarketDataProvider subscriptions={marketSubscriptions}>
    <DndContext
      sensors={sensors}
      collisionDetection={closestCenter}
      onDragStart={handleDragStart}
      onDragOver={handleDragOver}
      onDragEnd={handleDragEnd}
      onDragCancel={handleDragCancel}
    >
      <Box
        sx={{
          display: 'flex',
          flexDirection: 'column',
          height: isFullscreen ? '100vh' : 'calc(100vh - 64px)',
          bgcolor: 'background.default',
          overflow: 'hidden',
          ...(isFullscreen && {
            position: 'fixed',
            inset: 0,
            zIndex: 9999,
          }),
        }}
      >
        {!isFullscreen && (
        <Box
          sx={{
            display: 'flex',
            alignItems: 'center',
            gap: 2,
            py: 1,
            px: 1,
            flexShrink: 0,
            borderBottom: '1px solid',
            borderColor: 'divider',
            bgcolor: 'background.paper',
          }}
        >
          <StockSelectDropdown
            open={stockMenuOpen}
            onOpen={() => setStockMenuOpen(true)}
            onClose={() => setStockMenuOpen(false)}
            onSelect={handleStockSelectFromDropdown}
            isDraggingFromMenu={isDraggingFromMenu}
          />
          <FormControl size="small" sx={{ minWidth: 88, '& .MuiInputBase-root': { fontSize: '12px' }, '& .MuiSelect-select': { py: 0.375, fontSize: '12px' } }}>
            <Select
              value={gridLayout}
              displayEmpty
              onChange={(e) => setGridLayout(normalizeMultiChartGridPreset(e.target.value))}
            >
              {GRID_LAYOUT_OPTIONS.map((o) => (
                <MenuItem key={o.value} value={o.value}>
                  {o.label}
                </MenuItem>
              ))}
            </Select>
          </FormControl>
          <FormControl size="small" sx={{ minWidth: 80, '& .MuiInputBase-root': { fontSize: '12px' }, '& .MuiSelect-select': { py: 0.375, fontSize: '12px' } }}>
            <Select value={timeframe} displayEmpty onChange={(e) => setTimeframe(e.target.value)}>
              {TIMEFRAME_OPTIONS.map((o) => (
                <MenuItem key={o.value} value={o.value}>
                  {o.label}
                </MenuItem>
              ))}
            </Select>
          </FormControl>
          <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
            {loadingChartType && <CircularProgress size={18} />}
            <FormControl size="small" sx={{ minWidth: 110, '& .MuiInputBase-root': { fontSize: '12px' }, '& .MuiSelect-select': { py: 0.375, fontSize: '12px' } }}>
              <Select
                value={chartType}
                displayEmpty
                onChange={(e) => setChartType(e.target.value as ChartType)}
                disabled={loadingChartType}
              >
                {CHART_TYPE_OPTIONS.map((o) => (
                  <MenuItem key={o.value} value={o.value}>
                    {o.label}
                  </MenuItem>
                ))}
              </Select>
            </FormControl>
          </Box>
          <Box sx={{ flex: 1 }} />
          {/* 이동평균선 / 일목균형표 / 볼린저밴드 선택 */}
          <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
            <Tooltip title={`이동평균선 (${multiChartMaLines.filter((m) => m.enabled).length}개)`} arrow>
              <Button
                variant="outlined"
                size="small"
                onClick={(e) => setMaMenuAnchor(e.currentTarget)}
                startIcon={<ShowChartIcon sx={{ fontSize: 18 }} />}
                endIcon={<KeyboardArrowDown />}
                sx={{ fontSize: '12px', textTransform: 'none', borderColor: 'divider', px: 1, py: 0.5 }}
              >
                MA({multiChartMaLines.filter((m) => m.enabled).length})
              </Button>
            </Tooltip>
            <Tooltip title={`일목균형표 (${[multiChartIchimoku.tenkan, multiChartIchimoku.kijun, multiChartIchimoku.chikou, multiChartIchimoku.senkouA, multiChartIchimoku.senkouB].filter(Boolean).length}개)`} arrow>
              <Button
                variant="outlined"
                size="small"
                onClick={(e) => setIchimokuMenuAnchor(e.currentTarget)}
                startIcon={<TimelineIcon sx={{ fontSize: 18 }} />}
                endIcon={<KeyboardArrowDown />}
                sx={{ fontSize: '12px', textTransform: 'none', borderColor: 'divider', px: 1, py: 0.5 }}
              >
                일목({[multiChartIchimoku.tenkan, multiChartIchimoku.kijun, multiChartIchimoku.chikou, multiChartIchimoku.senkouA, multiChartIchimoku.senkouB].filter(Boolean).length})
              </Button>
            </Tooltip>
            <Tooltip title={`볼린저 밴드 (${[multiChartBollinger.upper, multiChartBollinger.middle, multiChartBollinger.lower].filter(Boolean).length}개)`} arrow>
              <Button
                variant="outlined"
                size="small"
                onClick={(e) => setBollingerMenuAnchor(e.currentTarget)}
                startIcon={<ShowChartIcon sx={{ fontSize: 18 }} />}
                endIcon={<KeyboardArrowDown />}
                sx={{ fontSize: '12px', textTransform: 'none', borderColor: 'divider', px: 1, py: 0.5 }}
              >
                볼린저({[multiChartBollinger.upper, multiChartBollinger.middle, multiChartBollinger.lower].filter(Boolean).length})
              </Button>
            </Tooltip>
          </Box>
          <MultiChartWebSocketStatus />
          <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
            <Tooltip title="설정 (내보내기/가져오기)">
              <IconButton size="small" onClick={(e) => setSettingsMenuAnchor(e.currentTarget)} aria-label="설정">
                <SettingsIcon fontSize="small" />
              </IconButton>
            </Tooltip>
            <Menu anchorEl={settingsMenuAnchor} open={Boolean(settingsMenuAnchor)} onClose={() => setSettingsMenuAnchor(null)} anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }} transformOrigin={{ vertical: 'top', horizontal: 'right' }}>
              <MenuItem onClick={handleExportConfig}>
                <FileDownloadIcon fontSize="small" sx={{ mr: 1 }} />
                설정 내보내기 (JSON 파일)
              </MenuItem>
              <MenuItem onClick={handleImportConfig}>
                <FileUploadIcon fontSize="small" sx={{ mr: 1 }} />
                설정 가져오기 (JSON 파일)
              </MenuItem>
            </Menu>
            <input ref={importInputRef} type="file" accept=".json,application/json" style={{ display: 'none' }} onChange={handleImportFileChange} />
            <Tooltip title="차트 새로고침">
              <IconButton size="small" onClick={handleRefresh} disabled={loadingChartType} aria-label="차트 새로고침">
                <RefreshIcon fontSize="small" />
              </IconButton>
            </Tooltip>
            <Tooltip title="전체화면">
              <IconButton size="small" onClick={handleFullscreenToggle} aria-label="전체화면">
                <FullscreenIcon fontSize="small" />
              </IconButton>
            </Tooltip>
          </Box>
        </Box>
        )}

        {isFullscreen && (
          <Box
            sx={{
              position: 'absolute',
              top: 8,
              right: 8,
              zIndex: 10,
              display: 'flex',
              alignItems: 'center',
              gap: 1,
              bgcolor: 'background.paper',
              borderRadius: 1,
              boxShadow: 1,
              p: 0.5,
            }}
          >
            <MultiChartWebSocketStatus />
            <Tooltip title="전체화면 종료">
              <IconButton size="small" onClick={handleFullscreenToggle} aria-label="전체화면 종료">
                <FullscreenExitIcon fontSize="small" />
              </IconButton>
            </Tooltip>
          </Box>
        )}

        <Box ref={chartAreaRef} sx={{ flex: 1, minHeight: 0, p: chartFullscreenSlotId ? 0 : 1, overflow: chartFullscreenSlotId ? 'hidden' : 'auto' }}>
          {chartFullscreenSlotId && slots.some((s) => s.id === chartFullscreenSlotId && s.stock) ? (
            <Box sx={{ height: '100%', minHeight: 0, display: 'flex', flexDirection: 'column' }}>
              {sortedSlots
                .filter((s) => s.id === chartFullscreenSlotId)
                .map((slot) => (
                  <Box key={slot.id} sx={{ flex: 1, minHeight: 0, overflow: 'hidden', display: 'flex', flexDirection: 'column' }}>
                    <ChartItemInvestment
                      id={slot.id}
                      stock={slot.stock}
                      gridIndex={slot.gridIndex}
                      timeframe={slotTimeframes[slot.id] ?? timeframe}
                      refreshTrigger={refreshTrigger}
                      onAddClick={() => handleAddClick(slot.gridIndex)}
                      onRemove={() => clearSlot(slot.gridIndex)}
                      onDetailClick={handleDetailClick}
                      onChartFullscreen={() => handleChartFullscreenToggle(slot.id)}
                      isChartFullscreen
                      fullscreenHeight={Math.min(chartAreaHeight - 56, Math.max(280, Math.round(gridWidth * 0.52)))}
                      onTimeframeChange={(slotId, tf) => setSlotTimeframe(slotId, tf)}
                      indicatorSettings={slotIndicatorSettings[slot.id]}
                      onIndicatorSettingsChange={(s) => setSlotIndicatorSettings(slot.id, s)}
                      indicatorOrder={slotIndicatorOrder[slot.id]}
                      onIndicatorOrderChange={(o) => setSlotIndicatorOrder(slot.id, o)}
                      candleHeightRatio={slotCandleHeightRatio[slot.id] ?? 0.6}
                      onCandleHeightRatioChange={(r) => setSlotCandleHeightRatio(slot.id, r)}
                      maLines={multiChartMaLines}
                      ichimokuSettings={ichimokuChartForCards}
                      chartColorOverrides={mcChartColorOverrides}
                      overlaySettingsOverride={[multiChartBollinger.upper, multiChartBollinger.middle, multiChartBollinger.lower].some(Boolean) ? { bollingerUpper: multiChartBollinger.upper, bollingerMiddle: multiChartBollinger.middle, bollingerLower: multiChartBollinger.lower, bollingerPeriod: multiChartBollinger.period, bollingerStdDev: multiChartBollinger.stdDev } : undefined}
                      isLatestAlert={chartType === 'alert' && !!slot.stock && latestAlertSymbols.has(slot.stock.symbol)}
                      enableTitleBarReorder={false}
                    />
                  </Box>
                ))}
            </Box>
          ) : (
            <Box
              sx={{
                display: 'grid',
                gridTemplateColumns: `repeat(${gridColsForPreset(gridLayout)}, minmax(0, 1fr))`,
                gap: 1,
                alignContent: 'start',
                minWidth: 0,
                pb: 1,
              }}
            >
              {sortedSlots.map((slot) => (
                <Box
                  key={slot.id}
                  sx={{
                    minHeight: chartCellHeight,
                    height: chartCellHeight,
                    minWidth: 0,
                    overflow: 'hidden',
                    display: 'flex',
                    flexDirection: 'column',
                  }}
                >
                  <ChartItemInvestment
                    id={slot.id}
                    stock={slot.stock}
                    gridIndex={slot.gridIndex}
                    timeframe={slotTimeframes[slot.id] ?? timeframe}
                    refreshTrigger={refreshTrigger}
                    onAddClick={() => handleAddClick(slot.gridIndex)}
                    onRemove={() => clearSlot(slot.gridIndex)}
                    onDetailClick={handleDetailClick}
                    onChartFullscreen={() => handleChartFullscreenToggle(slot.id)}
                    isChartFullscreen={false}
                    onTimeframeChange={(slotId, tf) => setSlotTimeframe(slotId, tf)}
                    indicatorSettings={slotIndicatorSettings[slot.id]}
                    onIndicatorSettingsChange={(s) => setSlotIndicatorSettings(slot.id, s)}
                    indicatorOrder={slotIndicatorOrder[slot.id]}
                    onIndicatorOrderChange={(o) => setSlotIndicatorOrder(slot.id, o)}
                    candleHeightRatio={slotCandleHeightRatio[slot.id] ?? 0.6}
                    onCandleHeightRatioChange={(r) => setSlotCandleHeightRatio(slot.id, r)}
                    maLines={multiChartMaLines}
                    ichimokuSettings={ichimokuChartForCards}
                    chartColorOverrides={mcChartColorOverrides}
                    overlaySettingsOverride={[multiChartBollinger.upper, multiChartBollinger.middle, multiChartBollinger.lower].some(Boolean) ? { bollingerUpper: multiChartBollinger.upper, bollingerMiddle: multiChartBollinger.middle, bollingerLower: multiChartBollinger.lower, bollingerPeriod: multiChartBollinger.period, bollingerStdDev: multiChartBollinger.stdDev } : undefined}
                    isLatestAlert={chartType === 'alert' && !!slot.stock && latestAlertSymbols.has(slot.stock.symbol)}
                    enableTitleBarReorder
                  />
                </Box>
              ))}
              {sortSlotsByGridIndex(slots).some((s) => s.stock == null) && stockCountInGrid < MAX_MULTI_CHART_COUNT && (
                <Box sx={{ gridColumn: '1 / -1' }}>
                  <GridAddDropStrip onClickPick={openGridAdd} />
                </Box>
              )}
            </Box>
          )}
        </Box>
      </Box>

      <StockSelectDialog
        open={selectDialogOpen}
        onClose={() => { setSelectDialogOpen(false); setSelectTargetIndex(null); }}
        onSelect={handleSelectStock}
      />

      {/* MA 메뉴 */}
      <Menu anchorEl={maMenuAnchor} open={Boolean(maMenuAnchor)} onClose={() => setMaMenuAnchor(null)} PaperProps={{ sx: { minWidth: 180 } }}>
        {multiChartMaLines.map((line, idx) => (
          <MenuItem
            key={line.id}
            onClick={(e) => {
              e.stopPropagation();
              setMultiChartMaLines((prev) => {
                const next = [...prev];
                next[idx] = { ...next[idx], enabled: !next[idx].enabled };
                return next;
              });
            }}
            sx={{ py: 0.5 }}
          >
            <Checkbox checked={line.enabled} size="small" sx={{ p: 0.5, color: line.color, '&.Mui-checked': { color: line.color } }} />
            <Box sx={{ width: 12, height: 2, bgcolor: line.color, ml: 1, mr: 1 }} />
            <ListItemText primary={`${line.type}${line.period}`} primaryTypographyProps={{ fontSize: '13px' }} />
          </MenuItem>
        ))}
        <Divider />
        <MenuItem
          onClick={(e) => {
            e.stopPropagation();
            setMaMenuAnchor(null);
            setMaSettingsOpen(true);
          }}
          sx={{ py: 0.5 }}
        >
          <ListItemIcon>
            <SettingsIcon fontSize="small" />
          </ListItemIcon>
          <ListItemText primary="설정" primaryTypographyProps={{ fontSize: '13px' }} />
        </MenuItem>
      </Menu>

      {/* 일목균형표 메뉴 */}
      <Menu anchorEl={ichimokuMenuAnchor} open={Boolean(ichimokuMenuAnchor)} onClose={() => setIchimokuMenuAnchor(null)} PaperProps={{ sx: { minWidth: 150 } }}>
        {[
          { key: 'tenkan' as const, label: '전환선' },
          { key: 'kijun' as const, label: '기준선' },
          { key: 'chikou' as const, label: '후행스팬' },
          { key: 'senkouA' as const, label: '선행스팬1' },
          { key: 'senkouB' as const, label: '선행스팬2' },
        ].map(({ key, label }) => (
          <MenuItem
            key={key}
            onClick={(e) => {
              e.stopPropagation();
              setMultiChartIchimoku((prev) => ({ ...prev, [key]: !prev[key] }));
            }}
            sx={{ py: 0.5 }}
          >
            <Checkbox checked={multiChartIchimoku[key]} size="small" sx={{ p: 0.5 }} />
            <ListItemText primary={label} primaryTypographyProps={{ fontSize: '13px' }} />
          </MenuItem>
        ))}
        <Divider />
        <MenuItem
          onClick={(e) => {
            e.stopPropagation();
            setIchimokuMenuAnchor(null);
            setIchimokuSettingsOpen(true);
          }}
          sx={{ py: 0.5 }}
        >
          <ListItemIcon>
            <SettingsIcon fontSize="small" />
          </ListItemIcon>
          <ListItemText primary="설정" primaryTypographyProps={{ fontSize: '13px' }} />
        </MenuItem>
      </Menu>

      {/* 볼린저 밴드 메뉴 */}
      <Menu anchorEl={bollingerMenuAnchor} open={Boolean(bollingerMenuAnchor)} onClose={() => setBollingerMenuAnchor(null)} PaperProps={{ sx: { minWidth: 150 } }}>
        <MenuItem onClick={(e) => { e.stopPropagation(); setMultiChartBollinger((p) => ({ ...p, upper: !p.upper })); }} sx={{ py: 0.5 }}>
          <Checkbox checked={multiChartBollinger.upper} size="small" sx={{ p: 0.5 }} />
          <ListItemText primary="상한선 (Upper)" primaryTypographyProps={{ fontSize: '13px' }} />
        </MenuItem>
        <MenuItem onClick={(e) => { e.stopPropagation(); setMultiChartBollinger((p) => ({ ...p, middle: !p.middle })); }} sx={{ py: 0.5 }}>
          <Checkbox checked={multiChartBollinger.middle} size="small" sx={{ p: 0.5 }} />
          <ListItemText primary="중간선 (Middle)" primaryTypographyProps={{ fontSize: '13px' }} />
        </MenuItem>
        <MenuItem onClick={(e) => { e.stopPropagation(); setMultiChartBollinger((p) => ({ ...p, lower: !p.lower })); }} sx={{ py: 0.5 }}>
          <Checkbox checked={multiChartBollinger.lower} size="small" sx={{ p: 0.5 }} />
          <ListItemText primary="하한선 (Lower)" primaryTypographyProps={{ fontSize: '13px' }} />
        </MenuItem>
        <Divider />
        <MenuItem
          onClick={(e) => {
            e.stopPropagation();
            setBollingerMenuAnchor(null);
            setBollingerSettingsOpen(true);
          }}
          sx={{ py: 0.5 }}
        >
          <ListItemIcon>
            <SettingsIcon fontSize="small" />
          </ListItemIcon>
          <ListItemText primary="설정" primaryTypographyProps={{ fontSize: '13px' }} />
        </MenuItem>
      </Menu>

      <DragOverlay dropAnimation={null} style={{ zIndex: 10000 }}>
        {chartDragPreview ? (
          <ChartDragOverlayPreview
            stock={chartDragPreview}
            width={chartOverlaySize.width}
            height={chartOverlaySize.height}
          />
        ) : activeDraggedStock ? (
          <Chip
            label={`${activeDraggedStock.koreanName} (${activeDraggedStock.symbol})`}
            size="small"
            sx={{
              cursor: 'grabbing',
              boxShadow: 3,
              pointerEvents: 'none',
            }}
          />
        ) : null}
      </DragOverlay>
    </DndContext>
    <Snackbar
      open={snackbar.open}
      autoHideDuration={3000}
      onClose={() => setSnackbar((s) => ({ ...s, open: false }))}
      anchorOrigin={{ vertical: 'bottom', horizontal: 'center' }}
    >
      <Alert onClose={() => setSnackbar((s) => ({ ...s, open: false }))} severity={snackbar.severity} sx={{ width: '100%' }}>
        {snackbar.message}
      </Alert>
    </Snackbar>

    {/* MA 설정 다이얼로그 */}
    <MASettingsDialog
      open={maSettingsOpen}
      onClose={() => setMaSettingsOpen(false)}
      multiChartToolbar={mcMaDialogToolbar}
    />

    {/* Ichimoku 설정 다이얼로그 */}
    <IchimokuSettingsDialog
      open={ichimokuSettingsOpen}
      onClose={() => setIchimokuSettingsOpen(false)}
      multiChartToolbar={mcIchimokuDialogToolbar}
    />

    {/* Bollinger Bands 설정 다이얼로그 */}
    <BollingerSettingsDialog
      open={bollingerSettingsOpen}
      onClose={() => setBollingerSettingsOpen(false)}
      multiChartToolbar={mcBollingerDialogToolbar}
    />
    </MultiChartMarketDataProvider>
  );
};
