/**
 * 알림목록 화면
 * - 좌측: 알림 발생 최신순 조건 일치 종목 목록
 * - 우측: 1~3열 그리드 차트 (멀티차트와 동일 열 표시)
 */
import React, { useCallback, useState, useEffect, useMemo } from 'react';
import {
  Box,
  FormControl,
  InputLabel,
  Select,
  MenuItem,
  Typography,
  List,
  ListItemButton,
  ListItemText,
  CircularProgress,
  Button,
  Tooltip,
  Menu,
  Checkbox,
  IconButton,
  Dialog,
  DialogTitle,
  DialogContent,
  Snackbar,
  Alert,
  ListItemIcon,
  Divider,
} from '@mui/material';
import { ShowChart as ShowChartIcon, Timeline as TimelineIcon, KeyboardArrowDown, ChevronLeft as ChevronLeftIcon, FormatListBulleted as FormatListBulletedIcon, DeleteSweep as DeleteSweepIcon, DragIndicator as DragIndicatorIcon, Settings as SettingsIcon } from '@mui/icons-material';
import MASettingsDialog from '../components/MASettingsDialog';
import IchimokuSettingsDialog from '../components/IchimokuSettingsDialog';
import BollingerSettingsDialog from '../components/BollingerSettingsDialog';
import {
  DndContext,
  DragEndEvent,
  DragStartEvent,
  useSensor,
  useSensors,
  PointerSensor,
  closestCorners,
  useDroppable,
} from '@dnd-kit/core';
import { useDraggable } from '@dnd-kit/core';
import { useNavigation } from '../contexts/NavigationContext';
import { MultiChartMarketDataProvider } from '../contexts/MultiChartMarketDataContext';
import { ChartItem } from '../components/multiChart/ChartItem';
import { MultiChartWebSocketStatus } from '../components/multiChart/MultiChartWebSocketStatus';
import { getNotifications, getScheduleConfig, saveScheduleConfig, type AlertScheduleConfigDto } from '../services/alertApi';
import upbitApiService from '../services/upbitApi';
import { extractAssetNamesFromMessage, resolveSymbols, extractSymbolToStrategyNames } from '../utils/alertMessageParser';
import { extractIndicatorKeysFromStrategyRule, indicatorKeysToSettings } from '../utils/strategyIndicatorMapping';
import { getStrategyRules, getStrategyRule } from '../services/strategyRuleApi';
import { getAllAlertStrategies } from '../services/alertStrategyApi';
import { defaultMALines } from '../contexts/IndicatorVisibilityContext';
import type { MALineConfig } from '../contexts/IndicatorVisibilityContext';
import type { StockInfo, ChartSlot } from '../stores/useDashboardStore';
import {
  gridColsForPreset,
  gridRowsForPreset,
  MAX_MULTI_CHART_COUNT,
  MULTI_CHART_GRID_PRESETS,
  normalizeMultiChartGridPreset,
  packFlatForGridView,
  presetSlotCount,
  repackFlatForGridModeChange,
  type MultiChartGridPreset,
} from '../utils/multiChartLayoutUtils';

const LIST_PANEL_WIDTH = 240;

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

const ALERT_GRID_ADD_ID = 'alert-list-grid-add';
/** 2·3열: 셀 높이 하한(작은 뷰포트) */
const MIN_ALERT_CHART_HEIGHT = 280;
/** 2·3열: 세로 화면에서 한 번에 대략 보이는 그리프 행 수(스크롤로 나머지) */
const ALERT_MULTI_COL_VISIBLE_ROWS_TARGET = 2;
/** 그리드 gap:1 ≈ 8px — 행 간격 추정에 사용 */
const ALERT_GRID_ROW_GAP_PX = 8;
const ALERT_CHART_AREA_PAD_Y = 16;
const ALERT_ONE_COL_CELL_MIN_HEIGHT = 200;

function gridFlatFromStocks(stocks: StockInfo[], preset: MultiChartGridPreset): (StockInfo | null)[] {
  return packFlatForGridView(stocks, preset);
}

function stocksFromFlat(flat: (StockInfo | null)[]): StockInfo[] {
  return flat.filter((x): x is StockInfo => x != null);
}

const AlertGridAddStrip: React.FC<{ onClickPick: () => void }> = ({ onClickPick }) => {
  const { setNodeRef, isOver } = useDroppable({ id: ALERT_GRID_ADD_ID });
  return (
    <Box
      ref={setNodeRef}
      onClick={onClickPick}
      role="button"
      tabIndex={0}
      sx={{
        minHeight: 96,
        border: '2px dashed',
        borderColor: isOver ? 'primary.main' : 'divider',
        borderRadius: 1,
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        cursor: 'pointer',
        bgcolor: 'action.hover',
      }}
    >
      <Typography variant="body2" color="text.secondary">
        + 차트 추가 (클릭 또는 종목 드래그)
      </Typography>
    </Box>
  );
};

/** 드래그 가능한 알림 종목 리스트 아이템 */
const DraggableAlertListItem: React.FC<{
  stock: StockInfo;
  idx: number;
  isSelected: boolean;
  onSingleClick: () => void;
  onDoubleClick: () => void;
}> = ({ stock, idx, isSelected, onSingleClick, onDoubleClick }) => {
  const { attributes, listeners, setNodeRef, isDragging } = useDraggable({
    id: `alert-stock-${stock.symbol}`,
    data: { type: 'stock', stock },
  });
  return (
    <ListItemButton
      ref={setNodeRef}
      {...attributes}
      {...listeners}
      selected={isSelected}
      sx={{ py: 0.5, opacity: isDragging ? 0.5 : 1 }}
      onClick={(e) => { e.preventDefault(); onSingleClick(); }}
      onDoubleClick={(e) => { e.preventDefault(); onDoubleClick(); }}
    >
      <DragIndicatorIcon fontSize="small" sx={{ mr: 0.5, color: 'text.disabled', flexShrink: 0 }} />
      <ListItemText
        primary={stock.koreanName}
        secondary={stock.symbol}
        primaryTypographyProps={{ fontSize: '13px', fontWeight: idx === 0 ? 600 : 400 }}
        secondaryTypographyProps={{ fontSize: '11px' }}
      />
    </ListItemButton>
  );
};

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: '일봉' },
];

const VALID_CHART_TIMEFRAMES = new Set(TIMEFRAME_OPTIONS.map((o) => o.value));
/** 알림에 timeInterval이 없거나 API 값이 차트에서 미지원일 때 */
const DEFAULT_ALERT_CHART_TIMEFRAME = '3m';

const STRATEGY_CHECK_INTERVAL_SECONDS_OPTIONS = Array.from({ length: 28 }, (_, i) => i + 3);

function chartTimeframeFromAlert(raw?: string | null): string {
  const t = raw?.trim();
  if (t && VALID_CHART_TIMEFRAMES.has(t)) return t;
  return DEFAULT_ALERT_CHART_TIMEFRAME;
}

const defaultIchimoku = { tenkan: false, kijun: false, chikou: false, senkouA: false, senkouB: false };
const defaultBollinger = { upper: false, middle: false, lower: false, period: 20, stdDev: 2 };

export const AlertListPage: React.FC = () => {
  const { navigateToPage, navigationState } = useNavigation();
  const [alertStocks, setAlertStocks] = useState<StockInfo[]>([]);
  const [loading, setLoading] = useState(true);
  const [gridLayout, setGridLayout] = useState<MultiChartGridPreset>('1x2');
  /** 알림 팝업에서 넘어온 매수/매도 마커 (종목 심볼 → 시각·신호) */
  const [symbolNavAlertMarkers, setSymbolNavAlertMarkers] = useState<
    Record<string, { signal: 'BUY' | 'SELL'; timeIso: string }>
  >({});
  /** 종목별: 해당 종목이 처음 포함된(최신순 기준) 알림 건의 timeInterval → 차트/WS 고정 봉 */
  const [symbolToAlertTimeframe, setSymbolToAlertTimeframe] = useState<Record<string, string>>({});
  const [slotIndicatorSettings, setSlotIndicatorSettings] = useState<Record<string, Record<string, boolean>>>({});
  const [slotIndicatorOrder, setSlotIndicatorOrder] = useState<Record<string, string[]>>({});
  const [slotCandleHeightRatio, setSlotCandleHeightRatio] = useState<Record<string, number>>({});
  const [refreshTrigger, setRefreshTrigger] = useState(0);
  const [gridWidth, setGridWidth] = useState(800);
  const [chartAreaHeight, setChartAreaHeight] = useState(400);
  const chartAreaRef = React.useRef<HTMLDivElement | null>(null);
  const rightPanelRef = React.useRef<HTMLDivElement | null>(null);

  // 멀티차트와 동일: 이동평균선, 일목균형표, 볼린저 밴드 (localStorage 공유)
  const [maLines, setMaLines] = useState<MALineConfig[]>(() =>
    (() => {
      try {
        const saved = localStorage.getItem('multiChartMaLines');
        if (!saved) return defaultMALines;
        const parsed = JSON.parse(saved);
        return Array.isArray(parsed) ? parsed : defaultMALines;
      } catch {
        return defaultMALines;
      }
    })()
  );
  const [ichimoku, setIchimoku] = useState(() => {
    try {
      const saved = localStorage.getItem('multiChartIchimoku');
      if (!saved) return defaultIchimoku;
      const parsed = JSON.parse(saved) as Record<string, unknown>;
      return {
        tenkan: !!parsed?.tenkan,
        kijun: !!parsed?.kijun,
        chikou: !!parsed?.chikou,
        senkouA: !!parsed?.senkouA,
        senkouB: !!parsed?.senkouB,
      };
    } catch {
      return defaultIchimoku;
    }
  });
  const [bollinger, setBollinger] = useState(() => {
    try {
      const saved = localStorage.getItem('multiChartBollinger');
      if (!saved) return defaultBollinger;
      const parsed = JSON.parse(saved) as Record<string, unknown>;
      return {
        upper: !!parsed?.upper,
        middle: !!parsed?.middle,
        lower: !!parsed?.lower,
        period: typeof parsed?.period === 'number' ? parsed.period : 20,
        stdDev: typeof parsed?.stdDev === 'number' ? parsed.stdDev : 2,
      };
    } catch {
      return defaultBollinger;
    }
  });
  const [maMenuAnchor, setMaMenuAnchor] = useState<null | HTMLElement>(null);
  const [ichimokuMenuAnchor, setIchimokuMenuAnchor] = useState<null | HTMLElement>(null);
  const [bollingerMenuAnchor, setBollingerMenuAnchor] = useState<null | HTMLElement>(null);
  const [maSettingsOpen, setMaSettingsOpen] = useState(false);
  const [ichimokuSettingsOpen, setIchimokuSettingsOpen] = useState(false);
  const [bollingerSettingsOpen, setBollingerSettingsOpen] = useState(false);
  const [chartFullscreenSlotId, setChartFullscreenSlotId] = useState<string | null>(null);
  const [selectedSlotIndex, setSelectedSlotIndex] = useState<number | null>(null);
  const [listPanelOpen, setListPanelOpen] = useState(true);
  const [addStockDialogOpen, setAddStockDialogOpen] = useState(false);
  const [addStockTargetIndex, setAddStockTargetIndex] = useState<number | null>(null);
  const [snackbar, setSnackbar] = useState<{ open: boolean; message: string }>({ open: false, message: '' });
  const [alertScheduleConfig, setAlertScheduleConfig] = useState<AlertScheduleConfigDto | null>(null);

  const reloadAlertScheduleConfig = useCallback(async () => {
    try {
      const c = await getScheduleConfig();
      setAlertScheduleConfig(c);
    } catch (e) {
      console.error('알림 스케줄 설정 로드 실패:', e);
    }
  }, []);

  useEffect(() => {
    reloadAlertScheduleConfig();
  }, [reloadAlertScheduleConfig]);

  const handleAlertListStrategyIntervalChange = useCallback(
    async (sec: number) => {
      const base = alertScheduleConfig ?? (await getScheduleConfig().catch(() => null));
      if (!base) return;
      const merged = { ...base, strategyCheckIntervalSeconds: sec };
      setAlertScheduleConfig(merged);
      try {
        const saved = await saveScheduleConfig(merged);
        setAlertScheduleConfig(saved);
      } catch (e) {
        console.error(e);
        reloadAlertScheduleConfig();
      }
    },
    [alertScheduleConfig, reloadAlertScheduleConfig]
  );

  const handleAlertListTimeIntervalChange = useCallback(
    async (timeInterval: string) => {
      const base = alertScheduleConfig ?? (await getScheduleConfig().catch(() => null));
      if (!base) return;
      const merged = { ...base, timeInterval };
      setAlertScheduleConfig(merged);
      try {
        const saved = await saveScheduleConfig(merged);
        setAlertScheduleConfig(saved);
      } catch (e) {
        console.error(e);
        reloadAlertScheduleConfig();
      }
    },
    [alertScheduleConfig, reloadAlertScheduleConfig]
  );

  const strategyCheckSecondOptions = useMemo(() => Array.from({ length: 28 }, (_, i) => i + 3), []);

  useEffect(() => {
    try {
      localStorage.setItem('multiChartMaLines', JSON.stringify(maLines));
    } catch (_) {}
  }, [maLines]);
  useEffect(() => {
    try {
      localStorage.setItem('multiChartIchimoku', JSON.stringify(ichimoku));
    } catch (_) {}
  }, [ichimoku]);
  useEffect(() => {
    try {
      localStorage.setItem('multiChartBollinger', JSON.stringify(bollinger));
    } catch (_) {}
  }, [bollinger]);


  const [latestAlertSymbols, setLatestAlertSymbols] = useState<Set<string>>(new Set());
  const [symbolToStrategyNames, setSymbolToStrategyNames] = useState<Record<string, string[]>>({});

  const focusStockInCharts = useCallback((symbol: string) => {
    if (!symbol) return;
    setAlertStocks((prev) => {
      const idx = prev.findIndex((s) => s.symbol === symbol);
      if (idx <= 0) return prev;
      const next = [...prev];
      const [target] = next.splice(idx, 1);
      next.unshift(target);
      return next;
    });
    // 우측 차트는 정렬 순서 기준으로 렌더링되므로, 포커스 종목을 0번 슬롯으로 맞춘다.
    setSelectedSlotIndex(0);
  }, []);

  // 알림 목록 로드: 통합 알림 메시지에서 모든 종목 추출 후 최신순, symbol 중복 제거
  // 마지막(가장 최근) 알림에서 조건 일치한 모든 종목 symbol → latestAlertSymbols (타이틀바 파란 그라데이션용)
  useEffect(() => {
    let cancelled = false;
    setLoading(true);
    Promise.all([getNotifications(undefined, 0, 80), upbitApiService.getActiveCryptos()])
      .then(([notifRes, cryptos]) => {
        if (cancelled) return;
        const koreanToSymbol = new Map<string, string>();
        cryptos.forEach((c) => koreanToSymbol.set(c.koreanName, c.symbol));

        type Item = { symbol: string; koreanName: string; triggeredAt: number };
        const items: Item[] = [];
        const symbolFirstAlertTimeframe = new Map<string, string>();
        const sorted = [...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 sorted) {
          const triggeredAt = n.triggeredAt ? new Date(n.triggeredAt).getTime() : 0;
          const tf = chartTimeframeFromAlert(n.timeInterval);
          const assetNames = extractAssetNamesFromMessage(n.message || '');
          if (assetNames && assetNames.length > 0) {
            const resolved = resolveSymbols(assetNames, koreanToSymbol);
            resolved.forEach((r) => {
              if (!symbolFirstAlertTimeframe.has(r.symbol)) {
                symbolFirstAlertTimeframe.set(r.symbol, tf);
              }
              items.push({ ...r, triggeredAt });
            });
          } else if (n.symbol && n.koreanName) {
            if (!symbolFirstAlertTimeframe.has(n.symbol)) {
              symbolFirstAlertTimeframe.set(n.symbol, tf);
            }
            items.push({
              symbol: n.symbol,
              koreanName: n.koreanName,
              triggeredAt,
            });
          }
        }

        const seen = new Set<string>();
        const deduped = items.filter((it) => {
          if (seen.has(it.symbol)) return false;
          seen.add(it.symbol);
          return true;
        });
        setAlertStocks(
          deduped.slice(0, MAX_MULTI_CHART_COUNT).map((it) => ({ symbol: it.symbol, koreanName: it.koreanName }))
        );
        setSymbolToAlertTimeframe(Object.fromEntries(symbolFirstAlertTimeframe));

        // 종목별 매칭 전략명 추출 (보조지표 자동 추가용)
        setSymbolToStrategyNames(extractSymbolToStrategyNames(notifRes.content, koreanToSymbol));

        // 마지막 알림 배치(가장 최근 triggeredAt)의 모든 종목 symbol 수집
        // 개별 알림(종목별 1건) + 통합 알림(1건에 여러 종목) 모두 대응
        const latest = new Set<string>();
        const maxTriggeredAt = sorted.length > 0 && sorted[0].triggeredAt
          ? new Date(sorted[0].triggeredAt).getTime()
          : 0;
        const BATCH_WINDOW_MS = 10000; // 동일 배치로 간주하는 시간(ms)
        for (const n of sorted) {
          const t = n.triggeredAt ? new Date(n.triggeredAt).getTime() : 0;
          if (maxTriggeredAt - t > BATCH_WINDOW_MS) break;
          const assetNames = extractAssetNamesFromMessage(n.message || '');
          if (assetNames && assetNames.length > 0) {
            const resolved = resolveSymbols(assetNames, koreanToSymbol);
            resolved.forEach((r) => latest.add(r.symbol));
          } else if (n.symbol) {
            latest.add(n.symbol);
          }
        }
        setLatestAlertSymbols(latest);
      })
      .catch((e) => {
        if (!cancelled) console.error('[AlertList] 알림 로드 실패', e);
      })
      .finally(() => {
        if (!cancelled) setLoading(false);
      });
    return () => { cancelled = true; };
  }, []);

  // 알림 팝업에서 종목 클릭으로 진입 시 해당 종목 선택 + 차트 매수/매도 마커
  useEffect(() => {
    const symbol = navigationState?.symbol;
    const sig = navigationState?.alertSignal;
    const t = navigationState?.alertMarkerTime;
    if (symbol && sig && t) {
      setSymbolNavAlertMarkers((prev) => ({ ...prev, [symbol]: { signal: sig, timeIso: t } }));
    }
    if (!symbol || alertStocks.length === 0) return;
    focusStockInCharts(symbol);
  }, [
    navigationState?.symbol,
    navigationState?.alertSignal,
    navigationState?.alertMarkerTime,
    alertStocks.length,
    focusStockInCharts,
  ]);

  // 일치하는 전략에 보조지표 조건이 있으면 해당 슬롯에 보조지표 자동 추가
  useEffect(() => {
    if (Object.keys(symbolToStrategyNames).length === 0) return;

    const run = async () => {
      const nameToId = new Map<string, number>();

      try {
        const alertStrategies = await getAllAlertStrategies();
        alertStrategies.forEach((a) => {
          if (a.strategyRuleName) nameToId.set(a.strategyRuleName.trim(), a.strategyRuleId);
        });
        if (nameToId.size === 0) {
          const allRules = await getStrategyRules();
          allRules.forEach((r) => {
            if (r.name) nameToId.set(r.name.trim(), r.id!);
          });
        }
      } catch (e) {
        console.warn('[AlertList] 전략 목록 로드 실패:', e);
        return;
      }

      const nextSettings: Record<string, Record<string, boolean>> = {};
      const slotsToProcess = alertStocks.length;

      for (let i = 0; i < slotsToProcess; i++) {
        const stock = alertStocks[i];
        if (!stock?.symbol) continue;

        const strategyNames = symbolToStrategyNames[stock.symbol];
        if (!strategyNames || strategyNames.length === 0) continue;

        const allIndicatorKeys = new Set<string>();
        const ruleIdsToFetch = new Set<number>();

        for (const name of strategyNames) {
          const id = nameToId.get(name.trim());
          if (id != null) ruleIdsToFetch.add(id);
        }

        for (const ruleId of Array.from(ruleIdsToFetch)) {
          try {
            const rule = await getStrategyRule(ruleId);
            const keys = extractIndicatorKeysFromStrategyRule(rule);
            keys.forEach((k) => allIndicatorKeys.add(k));
          } catch {
            // 개별 규칙 로드 실패 시 무시
          }
        }

        if (allIndicatorKeys.size > 0) {
          const sym = stock.symbol;
          const slotId = `al-sym-${sym}`;
          nextSettings[slotId] = indicatorKeysToSettings(Array.from(allIndicatorKeys));
        }
      }

      if (Object.keys(nextSettings).length > 0) {
        setSlotIndicatorSettings((prev) => ({ ...prev, ...nextSettings }));
      }
    };

    run();
  }, [alertStocks, symbolToStrategyNames, gridLayout]);

  const displaySlots: ChartSlot[] = useMemo(() => {
    const flat = gridFlatFromStocks(alertStocks, gridLayout);
    return flat.map((stock, i) => ({
      id: stock ? `al-sym-${stock.symbol}` : `al-e-${i}`,
      gridIndex: i,
      stock,
    }));
  }, [alertStocks, gridLayout]);

  const alertChartCellHeight = useMemo(() => {
    const padY = chartFullscreenSlotId ? 0 : ALERT_CHART_AREA_PAD_Y;
    const inner = Math.max(0, chartAreaHeight - padY);
    if (gridRowsForPreset(gridLayout) === 1) {
      return Math.max(ALERT_ONE_COL_CELL_MIN_HEIGHT, inner);
    }
    // 2·3열: 차트 영역을 세로 기준 약 2행만 보이도록 셀 높이 확대 (기존 280px 고정 → 3개가 한 화면에 들어가던 현상 완화)
    const rowGaps = ALERT_GRID_ROW_GAP_PX * (ALERT_MULTI_COL_VISIBLE_ROWS_TARGET - 1);
    const perCell = Math.floor((inner - rowGaps) / ALERT_MULTI_COL_VISIBLE_ROWS_TARGET);
    return Math.max(MIN_ALERT_CHART_HEIGHT, perCell);
  }, [gridLayout, chartAreaHeight, chartFullscreenSlotId]);

  const marketSubscriptions = useMemo(() => {
    const subs = displaySlots
      .filter((s) => s.stock && s.stock.symbol)
      .map((s) => ({
        market: s.stock!.symbol,
        timeframe: symbolToAlertTimeframe[s.stock!.symbol] ?? DEFAULT_ALERT_CHART_TIMEFRAME,
        koreanName: s.stock!.koreanName,
      }));
    console.log('📊 [AlertList] marketSubscriptions 생성:', { subs, displaySlotsCount: displaySlots.length, slotsWithStock: displaySlots.filter(s => s.stock).length });
    return subs;
  }, [displaySlots, symbolToAlertTimeframe]);

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

  const handleIndicatorSettingsChange = useCallback((slotId: string, s: Record<string, boolean>) => {
    setSlotIndicatorSettings((prev) => ({ ...prev, [slotId]: s }));
  }, []);

  const handleIndicatorOrderChange = useCallback((slotId: string, order: string[]) => {
    setSlotIndicatorOrder((prev) => ({ ...prev, [slotId]: order }));
  }, []);

  const handleAddStockToSlot = useCallback(
    (gridIndex: number, stock: StockInfo) => {
      let rejected = false;
      setAlertStocks((prev) => {
        const without = prev.filter((s) => s.symbol !== stock.symbol);
        let flat = gridFlatFromStocks(without, gridLayout);
        while (flat.length <= gridIndex) flat.push(null);
        flat[gridIndex] = stock;
        const next = stocksFromFlat(flat);
        if (next.length > MAX_MULTI_CHART_COUNT) {
          rejected = true;
          return prev;
        }
        return next;
      });
      if (rejected) {
        setSnackbar({ open: true, message: `차트는 최대 ${MAX_MULTI_CHART_COUNT}개까지 추가할 수 있습니다.` });
        return;
      }
      setSelectedSlotIndex(gridIndex);
      setAddStockDialogOpen(false);
      setAddStockTargetIndex(null);
    },
    [gridLayout]
  );

  const appendStockOneColumn = useCallback((stock: StockInfo) => {
    let rejected = false;
    setAlertStocks((prev) => {
      const cap = Math.min(presetSlotCount(gridLayout), MAX_MULTI_CHART_COUNT);
      if (prev.length >= MAX_MULTI_CHART_COUNT) {
        rejected = true;
        return prev;
      }
      const without = prev.filter((s) => s.symbol !== stock.symbol);
      if (without.length >= cap) {
        rejected = true;
        return prev;
      }
      return [...without, stock];
    });
    if (rejected) {
      setSnackbar({ open: true, message: `차트는 최대 ${MAX_MULTI_CHART_COUNT}개까지 추가할 수 있습니다.` });
    }
  }, [gridLayout]);

  const openGridAdd = useCallback(() => {
    if (alertStocks.length >= MAX_MULTI_CHART_COUNT) {
      setSnackbar({ open: true, message: `차트는 최대 ${MAX_MULTI_CHART_COUNT}개까지 추가할 수 있습니다.` });
      return;
    }
    const flat = gridFlatFromStocks(alertStocks, gridLayout);
    const idx = flat.findIndex((x) => x == null);
    if (idx < 0) {
      setSnackbar({ open: true, message: '빈 차트 칸이 없습니다. 종목을 제거한 뒤 추가하세요.' });
      return;
    }
    setAddStockTargetIndex(idx);
    setAddStockDialogOpen(true);
  }, [alertStocks, gridLayout]);

  const handleAddClick = useCallback((gridIndex: number) => {
    setAddStockTargetIndex(gridIndex);
    setAddStockDialogOpen(true);
  }, []);

  const handleClearAll = useCallback(() => {
    setAlertStocks([]);
    setSelectedSlotIndex(null);
    setChartFullscreenSlotId(null);
    setSlotIndicatorSettings({});
    setSlotIndicatorOrder({});
    setSlotCandleHeightRatio({});
    setSymbolNavAlertMarkers({});
  }, []);

  const handleCandleHeightRatioChange = useCallback((slotId: string, ratio: number) => {
    setSlotCandleHeightRatio((prev) => ({
      ...prev,
      /** 알림목록: 캔들 영역 최소 60%, 최대 90% */
      [slotId]: Math.max(0.6, Math.min(0.9, ratio)),
    }));
  }, []);

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

  const handleRemoveChart = useCallback(
    (gridIndex: number) => {
      const flatBefore = gridFlatFromStocks(alertStocks, gridLayout);
      const deadSym = flatBefore[gridIndex]?.symbol;
      const deadId = deadSym ? `al-sym-${deadSym}` : null;
      setAlertStocks((prev) => {
        const f = gridFlatFromStocks(prev, gridLayout);
        if (gridIndex >= 0 && gridIndex < f.length) f[gridIndex] = null;
        return stocksFromFlat(f);
      });
      if (deadId) setChartFullscreenSlotId((p) => (p === deadId ? null : p));
      setSelectedSlotIndex((prev) => {
        if (prev === null || deadSym == null) return prev;
        if (alertStocks[prev]?.symbol === deadSym) return null;
        return prev;
      });
      if (deadSym) {
        setSymbolNavAlertMarkers((prev) => {
          if (!prev[deadSym]) return prev;
          const next = { ...prev };
          delete next[deadSym];
          return next;
        });
      }
    },
    [alertStocks, gridLayout]
  );

  const handleListItemClick = useCallback(
    (idx: number) => {
      if (chartFullscreenSlotId) setChartFullscreenSlotId(null);
      const symbol = alertStocks[idx]?.symbol;
      if (!symbol) return;
      focusStockInCharts(symbol);
    },
    [chartFullscreenSlotId, alertStocks, focusStockInCharts]
  );

  const handleListItemDoubleClick = useCallback((idx: number) => {
    const sym = alertStocks[idx]?.symbol;
    if (!sym) return;
    focusStockInCharts(sym);
    setChartFullscreenSlotId(`al-sym-${sym}`);
  }, [alertStocks, focusStockInCharts]);

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

  useEffect(() => {
    if (chartFullscreenSlotId && !displaySlots.some((s) => s.id === chartFullscreenSlotId && s.stock)) {
      setChartFullscreenSlotId(null);
    }
  }, [chartFullscreenSlotId, displaySlots]);

  useEffect(() => {
    const el = rightPanelRef.current;
    if (!el) return;
    const updateSize = () => {
      // 상단 헤더와 동일한 수평 폭: px:1(8px)*2 = 16px 제외
      setGridWidth(Math.max(400, el.clientWidth - 16));
    };
    const ro = new ResizeObserver(updateSize);
    ro.observe(el);
    updateSize();
    return () => ro.disconnect();
  }, []);

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

  const handleGridLayoutChange = useCallback((next: MultiChartGridPreset) => {
    setGridLayout((prev) => {
      if (next === prev) return prev;
      setAlertStocks((stocks) => {
        const flat = gridFlatFromStocks(stocks, prev);
        const newFlat = repackFlatForGridModeChange(flat, prev, next);
        return stocksFromFlat(newFlat);
      });
      return next;
    });
  }, []);

  const stocksAvailableToAdd = alertStocks;

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

  const handleDragEnd = useCallback(
    (e: DragEndEvent) => {
      const { active, over } = e;
      const activeData = active.data.current;

      if (activeData?.type === 'chart-reorder') {
        if (!over) return;
        const overId = String(over.id);
        if (overId === ALERT_GRID_ADD_ID) return;
        const fromSlotId = activeData?.slotId as string | undefined;
        if (!fromSlotId || fromSlotId === overId) return;
        const fromSlot = displaySlots.find((s) => s.id === fromSlotId);
        const toSlot = displaySlots.find((s) => s.id === overId);
        if (!fromSlot || !toSlot || !fromSlot.stock || !toSlot.stock) return;
        const fromIdx = alertStocks.findIndex((s) => s.symbol === fromSlot.stock!.symbol);
        const toIdx = alertStocks.findIndex((s) => s.symbol === toSlot.stock!.symbol);
        if (fromIdx >= 0 && toIdx >= 0 && fromIdx !== toIdx) {
          setAlertStocks((prev) => {
            const next = [...prev];
            const tmp = next[fromIdx];
            next[fromIdx] = next[toIdx];
            next[toIdx] = tmp;
            return next;
          });
        }
        return;
      }

      const activeId = String(active.id);
      if (!activeId.startsWith('alert-stock-') || !over) return;
      const overId = String(over.id);
      const stock = active.data.current?.stock as StockInfo | undefined;
      if (!stock) return;
      if (overId === ALERT_GRID_ADD_ID) {
        appendStockOneColumn(stock);
        return;
      }
      const hit = displaySlots.find((s) => s.id === overId);
      if (hit) handleAddStockToSlot(hit.gridIndex, stock);
    },
    [appendStockOneColumn, displaySlots, handleAddStockToSlot, alertStocks]
  );

  return (
    <DndContext
      sensors={sensors}
      collisionDetection={closestCorners}
      onDragEnd={handleDragEnd}
    >
    <Box
      sx={{
        display: 'flex',
        height: 'calc(100vh - 64px)',
        bgcolor: 'background.default',
        overflow: 'hidden',
      }}
    >
      {/* 좌측: 알림 종목 목록 (접기/펼치기) */}
      <Box
        sx={{
          width: listPanelOpen ? LIST_PANEL_WIDTH : 40,
          flexShrink: 0,
          borderRight: '1px solid',
          borderColor: 'divider',
          bgcolor: 'background.paper',
          overflow: 'hidden',
          display: 'flex',
          flexDirection: 'column',
          transition: 'width 0.25s ease',
        }}
      >
        {listPanelOpen ? (
          <>
            <Box
              sx={{
                display: 'flex',
                alignItems: 'center',
                justifyContent: 'space-between',
                p: 1.5,
                borderBottom: '1px solid',
                borderColor: 'divider',
              }}
            >
              <Box>
                <Typography variant="subtitle1" fontWeight="bold">
                  알림 목록
                </Typography>
                <Typography variant="caption" color="text.secondary" display="block">
                  조건 일치 종목 (최신순)
                </Typography>
              </Box>
              <Tooltip title="목록 패널 닫기">
                <IconButton size="small" onClick={() => setListPanelOpen(false)} aria-label="목록 패널 닫기">
                  <ChevronLeftIcon fontSize="small" />
                </IconButton>
              </Tooltip>
            </Box>
            <Box sx={{ flex: 1, overflow: 'auto' }}>
              {loading ? (
                <Box sx={{ display: 'flex', justifyContent: 'center', py: 4 }}>
                  <CircularProgress size={24} />
                </Box>
              ) : alertStocks.length === 0 ? (
                <Typography variant="body2" color="text.secondary" sx={{ p: 2 }}>
                  알림이 없습니다.
                </Typography>
              ) : (
                <List dense disablePadding>
                  {alertStocks.map((stock, idx) => (
                    <DraggableAlertListItem
                      key={stock.symbol}
                      stock={stock}
                      idx={idx}
                      isSelected={idx === selectedSlotIndex}
                      onSingleClick={() => handleListItemClick(idx)}
                      onDoubleClick={() => handleListItemDoubleClick(idx)}
                    />
                  ))}
                </List>
              )}
            </Box>
          </>
        ) : (
          <Box
            sx={{
              width: 40,
              flex: 1,
              display: 'flex',
              flexDirection: 'column',
              alignItems: 'center',
              pt: 2,
            }}
          >
            <Tooltip title="목록 패널 펼치기" placement="right">
              <IconButton onClick={() => setListPanelOpen(true)} aria-label="목록 패널 펼치기" sx={{ color: 'text.secondary' }}>
                <FormatListBulletedIcon />
              </IconButton>
            </Tooltip>
          </Box>
        )}
      </Box>

      {/* 우측: 차트 그리드 (WebSocket 실시간 연동) */}
      <Box ref={rightPanelRef} sx={{ flex: 1, display: 'flex', flexDirection: 'column', minWidth: 0 }}>
        <MultiChartMarketDataProvider subscriptions={marketSubscriptions}>
        {/* 차트 영역 상단: 레이아웃 */}
        <Box
          sx={{
            display: 'flex',
            alignItems: 'center',
            gap: 2,
            py: 1,
            px: 1,
            flexShrink: 0,
            borderBottom: '1px solid',
            borderColor: 'divider',
            bgcolor: 'background.paper',
          }}
        >
          <FormControl size="small" sx={{ minWidth: 110, '& .MuiInputBase-root': { fontSize: '12px' }, '& .MuiSelect-select': { py: 0.375, fontSize: '12px' } }}>
            <Select
              value={gridLayout}
              displayEmpty
              onChange={(e) => handleGridLayoutChange(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: 100, '& .MuiInputBase-root': { fontSize: '12px' }, '& .MuiSelect-select': { py: 0.375, fontSize: '12px' } }}>
            <InputLabel id="alertlist-tf">평가봉</InputLabel>
            <Select
              labelId="alertlist-tf"
              label="평가봉"
              value={alertScheduleConfig?.timeInterval ?? '1h'}
              onChange={(e) => handleAlertListTimeIntervalChange(String(e.target.value))}
            >
              <MenuItem value="1m">1m</MenuItem>
              <MenuItem value="3m">3m</MenuItem>
              <MenuItem value="5m">5m</MenuItem>
              <MenuItem value="10m">10m</MenuItem>
              <MenuItem value="15m">15m</MenuItem>
              <MenuItem value="30m">30m</MenuItem>
              <MenuItem value="1h">1h</MenuItem>
              <MenuItem value="4h">4h</MenuItem>
            </Select>
          </FormControl>
          <FormControl size="small" sx={{ minWidth: 88, '& .MuiInputBase-root': { fontSize: '12px' }, '& .MuiSelect-select': { py: 0.375, fontSize: '12px' } }}>
            <InputLabel id="alertlist-sec">체크</InputLabel>
            <Select
              labelId="alertlist-sec"
              label="체크"
              value={alertScheduleConfig?.strategyCheckIntervalSeconds ?? 5}
              onChange={(e) => handleAlertListStrategyIntervalChange(Number(e.target.value))}
            >
              {strategyCheckSecondOptions.map((sec) => (
                <MenuItem key={sec} value={sec}>
                  {sec}초
                </MenuItem>
              ))}
            </Select>
          </FormControl>
          <Box sx={{ flex: 1 }} />
          {/* 전체목록 제거 / 이동평균선 / 일목균형표 / 볼린저밴드 */}
          <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
            <Tooltip title="전체 목록 제거 (모든 종목 및 차트 삭제)" arrow>
              <IconButton size="small" onClick={handleClearAll} aria-label="전체 목록 제거" color={alertStocks.length > 0 ? 'error' : 'default'} disabled={alertStocks.length === 0}>
                <DeleteSweepIcon fontSize="small" />
              </IconButton>
            </Tooltip>
            <Tooltip title={`이동평균선 (${maLines.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({maLines.filter((m) => m.enabled).length})
              </Button>
            </Tooltip>
            <Tooltip title={`일목균형표 (${[ichimoku.tenkan, ichimoku.kijun, ichimoku.chikou, ichimoku.senkouA, ichimoku.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 }}
              >
                일목({[ichimoku.tenkan, ichimoku.kijun, ichimoku.chikou, ichimoku.senkouA, ichimoku.senkouB].filter(Boolean).length})
              </Button>
            </Tooltip>
            <Tooltip title={`볼린저 밴드 (${[bollinger.upper, bollinger.middle, bollinger.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 }}
              >
                볼린저({[bollinger.upper, bollinger.middle, bollinger.lower].filter(Boolean).length})
              </Button>
            </Tooltip>
          </Box>
          <MultiChartWebSocketStatus />
        </Box>

        {/* 차트 그리드 */}
        <Box ref={chartAreaRef} sx={{ flex: 1, minHeight: 0, p: chartFullscreenSlotId ? 0 : 1, overflow: chartFullscreenSlotId ? 'hidden' : 'auto' }}>
          {chartFullscreenSlotId && displaySlots.some((s) => s.id === chartFullscreenSlotId && s.stock) ? (
            <Box sx={{ height: '100%', minHeight: 0, display: 'flex', flexDirection: 'column' }}>
              {displaySlots
                .filter((s) => s.id === chartFullscreenSlotId)
                .map((slot) => (
                  <Box key={slot.id} sx={{ flex: 1, minHeight: 0, overflow: 'hidden', display: 'flex', flexDirection: 'column' }}>
                    <ChartItem
                      id={slot.id}
                      stock={slot.stock}
                      gridIndex={slot.gridIndex}
                      timeframe={
                        slot.stock
                          ? symbolToAlertTimeframe[slot.stock.symbol] ?? DEFAULT_ALERT_CHART_TIMEFRAME
                          : DEFAULT_ALERT_CHART_TIMEFRAME
                      }
                      refreshTrigger={refreshTrigger}
                      onAddClick={() => handleAddClick(slot.gridIndex)}
                      onRemove={() => handleRemoveChart(slot.gridIndex)}
                      onDetailClick={handleDetailClick}
                      onChartFullscreen={() => handleChartFullscreenToggle(slot.id)}
                      isChartFullscreen
                      fullscreenHeight={Math.min(chartAreaHeight - 56, Math.max(280, Math.round(gridWidth * 0.52)))}
                      lockTimeframe
                      indicatorSettings={slotIndicatorSettings[slot.id]}
                      onIndicatorSettingsChange={(s) => handleIndicatorSettingsChange(slot.id, s)}
                      indicatorOrder={slotIndicatorOrder[slot.id]}
                      onIndicatorOrderChange={(o) => handleIndicatorOrderChange(slot.id, o)}
                      candleHeightRatio={slotCandleHeightRatio[slot.id] ?? 0.6}
                      onCandleHeightRatioChange={(r) => handleCandleHeightRatioChange(slot.id, r)}
                      preferCandleHeightRatio
                      minCandleHeightRatio={0.6}
                      maLines={maLines}
                      ichimokuSettings={
                        ichimoku.tenkan || ichimoku.kijun || ichimoku.chikou || ichimoku.senkouA || ichimoku.senkouB
                          ? {
                              tenkan: ichimoku.tenkan,
                              kijun: ichimoku.kijun,
                              chikou: ichimoku.chikou,
                              senkouA: ichimoku.senkouA,
                              senkouB: ichimoku.senkouB,
                              tenkanPeriod: 9,
                              kijunPeriod: 26,
                              senkouBPeriod: 52,
                            }
                          : undefined
                      }
                      overlaySettingsOverride={
                        bollinger.upper || bollinger.middle || bollinger.lower
                          ? {
                              bollingerUpper: bollinger.upper,
                              bollingerMiddle: bollinger.middle,
                              bollingerLower: bollinger.lower,
                              bollingerPeriod: bollinger.period,
                              bollingerStdDev: bollinger.stdDev,
                            }
                          : undefined
                      }
                      isLatestAlert={!!slot.stock && latestAlertSymbols.has(slot.stock.symbol)}
                      isSelected={
                        slot.stock != null &&
                        selectedSlotIndex != null &&
                        alertStocks[selectedSlotIndex]?.symbol === slot.stock.symbol
                      }
                      enableTitleBarReorder={false}
                      navAlertMarker={
                        slot.stock ? symbolNavAlertMarkers[slot.stock.symbol] ?? null : null
                      }
                    />
                  </Box>
                ))}
            </Box>
          ) : (
            <Box
              sx={{
                display: 'grid',
                gridTemplateColumns: `repeat(${gridColsForPreset(gridLayout)}, minmax(0, 1fr))`,
                gap: 1,
                alignContent: 'start',
                minWidth: 0,
                pb: 1,
              }}
            >
              {displaySlots.map((slot) => (
                <Box
                  key={slot.id}
                  sx={{
                    minHeight: alertChartCellHeight,
                    height: alertChartCellHeight,
                    minWidth: 0,
                    overflow: 'hidden',
                    display: 'flex',
                    flexDirection: 'column',
                  }}
                >
                  <ChartItem
                    id={slot.id}
                    stock={slot.stock}
                    gridIndex={slot.gridIndex}
                    timeframe={
                      slot.stock
                        ? symbolToAlertTimeframe[slot.stock.symbol] ?? DEFAULT_ALERT_CHART_TIMEFRAME
                        : DEFAULT_ALERT_CHART_TIMEFRAME
                    }
                    refreshTrigger={refreshTrigger}
                    onAddClick={() => handleAddClick(slot.gridIndex)}
                    onRemove={() => handleRemoveChart(slot.gridIndex)}
                    onDetailClick={handleDetailClick}
                    onChartFullscreen={() => handleChartFullscreenToggle(slot.id)}
                    isChartFullscreen={false}
                    lockTimeframe
                    indicatorSettings={slotIndicatorSettings[slot.id]}
                    onIndicatorSettingsChange={(s) => handleIndicatorSettingsChange(slot.id, s)}
                    indicatorOrder={slotIndicatorOrder[slot.id]}
                    onIndicatorOrderChange={(o) => handleIndicatorOrderChange(slot.id, o)}
                    candleHeightRatio={slotCandleHeightRatio[slot.id] ?? 0.6}
                    onCandleHeightRatioChange={(r) => handleCandleHeightRatioChange(slot.id, r)}
                    preferCandleHeightRatio
                    minCandleHeightRatio={0.6}
                    maLines={maLines}
                    ichimokuSettings={
                      ichimoku.tenkan || ichimoku.kijun || ichimoku.chikou || ichimoku.senkouA || ichimoku.senkouB
                        ? {
                            tenkan: ichimoku.tenkan,
                            kijun: ichimoku.kijun,
                            chikou: ichimoku.chikou,
                            senkouA: ichimoku.senkouA,
                            senkouB: ichimoku.senkouB,
                            tenkanPeriod: 9,
                            kijunPeriod: 26,
                            senkouBPeriod: 52,
                          }
                        : undefined
                    }
                    overlaySettingsOverride={
                      bollinger.upper || bollinger.middle || bollinger.lower
                        ? {
                            bollingerUpper: bollinger.upper,
                            bollingerMiddle: bollinger.middle,
                            bollingerLower: bollinger.lower,
                            bollingerPeriod: bollinger.period,
                            bollingerStdDev: bollinger.stdDev,
                          }
                        : undefined
                    }
                    isLatestAlert={!!slot.stock && latestAlertSymbols.has(slot.stock.symbol)}
                    isSelected={
                      slot.stock != null &&
                      selectedSlotIndex != null &&
                      alertStocks[selectedSlotIndex]?.symbol === slot.stock.symbol
                    }
                    enableTitleBarReorder
                    navAlertMarker={
                      slot.stock ? symbolNavAlertMarkers[slot.stock.symbol] ?? null : null
                    }
                  />
                </Box>
              ))}
              {displaySlots.some((s) => s.stock == null) && alertStocks.length < MAX_MULTI_CHART_COUNT && (
                <Box sx={{ gridColumn: '1 / -1' }}>
                  <AlertGridAddStrip onClickPick={openGridAdd} />
                </Box>
              )}
            </Box>
          )}
        </Box>
        </MultiChartMarketDataProvider>
      </Box>

      {/* MA 메뉴 */}
      <Menu anchorEl={maMenuAnchor} open={Boolean(maMenuAnchor)} onClose={() => setMaMenuAnchor(null)} PaperProps={{ sx: { minWidth: 180 } }}>
        {maLines.map((line, idx) => (
          <MenuItem
            key={line.id}
            onClick={(e) => {
              e.stopPropagation();
              setMaLines((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();
              setIchimoku((prev) => ({ ...prev, [key]: !prev[key] }));
            }}
            sx={{ py: 0.5 }}
          >
            <Checkbox checked={ichimoku[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(); setBollinger((p) => ({ ...p, upper: !p.upper })); }} sx={{ py: 0.5 }}>
          <Checkbox checked={bollinger.upper} size="small" sx={{ p: 0.5 }} />
          <ListItemText primary="상한선 (Upper)" primaryTypographyProps={{ fontSize: '13px' }} />
        </MenuItem>
        <MenuItem onClick={(e) => { e.stopPropagation(); setBollinger((p) => ({ ...p, middle: !p.middle })); }} sx={{ py: 0.5 }}>
          <Checkbox checked={bollinger.middle} size="small" sx={{ p: 0.5 }} />
          <ListItemText primary="중간선 (Middle)" primaryTypographyProps={{ fontSize: '13px' }} />
        </MenuItem>
        <MenuItem onClick={(e) => { e.stopPropagation(); setBollinger((p) => ({ ...p, lower: !p.lower })); }} sx={{ py: 0.5 }}>
          <Checkbox checked={bollinger.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>

      {/* 종목 추가 다이얼로그 (+ 버튼 클릭 시) */}
      <Dialog open={addStockDialogOpen} onClose={() => { setAddStockDialogOpen(false); setAddStockTargetIndex(null); }} maxWidth="xs" fullWidth>
        <DialogTitle>차트에 종목 추가</DialogTitle>
        <DialogContent>
          {addStockTargetIndex == null ? null : stocksAvailableToAdd.length === 0 ? (
            <Typography color="text.secondary">알림 목록에 종목이 없습니다.</Typography>
          ) : (
            <List dense disablePadding>
              {stocksAvailableToAdd.map((stock) => (
                <ListItemButton
                  key={stock.symbol}
                  onClick={() => handleAddStockToSlot(addStockTargetIndex, stock)}
                  sx={{ borderRadius: 1 }}
                >
                  <ListItemText primary={stock.koreanName} secondary={stock.symbol} />
                </ListItemButton>
              ))}
            </List>
          )}
        </DialogContent>
      </Dialog>

      <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="info" sx={{ width: '100%' }}>
          {snackbar.message}
        </Alert>
      </Snackbar>

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

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

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

    </Box>
    </DndContext>
  );
};

export default AlertListPage;
