import React, { useState, useEffect, useCallback, useMemo } from 'react';
import {
  Box,
  Paper,
  Typography,
  Button,
  Tabs,
  Tab,
  Table,
  TableBody,
  TableCell,
  TableContainer,
  TableHead,
  TableRow,
  IconButton,
  Chip,
  Dialog,
  DialogTitle,
  DialogContent,
  DialogActions,
  TextField,
  FormControl,
  InputLabel,
  Select,
  MenuItem,
  Switch,
  FormControlLabel,
  Alert,
  CircularProgress,
  Tooltip,
  Card,
  CardContent,
  Grid,
  Divider,
  Collapse,
  List,
  ListItem,
  ListItemText,
  ListItemSecondaryAction,
  ToggleButton,
  ToggleButtonGroup,
  Slider,
  useTheme,
} from '@mui/material';
import {
  Add as AddIcon,
  Edit as EditIcon,
  Delete as DeleteIcon,
  Search as SearchIcon,
  PlayArrow as PlayIcon,
  ExpandMore as ExpandMoreIcon,
  ExpandLess as ExpandLessIcon,
  ContentCopy as CopyIcon,
  TrendingUp,
  TrendingDown,
  CheckCircle,
  Cancel,
  VerifiedUser as VerifyIcon,
  Error as ErrorIcon,
  Warning as WarningIcon,
  Close,
  FileDownload as FileDownloadIcon,
  FileUpload as FileUploadIcon,
  DeleteSweep as DeleteSweepIcon,
  NotificationsActive,
  NotificationsOff,
} from '@mui/icons-material';
import {
  StrategyRuleDto,
  ConditionGroupDto,
  StrategyConditionDto,
  SymbolMatchResultDto,
  StrategyRuleSearchRequest,
  StrategyValidationResultDto,
  ValidationError,
  ValidationWarning,
  SignalType,
  LogicalOperator,
  IndicatorType,
  ConditionType,
  MaConditionDto,
  getStrategyRules,
  getStrategyRule,
  createStrategyRule,
  updateStrategyRule,
  deleteStrategyRule,
  toggleStrategyRule,
  searchSymbolsByRule,
  validateStrategyRule,
  getIndicatorDisplayName,
  getConditionDisplayName,
  generateConditionDescription,
  createEmptyRule,
  createEmptyGroup,
  createEmptyCondition,
  createEmptyMaCondition,
  createEmptyBollingerCondition,
  migrateMaConditionsToArray,
  getBuyConditionTemplates,
  getSellConditionTemplates,
} from '../services/strategyRuleApi';
import {
  createDSLStrategy,
  updateDSLStrategy,
  getDSLStrategies,
  getDSLStrategy,
  LogicNode,
  ConditionDSL,
  generateNodeId,
} from '../services/strategyDSLApi';
import { 
  getEnabledIndicators, 
  IndicatorMetadataDto,
  getCategoryDisplayName 
} from '../services/indicatorMetadataApi';
import { loadIndicatorSettings } from '../components/settings/IndicatorSettingsTab';
import {
  getAllAlertStrategies,
  addAlertStrategy,
  deleteAlertStrategy,
  AlertStrategyDto,
} from '../services/alertStrategyApi';

// Fallback: 보조지표 옵션 (DB 로드 실패 시 사용) - 업비트 표준 16개
const INDICATOR_OPTIONS_FALLBACK: { value: IndicatorType; label: string }[] = [
  { value: 'VOLUME', label: '거래량' },
  { value: 'MACD', label: 'MACD' },
  { value: 'STOCHASTIC', label: 'Stochastic Slow' },
  { value: 'RSI', label: 'RSI' },
  { value: 'ADX', label: 'ADX' },
  { value: 'CCI', label: 'CCI' },
  { value: 'TRIX', label: 'TRIX' },
  { value: 'DISPARITY', label: '이격도' },
  { value: 'NEW_PSYCHOLOGICAL', label: '신심리도' },
  { value: 'INVEST_PSYCHOLOGICAL', label: '투자심리도' },
  { value: 'WILLIAMS_R', label: 'Williams %R' },
  { value: 'BWI', label: '볼린저밴드 %B' },
  { value: 'OBV', label: 'OBV' },
  { value: 'VOLUME_OSC', label: 'VolumeOSC' },
  { value: 'VR', label: 'VR' },
  { value: 'DMI', label: 'DMI' },
];

// Fallback: 지표 라벨 맵 (DB 로드 실패 시 사용) - 업비트 표준 16개
const INDICATOR_LABEL_MAP_FALLBACK: { [key: string]: string } = {
  'VOLUME': '거래량',
  'MACD': 'MACD',
  'STOCHASTIC': 'Stochastic Slow',
  'RSI': 'RSI',
  'ADX': 'ADX',
  'CCI': 'CCI',
  'TRIX': 'TRIX',
  'DISPARITY': '이격도',
  'NEW_PSYCHOLOGICAL': '신심리도',
  'INVEST_PSYCHOLOGICAL': '투자심리도',
  'WILLIAMS_R': 'Williams %R',
  'BWI': '볼린저밴드 %B',
  'OBV': 'OBV',
  'VOLUME_OSC': 'VolumeOSC',
  'VR': 'VR',
  'DMI': 'DMI',
};

// 조건 라벨 맵
const CONDITION_LABEL_MAP: { [key: string]: string } = {
  'GOLDEN_CROSS': '골든크로스 (교차 순간)',
  'CROSS_UP': '기준값 돌파 (교차 순간)',
  'DEAD_CROSS': '데드크로스 (교차 순간)',
  'CROSS_DOWN': '기준값 하향 돌파 (교차 순간)',
  'ABOVE': '기준값 이상 (지속 상태)',
  'BELOW': '기준값 이하 (지속 상태)',
  'ABOVE_STRICT': '기준값 초과',
  'BELOW_STRICT': '기준값 미만',
  'GOLDEN_HOLD_CHECK': '골든크로스 유지 상태',
  'DEAD_HOLD_CHECK': '데드크로스 유지 상태',
  'BETWEEN': '범위',
  'INCREASING': '상승 중',
  'DECREASING': '하락 중',
  'OVERBOUGHT': '과매수',
  'OVERSOLD': '과매도',
  'ZERO_CROSS_UP': '0선 상향',
  'ZERO_CROSS_DOWN': '0선 하향',
};

// 조건 타입 옵션
const CONDITION_OPTIONS: { value: ConditionType; label: string }[] = [
  { value: 'GOLDEN_CROSS', label: '골든크로스 (교차 순간)' },
  { value: 'CROSS_UP', label: '기준값 돌파 (교차 순간)' },
  { value: 'DEAD_CROSS', label: '데드크로스 (교차 순간)' },
  { value: 'CROSS_DOWN', label: '기준값 하향 돌파 (교차 순간)' },
  { value: 'ABOVE', label: '기준값 이상 (>=)' },
  { value: 'BELOW', label: '기준값 이하 (<=)' },
  { value: 'ABOVE_STRICT', label: '기준값 초과 (>)' },
  { value: 'BELOW_STRICT', label: '기준값 미만 (<)' },
  { value: 'EQ', label: '기준값과 같음 (==)' },
  { value: 'NEQ', label: '기준값과 다름 (!=)' },
  { value: 'GOLDEN_HOLD_CHECK', label: '골든크로스 유지 상태' },
  { value: 'DEAD_HOLD_CHECK', label: '데드크로스 유지 상태' },
  { value: 'BETWEEN', label: '범위' },
  { value: 'INCREASING', label: '상승 중' },
  { value: 'DECREASING', label: '하락 중' },
  { value: 'OVERBOUGHT', label: '과매수' },
  { value: 'OVERSOLD', label: '과매도' },
  { value: 'ZERO_CROSS_UP', label: '0선 상향 교차' },
  { value: 'ZERO_CROSS_DOWN', label: '0선 하향 교차' },
];

// ========================================
// 지표별 허용 조건 매핑 (업비트 표준 16개) - 매수/매도 구분
// ========================================

// 매수 시그널용 조건
const BUY_CONDITIONS: ConditionType[] = [
  'GOLDEN_CROSS',     // 골든크로스 (상향 교차 순간)
  'CROSS_UP',         // 상향 유지
  'ZERO_CROSS_UP',    // 0선 상향 교차
  'ABOVE',
  'ABOVE_STRICT',
  'EQ',
  'NEQ',
  'OVERSOLD',
  'INCREASING'
];

// 매도 시그널용 조건
const SELL_CONDITIONS: ConditionType[] = [
  'DEAD_CROSS',       // 데드크로스 (하향 교차 순간)
  'CROSS_DOWN',       // 하향 유지
  'ZERO_CROSS_DOWN',  // 0선 하향 교차
  'BELOW',
  'BELOW_STRICT',
  'EQ',
  'NEQ',
  'OVERBOUGHT',
  'DECREASING'
];

const INDICATOR_CONDITIONS: { [key in IndicatorType]: ConditionType[] } = {
  // 거래량: 상향돌파, 하향이탈, 이상, 이하
  'VOLUME': ['CROSS_UP', 'CROSS_DOWN', 'ABOVE', 'BELOW', 'INCREASING', 'DECREASING'],
  
  // MACD: 골든크로스(MACD선이 시그널선 상향돌파)/상향유지/0선 돌파 (매수), 데드크로스/하향유지/0선 하락 (매도)
  'MACD': ['GOLDEN_CROSS', 'CROSS_UP', 'ZERO_CROSS_UP', 'DEAD_CROSS', 'CROSS_DOWN', 'ZERO_CROSS_DOWN'],
  
  // Stochastic Slow: %K가 %D를 골든크로스(상향돌파), 상향유지, 상향돌파(기준값)
  'STOCHASTIC': ['GOLDEN_CROSS', 'CROSS_UP', 'ABOVE'],
  
  // RSI: 골든크로스(RSI가 시그널선 상향돌파), 상향돌파(기준값), 상향유지
  'RSI': ['GOLDEN_CROSS', 'ABOVE', 'CROSS_UP', 'DEAD_CROSS', 'BELOW'],
  
  // ADX: 추세 강도 - 상향돌파, 하향이탈, 이상, 이하
  'ADX': ['CROSS_UP', 'CROSS_DOWN', 'ABOVE', 'BELOW', 'INCREASING', 'DECREASING'],
  
  // CCI: 골든크로스, 데드크로스, 상향돌파, 하향이탈
  'CCI': ['GOLDEN_CROSS', 'DEAD_CROSS', 'CROSS_UP', 'CROSS_DOWN',
          'ABOVE', 'BELOW', 'OVERBOUGHT', 'OVERSOLD'],
  
  // TRIX: Signal 골든크로스, 데드크로스, 0선 상향/하향
  'TRIX': ['GOLDEN_CROSS', 'DEAD_CROSS', 'ZERO_CROSS_UP', 'ZERO_CROSS_DOWN', 
           'INCREASING', 'DECREASING'],
  
  // 이격도: 상향돌파, 하향이탈
  'DISPARITY': ['CROSS_UP', 'CROSS_DOWN', 'ABOVE', 'BELOW'],
  
  // 신심리도: 상향돌파, 하향이탈
  'NEW_PSYCHOLOGICAL': ['CROSS_UP', 'CROSS_DOWN', 'ABOVE', 'BELOW', 'OVERBOUGHT', 'OVERSOLD'],
  
  // 투자심리도: 상향돌파, 하향이탈
  'INVEST_PSYCHOLOGICAL': ['CROSS_UP', 'CROSS_DOWN', 'ABOVE', 'BELOW', 'OVERBOUGHT', 'OVERSOLD'],
  
  // Williams %R: 상향돌파, 하향이탈 (골든/데드 크로스 없음 - 단일 라인)
  'WILLIAMS_R': ['CROSS_UP', 'CROSS_DOWN', 'ABOVE', 'BELOW', 'OVERBOUGHT', 'OVERSOLD'],
  
  // 볼린저밴드 %B: 상향돌파, 하향이탈
  'BWI': ['CROSS_UP', 'CROSS_DOWN', 'ABOVE', 'BELOW', 'OVERBOUGHT', 'OVERSOLD'],
  
  // OBV: 상향돌파, 하향이탈 (이동평균 기준)
  'OBV': ['GOLDEN_CROSS', 'DEAD_CROSS', 'INCREASING', 'DECREASING'],
  
  // Volume OSC: 0선 상향/하향, 상향돌파, 하향이탈
  'VOLUME_OSC': ['CROSS_UP', 'CROSS_DOWN', 'ZERO_CROSS_UP', 'ZERO_CROSS_DOWN', 
                 'ABOVE', 'BELOW'],
  
  // VR: 상향돌파, 하향이탈
  'VR': ['CROSS_UP', 'CROSS_DOWN', 'ABOVE', 'BELOW', 'OVERBOUGHT', 'OVERSOLD'],
  
  // DMI: +DI/-DI 골든크로스, 데드크로스
  'DMI': ['GOLDEN_CROSS', 'DEAD_CROSS', 'ABOVE', 'BELOW', 'INCREASING', 'DECREASING'],
};

// 지표별 기준값 범위 및 설정
interface TargetValueConfig {
  min: number;
  max: number;
  step: number;
  defaultValue: number;
  required: boolean;  // 기준값이 필수인지
  helperText?: string;
}

// 업비트 표준 16개 지표 기준값 설정
const INDICATOR_TARGET_VALUE_CONFIG: { [key in IndicatorType]: TargetValueConfig } = {
  'VOLUME': { min: 0, max: 1000000000, step: 1000, defaultValue: 100000, required: true, helperText: '거래량' },
  'MACD': { min: -100, max: 100, step: 0.1, defaultValue: 0, required: false, helperText: 'Signal 크로스 조건' },
  'STOCHASTIC': { min: 0, max: 100, step: 1, defaultValue: 20, required: true, helperText: '0-100 (%K/%D)' },
  'RSI': { min: 0, max: 100, step: 1, defaultValue: 30, required: true, helperText: '0-100' },
  'ADX': { min: 0, max: 100, step: 1, defaultValue: 25, required: true, helperText: '0-100 (추세 강도)' },
  'CCI': { min: -300, max: 300, step: 1, defaultValue: 100, required: true, helperText: '-200~200' },
  'TRIX': { min: -10, max: 10, step: 0.01, defaultValue: 0, required: false, helperText: 'Signal 크로스 조건' },
  'DISPARITY': { min: 50, max: 150, step: 0.1, defaultValue: 100, required: true, helperText: '80-120 (100=기준)' },
  'NEW_PSYCHOLOGICAL': { min: 0, max: 100, step: 1, defaultValue: 25, required: true, helperText: '0-100 (25이하=매수, 75이상=매도)' },
  'INVEST_PSYCHOLOGICAL': { min: 0, max: 100, step: 1, defaultValue: 25, required: true, helperText: '0-100 (25이하=매수, 75이상=매도)' },
  'WILLIAMS_R': { min: -100, max: 0, step: 1, defaultValue: -80, required: true, helperText: '-100~0 (과매도:-80, 과매수:-20)' },
  'BWI': { min: 0, max: 100, step: 1, defaultValue: 20, required: true, helperText: '0-100 (0=하단, 100=상단)' },
  'OBV': { min: -1000000000, max: 1000000000, step: 1000, defaultValue: 0, required: false, helperText: '거래량 잔고' },
  'VOLUME_OSC': { min: -100, max: 100, step: 1, defaultValue: 0, required: true, helperText: '-100~100 (0=기준)' },
  'VR': { min: 0, max: 500, step: 1, defaultValue: 100, required: true, helperText: '0-500 (100=기준)' },
  'DMI': { min: 0, max: 100, step: 1, defaultValue: 25, required: false, helperText: '+DI/-DI 크로스 조건' },
};

// ========================================
// 지표별 사전 정의된 기준값 프리셋 (업비트 표준 기반)
// ========================================
interface PresetValue {
  value: number | null;  // null은 "없음" 또는 크로스 조건용
  label: string;
  forBuy?: boolean;      // 매수 시그널용
  forSell?: boolean;     // 매도 시그널용
}

// 지표별 프리셋 기준값 목록
const INDICATOR_PRESET_VALUES: { [key in IndicatorType]: PresetValue[] } = {
  // ========================================
  // CCI (Commodity Channel Index): -200 ~ 200
  // 매수: 골든크로스, -100 상향돌파
  // 매도: 데드크로스, 100 하향이탈
  // ========================================
  'CCI': [
    { value: null, label: '없음 (크로스)' },
    // 매수용
    { value: -100, label: '-100 (과매도)', forBuy: true },
    { value: 0, label: '0선 상향돌파', forBuy: true },
    { value: 100, label: '100 (과매수진입)', forBuy: true },
    // 매도용
    { value: 100, label: '100 (과매수)', forSell: true },
    { value: 0, label: '0선 하향돌파', forSell: true },
    { value: -100, label: '-100 (침체이탈)', forSell: true },
  ],
  
  // ========================================
  // Stochastic Slow: 0 ~ 100
  // 매수: 골든크로스, 20 상향돌파
  // 매도: 데드크로스, 80 하향이탈
  // ========================================
  'STOCHASTIC': [
    { value: null, label: '없음 (크로스)' },
    { value: 80, label: '80 (과매수)', forSell: true },
    { value: 50, label: '50 (중립)' },
    { value: 20, label: '20 (과매도)', forBuy: true },
    { value: 10, label: '10 (극단적 과매도)', forBuy: true },
    { value: 90, label: '90 (극단적 과매수)', forSell: true },
  ],
  
  // ========================================
  // RSI (Relative Strength Index): 0 ~ 100
  // 매수: 골든크로스, 30 상향돌파
  // 매도: 데드크로스, 70 하향이탈
  // ========================================
  'RSI': [
    { value: null, label: '없음 (크로스)' },
    { value: 70, label: '70 (과매수)', forSell: true },
    { value: 50, label: '50 (중립)' },
    { value: 30, label: '30 (과매도)', forBuy: true },
    { value: 80, label: '80 (강한 과매수)', forSell: true },
    { value: 20, label: '20 (강한 과매도)', forBuy: true },
  ],
  
  // ========================================
  // Williams %R: -100 ~ 0 (단일 라인 - 골든/데드 크로스 없음)
  // 매수: -80 상향돌파
  // 매도: -20 하향이탈
  // ========================================
  'WILLIAMS_R': [
    { value: -20, label: '-20 (과매수)', forSell: true },
    { value: -50, label: '-50 (중립)' },
    { value: -80, label: '-80 (과매도)', forBuy: true },
    { value: -10, label: '-10 (극단적 과매수)', forSell: true },
    { value: -90, label: '-90 (극단적 과매도)', forBuy: true },
  ],
  
  // ========================================
  // 이격도 (Disparity): 80 ~ 120 (100 = 기준)
  // 매수: 100 상향돌파 (5일/10일/20일/60일)
  // 매도: 100 하향이탈 (5일/10일/20일/60일)
  // ========================================
  'DISPARITY': [
    { value: 100, label: '100 (기준선)' },
    { value: 98, label: '98 (약간 과매도)', forBuy: true },
    { value: 95, label: '95 (과매도)', forBuy: true },
    { value: 90, label: '90 (강한 과매도)', forBuy: true },
    { value: 102, label: '102 (약간 과매수)', forSell: true },
    { value: 105, label: '105 (과매수)', forSell: true },
    { value: 110, label: '110 (강한 과매수)', forSell: true },
  ],
  
  // ========================================
  // ADX (Average Directional Index): 0 ~ 100
  // 추세 강도 판단 (방향성 없음)
  // ========================================
  'ADX': [
    { value: 15, label: '15 (추세 없음)' },
    { value: 20, label: '20 (약한 추세)' },
    { value: 25, label: '25 (추세 발생)' },
    { value: 30, label: '30 (중간 추세)' },
    { value: 40, label: '40 (강한 추세)' },
    { value: 50, label: '50 (매우 강한 추세)' },
  ],
  
  // ========================================
  // MACD: Signal 크로스, 0선 크로스
  // 매수: 골든크로스 (MACD선이 시그널선 상향돌파), 상향유지, 0선 상향
  // 매도: 데드크로스 (MACD선이 시그널선 하향이탈), 하향유지, 0선 하향
  // ========================================
  'MACD': [
    { value: null, label: 'Signal 크로스' },
    { value: 0, label: '0 (기준선)' },
  ],
  
  // ========================================
  // TRIX: Signal 크로스, 0선 크로스
  // 매수: 골든크로스, 0선 상향
  // 매도: 데드크로스, 0선 하향
  // ========================================
  'TRIX': [
    { value: null, label: 'Signal 크로스' },
    { value: 0, label: '0 (기준선)' },
  ],
  
  // ========================================
  // 신심리도 (New Psychological Line): 0 ~ 100
  // 매수: 25 상향돌파
  // 매도: 75 하향이탈
  // ========================================
  'NEW_PSYCHOLOGICAL': [
    { value: null, label: '없음' },
    { value: 75, label: '75 (과매수)', forSell: true },
    { value: 50, label: '50 (중립)' },
    { value: 25, label: '25 (과매도)', forBuy: true },
    { value: 80, label: '80 (강한 과매수)', forSell: true },
    { value: 20, label: '20 (강한 과매도)', forBuy: true },
  ],
  
  // ========================================
  // 투자심리도 (Investment Psychological Line): 0 ~ 100
  // 매수: 25 상향돌파
  // 매도: 75 하향이탈
  // ========================================
  'INVEST_PSYCHOLOGICAL': [
    { value: null, label: '없음' },
    { value: 75, label: '75 (과매수)', forSell: true },
    { value: 50, label: '50 (중립)' },
    { value: 25, label: '25 (과매도)', forBuy: true },
    { value: 80, label: '80 (강한 과매수)', forSell: true },
    { value: 20, label: '20 (강한 과매도)', forBuy: true },
  ],
  
  // ========================================
  // 볼린저밴드 %B (BWI): 0 ~ 100
  // 매수: 0 상향돌파 (하단밴드)
  // 매도: 100 하향이탈 (상단밴드)
  // ========================================
  'BWI': [
    { value: null, label: '없음' },
    { value: 100, label: '100 (상단밴드)', forSell: true },
    { value: 80, label: '80', forSell: true },
    { value: 50, label: '50 (중심선)' },
    { value: 20, label: '20', forBuy: true },
    { value: 0, label: '0 (하단밴드)', forBuy: true },
  ],
  
  // ========================================
  // OBV (On Balance Volume): 이동평균 크로스
  // 매수: 골든크로스 (OBV가 이동평균 상향돌파)
  // 매도: 데드크로스 (OBV가 이동평균 하향이탈)
  // ========================================
  'OBV': [
    { value: null, label: '이동평균 크로스' },
  ],
  
  // ========================================
  // Volume Oscillator: -100 ~ 100
  // 매수: 0선 상향돌파
  // 매도: 0선 하향이탈
  // ========================================
  'VOLUME_OSC': [
    { value: null, label: '없음' },
    { value: 0, label: '0 (기준선)' },
    { value: 10, label: '10' },
    { value: 20, label: '20' },
    { value: -10, label: '-10' },
    { value: -20, label: '-20' },
  ],
  
  // ========================================
  // VR (Volume Ratio): 0 ~ 500 (100 = 기준)
  // 매수: 70 상향돌파
  // 매도: 150 하향이탈
  // ========================================
  'VR': [
    { value: 100, label: '100 (기준)' },
    { value: 150, label: '150 (과매수)', forSell: true },
    { value: 200, label: '200 (강한 과매수)', forSell: true },
    { value: 70, label: '70 (과매도)', forBuy: true },
    { value: 50, label: '50 (강한 과매도)', forBuy: true },
  ],
  
  // ========================================
  // DMI (Directional Movement Index): +DI/-DI 크로스
  // 매수: 골든크로스 (+DI가 -DI 상향돌파)
  // 매도: 데드크로스 (+DI가 -DI 하향이탈)
  // ========================================
  'DMI': [
    { value: null, label: '+DI/-DI 크로스' },
    { value: 20, label: '20' },
    { value: 25, label: '25' },
    { value: 30, label: '30' },
    { value: 40, label: '40 (강한 추세)' },
  ],
  
  // ========================================
  // 거래량 (Volume): 직접 입력
  // ========================================
  'VOLUME': [
    { value: null, label: '직접 입력' },
  ],
};

// ========================================
// 지표별 기간 옵션 (기간 설정이 필요한 지표)
// ========================================
const INDICATOR_PERIOD_OPTIONS: { [key: string]: { value: number; label: string }[] } = {
  // 이격도: 5일, 10일, 20일, 60일
  'DISPARITY': [
    { value: 5, label: '5일' },
    { value: 10, label: '10일' },
    { value: 20, label: '20일' },
    { value: 60, label: '60일' },
    { value: 120, label: '120일' },
  ],
  
  // RSI 기간
  'RSI': [
    { value: 6, label: '6일 (초단기)' },
    { value: 9, label: '9일 (단기)' },
    { value: 13, label: '13일' },
    { value: 14, label: '14일 (기본)' },
    { value: 21, label: '21일 (장기)' },
    { value: 28, label: '28일' },
  ],
  
  // Stochastic 기간
  'STOCHASTIC': [
    { value: 5, label: '5일 (단기)' },
    { value: 9, label: '9일' },
    { value: 13, label: '13일' },
    { value: 14, label: '14일 (기본)' },
    { value: 21, label: '21일 (장기)' },
  ],
  
  // CCI 기간
  'CCI': [
    { value: 9, label: '9일 (단기)' },
    { value: 13, label: '13일' },
    { value: 14, label: '14일' },
    { value: 20, label: '20일 (기본)' },
    { value: 40, label: '40일' },
  ],
  
  // Williams %R 기간
  'WILLIAMS_R': [
    { value: 11, label: '11일 (기본)' },
    { value: 14, label: '14일' },
    { value: 20, label: '20일' },
  ],
  
  // ADX 기간
  'ADX': [
    { value: 13, label: '13일' },
    { value: 14, label: '14일 (기본)' },
    { value: 21, label: '21일' },
    { value: 28, label: '28일' },
  ],
  
  // 신심리도 기간
  'NEW_PSYCHOLOGICAL': [
    { value: 10, label: '10일' },
    { value: 12, label: '12일 (기본)' },
    { value: 20, label: '20일' },
  ],
  
  // 투자심리도 기간
  'INVEST_PSYCHOLOGICAL': [
    { value: 10, label: '10일' },
    { value: 12, label: '12일 (기본)' },
    { value: 20, label: '20일' },
  ],
  
  // 볼린저밴드 기간
  'BWI': [
    { value: 20, label: '20일 (기본)' },
    { value: 10, label: '10일 (단기)' },
    { value: 50, label: '50일 (장기)' },
  ],
  
  // VR 기간
  'VR': [
    { value: 14, label: '14일' },
    { value: 20, label: '20일 (기본)' },
    { value: 26, label: '26일' },
  ],
  
  // MACD 기간 (MACD선 - 시그널선 기간 사용)
  'MACD': [
    { value: 9, label: 'MACD선 (시그널 기본 9일)' },
  ],
  
  // TRIX 기간
  'TRIX': [
    { value: 12, label: '12일 (기본)' },
    { value: 9, label: '9일 (단기)' },
    { value: 18, label: '18일 (장기)' },
  ],
  
  // Volume OSC 기간
  'VOLUME_OSC': [
    { value: 5, label: '5일 (단기)' },
    { value: 10, label: '10일' },
    { value: 20, label: '20일 (장기)' },
  ],
  
  // OBV 이동평균 기간
  'OBV': [
    { value: 5, label: '5일 (단기)' },
    { value: 10, label: '10일' },
    { value: 20, label: '20일 (기본)' },
    { value: 60, label: '60일 (장기)' },
  ],
  
  // DMI 기간
  'DMI': [
    { value: 14, label: '14일 (기본)' },
    { value: 21, label: '21일' },
  ],
};

// 조건 타입에 따른 한글 설명 (상세)
const getConditionDescription = (indicatorType: IndicatorType, conditionType: ConditionType): string => {
  const descriptions: { [key: string]: { [key: string]: string } } = {
    'MACD': {
      'GOLDEN_CROSS': 'MACD선이 Signal선을 상향돌파',
      'DEAD_CROSS': 'MACD선이 Signal선을 하향이탈',
      'ZERO_CROSS_UP': 'MACD선이 0선을 상향돌파',
      'ZERO_CROSS_DOWN': 'MACD선이 0선을 하향이탈',
    },
    'STOCHASTIC': {
      'GOLDEN_CROSS': '%K가 %D를 상향돌파 (골든크로스)',
      'CROSS_UP': '%K가 %D 상향에 존재 (상향유지)',
      'ABOVE': '기준값 상향돌파',
      'DEAD_CROSS': '%K가 %D를 하향이탈 (데드크로스)',
      'CROSS_DOWN': '%K가 %D 하향에 존재 (하향유지)',
      'BELOW': '기준값 하향돌파',
    },
    'RSI': {
      'GOLDEN_CROSS': 'RSI가 시그널선을 상향돌파 (골든크로스)',
      'CROSS_UP': 'RSI가 시그널선 상향에 존재 (상향유지)',
      'ABOVE': '기준값 상향돌파',
      'DEAD_CROSS': 'RSI가 시그널선을 하향이탈 (데드크로스)',
      'BELOW': '기준값 하향돌파',
    },
    'CCI': {
      'GOLDEN_CROSS': 'CCI가 Signal선을 상향돌파',
      'DEAD_CROSS': 'CCI가 Signal선을 하향이탈',
      'CROSS_UP': '기준값 상향돌파',
      'CROSS_DOWN': '기준값 하향이탈',
    },
    'DMI': {
      'GOLDEN_CROSS': '+DI가 -DI를 상향돌파',
      'DEAD_CROSS': '+DI가 -DI를 하향이탈',
    },
    'OBV': {
      'GOLDEN_CROSS': 'OBV가 이동평균선을 상향돌파',
      'DEAD_CROSS': 'OBV가 이동평균선을 하향이탈',
    },
    'TRIX': {
      'GOLDEN_CROSS': 'TRIX가 Signal선을 상향돌파',
      'DEAD_CROSS': 'TRIX가 Signal선을 하향이탈',
      'ZERO_CROSS_UP': 'TRIX가 0선을 상향돌파',
      'ZERO_CROSS_DOWN': 'TRIX가 0선을 하향이탈',
    },
  };
  
  return descriptions[indicatorType]?.[conditionType] || '';
};

// 지표별 조건 옵션 가져오기
const getConditionOptionsForIndicator = (indicatorType: IndicatorType): { value: ConditionType; label: string }[] => {
  const allowedConditions = INDICATOR_CONDITIONS[indicatorType] || [];
  return CONDITION_OPTIONS.filter(opt => allowedConditions.includes(opt.value));
};

// ============================================================
// ConditionGroup ↔ LogicNode 변환 유틸리티
// ============================================================

/**
 * StrategyConditionDto → ConditionDSL 변환
 */
const conditionDtoToDSL = (condition: StrategyConditionDto): ConditionDSL => {
  // conditionType 매핑: 백엔드 enum → DSL 형식
  let dslConditionType: string = condition.conditionType;
  if (condition.conditionType === 'ABOVE' && condition.targetValue !== undefined) {
    dslConditionType = 'GTE'; // >= (이상)
  } else if (condition.conditionType === 'BELOW' && condition.targetValue !== undefined) {
    dslConditionType = 'LTE'; // <= (이하)
  } else if (condition.conditionType === 'ABOVE_STRICT') {
    dslConditionType = 'GT'; // > (초과)
  } else if (condition.conditionType === 'BELOW_STRICT') {
    dslConditionType = 'LT'; // < (미만)
  }
  
  return {
    indicatorType: condition.indicatorType,
    conditionType: dslConditionType,
    targetValue: condition.targetValue,
    secondaryValue: condition.secondaryValue,
    period: condition.period,
    comparePeriod: condition.comparePeriod,
  };
};

/**
 * ConditionGroupDto → LogicNode 변환
 */
const conditionGroupToLogicNode = (group: ConditionGroupDto): LogicNode => {
  if (!group.conditions || group.conditions.length === 0) {
    throw new Error('조건 그룹에 조건이 없습니다');
  }
  
  // 조건이 1개만 있으면 단일 CONDITION 노드
  if (group.conditions.length === 1) {
    return {
      id: generateNodeId(),
      type: 'CONDITION',
      condition: conditionDtoToDSL(group.conditions[0]),
      description: group.conditions[0].description,
    };
  }
  
  // 조건이 여러 개면 AND/OR 노드
  // 그룹 내 조건들은 logicalOperator로 연결 (기본값: OR)
  const operator = group.logicalOperator || 'OR';
  
  return {
    id: generateNodeId(),
    type: operator,
    children: group.conditions.map(cond => ({
      id: generateNodeId(),
      type: 'CONDITION',
      condition: conditionDtoToDSL(cond),
      description: cond.description,
    })),
    description: group.name,
  };
};

/**
 * 여러 ConditionGroup → LogicNode 변환 (그룹 간 연산자 적용)
 */
const conditionGroupsToLogicNode = (groups: ConditionGroupDto[]): LogicNode | null => {
  if (!groups || groups.length === 0) {
    return null;
  }
  
  // 그룹이 1개만 있으면 해당 그룹을 LogicNode로 변환
  if (groups.length === 1) {
    return conditionGroupToLogicNode(groups[0]);
  }
  
  // 그룹이 여러 개면 그룹 간 연산자로 연결
  // 첫 번째 그룹의 interGroupOperator가 다음 그룹과의 연결 연산자
  const firstGroupOperator = groups[0].interGroupOperator || 'OR';
  
  // 모든 그룹이 동일한 연산자를 사용하는지 확인
  const allSameOperator = groups.every((g, idx) => 
    idx === groups.length - 1 || (g.interGroupOperator || 'OR') === firstGroupOperator
  );
  
  if (allSameOperator) {
    // 모두 같은 연산자면 하나의 AND/OR 노드로 묶음
    return {
      id: generateNodeId(),
      type: firstGroupOperator,
      children: groups.map(g => conditionGroupToLogicNode(g)),
      description: `${groups.length}개 그룹 (${firstGroupOperator})`,
    };
  } else {
    // 연산자가 다르면 순차적으로 중첩
    let rootNode = conditionGroupToLogicNode(groups[0]);
    
    for (let i = 1; i < groups.length; i++) {
      const operator = groups[i - 1].interGroupOperator || 'OR';
      const nextGroupNode = conditionGroupToLogicNode(groups[i]);
      
      rootNode = {
        id: generateNodeId(),
        type: operator,
        children: [rootNode, nextGroupNode],
        description: `그룹 ${i} ${operator} 그룹 ${i + 1}`,
      };
    }
    
    return rootNode;
  }
};

/**
 * ConditionDSL → StrategyConditionDto 변환 (역변환)
 */
const conditionDSLtoDto = (dsl: ConditionDSL): StrategyConditionDto => {
  // DSL conditionType → 백엔드 enum 매핑
  let mappedConditionType: string = dsl.conditionType;
  
  // 특정 변환
  if (dsl.conditionType === 'GTE') {
    mappedConditionType = 'ABOVE'; // >= → ABOVE
  } else if (dsl.conditionType === 'LTE') {
    mappedConditionType = 'BELOW'; // <= → BELOW
  } else if (dsl.conditionType === 'GT') {
    mappedConditionType = 'ABOVE_STRICT'; // > → ABOVE_STRICT
  } else if (dsl.conditionType === 'LT') {
    mappedConditionType = 'BELOW_STRICT'; // < → BELOW_STRICT
  }
  
  return {
    indicatorType: dsl.indicatorType as IndicatorType,
    conditionType: mappedConditionType as ConditionType,
    targetValue: dsl.targetValue,
    secondaryValue: dsl.secondaryValue,
    period: dsl.period,
    comparePeriod: dsl.comparePeriod,
    description: '', // 필요 시 생성
  };
};

/**
 * LogicNode → ConditionGroupDto 변환 (역변환)
 */
const logicNodeToConditionGroup = (node: LogicNode, groupIndex: number): ConditionGroupDto => {
  if (node.type === 'CONDITION' && node.condition) {
    // 단일 조건 노드
    return {
      name: `그룹 ${groupIndex + 1}`,
      logicalOperator: 'OR',
      conditions: [conditionDSLtoDto(node.condition)],
    };
  }
  
  // AND/OR 노드
  if ((node.type === 'AND' || node.type === 'OR') && node.children) {
    // 모든 자식이 CONDITION 노드인지 확인
    const allConditions = node.children.every(child => child.type === 'CONDITION');
    
    if (allConditions) {
      // 모두 CONDITION이면 하나의 그룹으로 변환
      return {
        name: `그룹 ${groupIndex + 1}`,
        logicalOperator: node.type,
        conditions: node.children
          .filter(child => child.condition)
          .map(child => conditionDSLtoDto(child.condition!)),
      };
    } else {
      // 중첩된 논리 구조 → 첫 번째 자식만 변환 (간소화)
      // 실제로는 재귀적으로 처리해야 하지만, UI 복잡도를 고려하여 단순화
      console.warn('⚠️ 중첩된 논리 구조는 단순화하여 표시합니다:', node);
      return logicNodeToConditionGroup(node.children[0], groupIndex);
    }
  }
  
  // NOT 노드는 현재 UI에서 지원하지 않으므로 스킵
  if (node.type === 'NOT' && node.children && node.children.length > 0) {
    console.warn('⚠️ NOT 연산자는 현재 UI에서 지원하지 않습니다:', node);
    return logicNodeToConditionGroup(node.children[0], groupIndex);
  }
  
  // 기본값 (빈 그룹)
  return {
    name: `그룹 ${groupIndex + 1}`,
    logicalOperator: 'OR',
    conditions: [],
  };
};

/**
 * LogicNode (DSL) → ConditionGroupDto[] 배열 변환
 */
const logicNodeToConditionGroups = (node: LogicNode | null): ConditionGroupDto[] => {
  if (!node) {
    return [];
  }
  
  // 루트가 AND/OR이고 여러 자식이 있으면 각각을 그룹으로 변환
  if ((node.type === 'AND' || node.type === 'OR') && node.children && node.children.length > 1) {
    const groups = node.children.map((child, idx) => logicNodeToConditionGroup(child, idx));
    
    // 그룹 간 연산자 설정 (루트의 type)
    // NOT은 LogicalOperator에 없으므로 AND/OR만 사용
    const groupOperator: LogicalOperator | undefined = 
      node.type === 'AND' || node.type === 'OR' ? node.type : 'OR';
    
    return groups.map((group, idx) => ({
      ...group,
      interGroupOperator: idx < groups.length - 1 ? groupOperator : undefined,
    }));
  }
  
  // 단일 노드면 하나의 그룹으로 변환
  return [logicNodeToConditionGroup(node, 0)];
};

// 조건에 따라 기준값이 필요한지 판단 (업비트 16개 지표 기준)
const isTargetValueRequired = (indicatorType: IndicatorType, conditionType: ConditionType): boolean => {
  // MACD/TRIX의 CROSS_UP/CROSS_DOWN은 Signal 크로스이므로 기준값 불필요
  if (['MACD', 'TRIX'].includes(indicatorType) && 
      ['CROSS_UP', 'CROSS_DOWN'].includes(conditionType)) {
    return false;
  }
  // Stochastic의 GOLDEN_CROSS, DEAD_CROSS, CROSS_UP, CROSS_DOWN은 %K/%D 크로스이므로 기준값 불필요
  if (indicatorType === 'STOCHASTIC' && ['GOLDEN_CROSS', 'DEAD_CROSS', 'CROSS_UP', 'CROSS_DOWN'].includes(conditionType)) {
    return false;
  }
  // Stochastic의 ABOVE, BELOW는 기준값 필요
  if (indicatorType === 'STOCHASTIC' && ['ABOVE', 'BELOW'].includes(conditionType)) {
    return true;
  }
  // RSI의 GOLDEN_CROSS, DEAD_CROSS, CROSS_UP은 시그널선 크로스이므로 기준값 불필요
  if (indicatorType === 'RSI' && ['GOLDEN_CROSS', 'DEAD_CROSS', 'CROSS_UP'].includes(conditionType)) {
    return false;
  }
  // RSI의 ABOVE, BELOW는 기준값 필요
  if (indicatorType === 'RSI' && ['ABOVE', 'BELOW'].includes(conditionType)) {
    return true;
  }
  // CCI의 GOLDEN_CROSS, DEAD_CROSS, CROSS_UP은 시그널선 크로스이므로 기준값 불필요
  if (indicatorType === 'CCI' && ['GOLDEN_CROSS', 'DEAD_CROSS', 'CROSS_UP'].includes(conditionType)) {
    return false;
  }
  // CCI의 ABOVE, BELOW는 기준값 필요
  if (indicatorType === 'CCI' && ['ABOVE', 'BELOW'].includes(conditionType)) {
    return true;
  }
  // TRIX의 GOLDEN_CROSS, DEAD_CROSS, CROSS_UP은 시그널선 크로스이므로 기준값 불필요
  if (indicatorType === 'TRIX' && ['GOLDEN_CROSS', 'DEAD_CROSS', 'CROSS_UP'].includes(conditionType)) {
    return false;
  }
  // TRIX의 ABOVE, BELOW는 기준값 필요
  if (indicatorType === 'TRIX' && ['ABOVE', 'BELOW'].includes(conditionType)) {
    return true;
  }
  // DISPARITY(이격도)는 모든 조건에서 기준값 필요 (95선, 100선, 105선)
  if (indicatorType === 'DISPARITY' && ['ABOVE', 'BELOW', 'CROSS_UP'].includes(conditionType)) {
    return true;
  }
  // NEW_PSYCHOLOGICAL(신심리도)는 상향돌파/하향돌파에서 기준값 필요
  if (indicatorType === 'NEW_PSYCHOLOGICAL' && ['ABOVE', 'BELOW'].includes(conditionType)) {
    return true;
  }
  // WILLIAMS_R은 상향돌파/하향돌파에서 기준값 필요
  if (indicatorType === 'WILLIAMS_R' && ['ABOVE', 'BELOW'].includes(conditionType)) {
    return true;
  }
  // DMI의 CROSS_UP/CROSS_DOWN은 +DI/-DI 크로스이므로 기준값 불필요
  if (indicatorType === 'DMI' && ['CROSS_UP', 'CROSS_DOWN'].includes(conditionType)) {
    return false;
  }
  // OBV의 CROSS_UP/CROSS_DOWN은 기준값 불필요
  if (indicatorType === 'OBV' && ['CROSS_UP', 'CROSS_DOWN'].includes(conditionType)) {
    return false;
  }
  // 골든/데드 크로스는 기간 비교이므로 기준값 불필요
  if (['GOLDEN_CROSS', 'DEAD_CROSS'].includes(conditionType)) {
    return false;
  }
  // 0선 크로스는 기준값 불필요
  if (['ZERO_CROSS_UP', 'ZERO_CROSS_DOWN'].includes(conditionType)) {
    return false;
  }
  // INCREASING/DECREASING은 기준값 불필요
  if (['INCREASING', 'DECREASING'].includes(conditionType)) {
    return false;
  }
  // 나머지 조건은 기준값 필요
  return ['CROSS_UP', 'CROSS_DOWN', 'ABOVE', 'BELOW', 'ABOVE_STRICT', 'BELOW_STRICT',
          'EQ', 'NEQ', 'BETWEEN', 'OVERBOUGHT', 'OVERSOLD'].includes(conditionType);
};

/**
 * 전략관리 페이지
 */
const StrategyRulePage: React.FC = () => {
  const theme = useTheme();
  const isDark = theme.palette.mode === 'dark';
  
  // 보조지표 메타데이터 (DB에서 동적 로드)
  const [indicatorMetadata, setIndicatorMetadata] = useState<IndicatorMetadataDto[]>([]);
  const [metadataLoading, setMetadataLoading] = useState(true);
  
  // 보조지표 메타데이터 로드
  useEffect(() => {
    const loadIndicatorMetadata = async () => {
      try {
        setMetadataLoading(true);
        const data = await getEnabledIndicators();
        setIndicatorMetadata(data);
        console.log('📊 보조지표 메타데이터 로드 완료:', data.length, '개');
      } catch (err) {
        console.error('보조지표 메타데이터 로드 실패:', err);
        // 실패 시 빈 배열 유지 (하드코딩된 fallback 사용)
      } finally {
        setMetadataLoading(false);
      }
    };
    loadIndicatorMetadata();
  }, []);
  
  // DB에서 로드된 보조지표 옵션 (동적)
  const dynamicIndicatorOptions = useMemo(() => {
    if (indicatorMetadata.length === 0) return null; // fallback 사용
    return indicatorMetadata.map(m => ({
      value: m.indicatorType as IndicatorType,
      label: m.displayName,
      category: m.category,
    }));
  }, [indicatorMetadata]);
  
  // DB에서 로드된 지표 라벨 맵 (동적)
  const dynamicIndicatorLabelMap = useMemo(() => {
    if (indicatorMetadata.length === 0) return null; // fallback 사용
    const map: { [key: string]: string } = {};
    indicatorMetadata.forEach(m => {
      map[m.indicatorType] = m.displayName;
    });
    return map;
  }, [indicatorMetadata]);
  
  // DB에서 로드된 지표별 허용 조건 (동적)
  const dynamicIndicatorConditions = useMemo(() => {
    if (indicatorMetadata.length === 0) return null; // fallback 사용
    const map: { [key: string]: ConditionType[] } = {};
    indicatorMetadata.forEach(m => {
      map[m.indicatorType] = m.allowedConditions.split(',') as ConditionType[];
    });
    return map;
  }, [indicatorMetadata]);
  
  // DB에서 로드된 지표별 설정값 (동적)
  const dynamicIndicatorConfig = useMemo(() => {
    if (indicatorMetadata.length === 0) return null; // fallback 사용
    const map: { [key: string]: { min: number; max: number; step: number; defaultValue: number; required: boolean; helperText?: string } } = {};
    indicatorMetadata.forEach(m => {
      map[m.indicatorType] = {
        min: m.minValue,
        max: m.maxValue,
        step: m.step,
        defaultValue: m.defaultValue,
        required: m.targetValueRequired,
        helperText: m.helperText,
      };
    });
    return map;
  }, [indicatorMetadata]);
  
  // 실제 사용할 보조지표 옵션 (DB 우선, fallback으로 하드코딩)
  const FINAL_INDICATOR_OPTIONS = dynamicIndicatorOptions || INDICATOR_OPTIONS_FALLBACK;
  const FINAL_INDICATOR_LABEL_MAP = dynamicIndicatorLabelMap || INDICATOR_LABEL_MAP_FALLBACK;
  const FINAL_INDICATOR_CONDITIONS = dynamicIndicatorConditions || INDICATOR_CONDITIONS;
  const FINAL_INDICATOR_CONFIG = dynamicIndicatorConfig || INDICATOR_TARGET_VALUE_CONFIG;
  
  // 지표별 조건 옵션 가져오기 (동적 데이터 사용)
  const getConditionOptionsForIndicatorDynamic = useCallback((indicatorType: IndicatorType, signalType?: SignalType) => {
    // MACD 특화: 매수 3개, 매도 3개
    if (indicatorType === 'MACD') {
      if (signalType === 'BUY') {
        return [
          { value: 'GOLDEN_CROSS' as ConditionType, label: '골든크로스 (상향 교차 순간)' },
          { value: 'CROSS_UP' as ConditionType, label: '상향 유지 (골든크로스 이후)' },
          { value: 'ZERO_CROSS_UP' as ConditionType, label: '0선 상향' },
        ];
      } else if (signalType === 'SELL') {
        return [
          { value: 'DEAD_CROSS' as ConditionType, label: '데드크로스 (하향 교차 순간)' },
          { value: 'CROSS_DOWN' as ConditionType, label: '하향 유지 (데드크로스 이후)' },
          { value: 'ZERO_CROSS_DOWN' as ConditionType, label: '0선 하향' },
        ];
      }
    }
    
    // Stochastic 특화: 매수 3개, 매도 2개
    if (indicatorType === 'STOCHASTIC') {
      if (signalType === 'BUY') {
        return [
          { value: 'GOLDEN_CROSS' as ConditionType, label: '골든크로스 (상향 교차 순간)' },
          { value: 'ABOVE' as ConditionType, label: '상향 돌파' },
          { value: 'CROSS_UP' as ConditionType, label: '상향 유지 (골든크로스 이후)' },
        ];
      } else if (signalType === 'SELL') {
        return [
          { value: 'DEAD_CROSS' as ConditionType, label: '데드크로스 (하향 교차 순간)' },
          { value: 'BELOW' as ConditionType, label: '하향 돌파' },
        ];
      }
    }
    
    // RSI 특화: 매수 3개, 매도 2개
    if (indicatorType === 'RSI') {
      if (signalType === 'BUY') {
        return [
          { value: 'GOLDEN_CROSS' as ConditionType, label: '골든크로스 (상향 교차 순간)' },
          { value: 'ABOVE' as ConditionType, label: '상향 돌파' },
          { value: 'CROSS_UP' as ConditionType, label: '상향 유지 (골든크로스 이후)' },
        ];
      } else if (signalType === 'SELL') {
        return [
          { value: 'DEAD_CROSS' as ConditionType, label: '데드크로스 (하향 교차 순간)' },
          { value: 'BELOW' as ConditionType, label: '하향 돌파' },
        ];
      }
    }
    
    // CCI 특화: 매수 3개, 매도 2개
    if (indicatorType === 'CCI') {
      if (signalType === 'BUY') {
        return [
          { value: 'GOLDEN_CROSS' as ConditionType, label: '골든크로스 (상향 교차 순간)' },
          { value: 'ABOVE' as ConditionType, label: '상향 돌파' },
          { value: 'CROSS_UP' as ConditionType, label: '상향 유지 (골든크로스 이후)' },
        ];
      } else if (signalType === 'SELL') {
        return [
          { value: 'DEAD_CROSS' as ConditionType, label: '데드크로스 (하향 교차 순간)' },
          { value: 'BELOW' as ConditionType, label: '하향 돌파' },
        ];
      }
    }
    
    // TRIX 특화: 매수 3개, 매도 2개
    if (indicatorType === 'TRIX') {
      if (signalType === 'BUY') {
        return [
          { value: 'GOLDEN_CROSS' as ConditionType, label: '골든크로스 (상향 교차 순간)' },
          { value: 'ABOVE' as ConditionType, label: '상향 돌파' },
          { value: 'CROSS_UP' as ConditionType, label: '상향 유지 (골든크로스 이후)' },
        ];
      } else if (signalType === 'SELL') {
        return [
          { value: 'DEAD_CROSS' as ConditionType, label: '데드크로스 (하향 교차 순간)' },
          { value: 'BELOW' as ConditionType, label: '하향 돌파' },
        ];
      }
    }
    
    // DISPARITY(이격도) 특화: 매수 2개, 매도 1개
    if (indicatorType === 'DISPARITY') {
      if (signalType === 'BUY') {
        return [
          { value: 'ABOVE' as ConditionType, label: '상향 돌파' },
          { value: 'CROSS_UP' as ConditionType, label: '상향 유지' },
        ];
      } else if (signalType === 'SELL') {
        return [
          { value: 'BELOW' as ConditionType, label: '하향 돌파' },
        ];
      }
    }
    
    // NEW_PSYCHOLOGICAL(신심리도) 특화: 매수 1개, 매도 1개
    if (indicatorType === 'NEW_PSYCHOLOGICAL') {
      if (signalType === 'BUY') {
        return [
          { value: 'ABOVE' as ConditionType, label: '상향 돌파' },
        ];
      } else if (signalType === 'SELL') {
        return [
          { value: 'BELOW' as ConditionType, label: '하향 돌파' },
        ];
      }
    }
    
    // WILLIAMS_R 특화: 매수 1개, 매도 1개
    if (indicatorType === 'WILLIAMS_R') {
      if (signalType === 'BUY') {
        return [
          { value: 'ABOVE' as ConditionType, label: '상향 돌파' },
        ];
      } else if (signalType === 'SELL') {
        return [
          { value: 'BELOW' as ConditionType, label: '하향 돌파' },
        ];
      }
    }
    
    const allowedConditions = FINAL_INDICATOR_CONDITIONS[indicatorType] || [];
    
    // 매수/매도 탭에 따른 조건 필터링
    const tabSpecificConditions = signalType === 'BUY' ? BUY_CONDITIONS :
                                   signalType === 'SELL' ? SELL_CONDITIONS :
                                   null;
    
    return CONDITION_OPTIONS.filter(opt => {
      const isAllowedByIndicator = allowedConditions.includes(opt.value);
      const isAllowedByTab = !tabSpecificConditions || tabSpecificConditions.includes(opt.value);
      return isAllowedByIndicator && isAllowedByTab;
    });
  }, [FINAL_INDICATOR_CONDITIONS]);
  
  // 상태
  const [tabValue, setTabValue] = useState<SignalType>('BUY');
  const [rules, setRules] = useState<StrategyRuleDto[]>([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  
  // 알림 전략 상태
  const [alertStrategies, setAlertStrategies] = useState<AlertStrategyDto[]>([]);
  const [alertLoading, setAlertLoading] = useState(false);
  
  // 편집 다이얼로그
  const [editDialogOpen, setEditDialogOpen] = useState(false);
  const [editingRule, setEditingRule] = useState<StrategyRuleDto | null>(null);
  const [isNewRule, setIsNewRule] = useState(false);
  
  // 검색 다이얼로그
  const [searchDialogOpen, setSearchDialogOpen] = useState(false);
  const [searchingRuleId, setSearchingRuleId] = useState<number | null>(null);
  const [searchResults, setSearchResults] = useState<SymbolMatchResultDto[]>([]);
  const [searching, setSearching] = useState(false);
  const [searchRequest, setSearchRequest] = useState<StrategyRuleSearchRequest>({
    dataSource: 'REALTIME',
    timeframe: '1d',
    minMatchScore: 0,
    sortBy: 'MATCH_SCORE',
    sortDirection: 'DESC',
    limit: 50,
  });
  
  // 템플릿 다이얼로그
  const [templateDialogOpen, setTemplateDialogOpen] = useState(false);
  
  // 설명 보기 다이얼로그
  const [descriptionDialogOpen, setDescriptionDialogOpen] = useState(false);
  const [selectedDescription, setSelectedDescription] = useState<{ name: string; description: string }>({ name: '', description: '' });

  // 설명 보기 핸들러
  const handleViewDescription = (ruleName: string, description: string) => {
    setSelectedDescription({ name: ruleName, description: description || '설명이 없습니다.' });
    setDescriptionDialogOpen(true);
  };

  // Import 다이얼로그 상태
  const [importDialogOpen, setImportDialogOpen] = useState(false);
  const [importData, setImportData] = useState<StrategyRuleDto[] | null>(null);
  const [importLoading, setImportLoading] = useState(false);
  const [successMessage, setSuccessMessage] = useState<string | null>(null);
  
  // 전체삭제 확인 다이얼로그
  const [deleteAllDialogOpen, setDeleteAllDialogOpen] = useState(false);
  const [deletingAll, setDeletingAll] = useState(false);

  // 데이터 로드
  const loadRules = useCallback(async () => {
    setLoading(true);
    setError(null);
    try {
      // DSL 전략 목록 불러오기
      const dslStrategies = await getDSLStrategies(false);
      
      // DSL StrategyDSLDto → StrategyRuleDto 변환
      const convertedRules: StrategyRuleDto[] = dslStrategies.map(dsl => ({
        id: dsl.id,
        name: dsl.name,
        description: dsl.description,
        signalType: 'ALL' as SignalType, // DSL은 신호 타입 구분 없음
        trendType: undefined,
        enabled: dsl.enabled ?? true,
        strategyType: 'DSL' as any,
        createdByUi: 'FORM' as any, // 기존 화면에서 생성된 것으로 표시
        // 조건 그룹은 나중에 편집 시 파싱
        buyConditionGroups: [],
        sellConditionGroups: [],
        // 레거시 필드는 빈 값으로 설정
        enableMaBuy: false,
        enableMaSell: false,
        enableIchimokuBuy: false,
        enableIchimokuSell: false,
        enableBollingerBuy: false,
        enableBollingerSell: false,
        enableAuxBuy: false,
        enableAuxSell: false,
      }));
      
      // 탭에 따라 필터링 (DSL은 ALL이므로 모든 탭에서 보여짐)
      const filteredRules = convertedRules.filter(rule => 
        (tabValue as string) === 'ALL' || (rule.signalType as string) === 'ALL' || rule.signalType === tabValue
      );
      
      setRules(filteredRules);
      
      console.log(`✅ DSL 전략 ${dslStrategies.length}개 로드 완료 (탭: ${tabValue})`);
    } catch (err: any) {
      console.error('❌ 전략 목록 로드 실패:', err);
      setError(err.response?.data?.error || '전략 목록을 불러오는데 실패했습니다.');
    } finally {
      setLoading(false);
    }
  }, [tabValue]);

  // 알림 전략 목록 로드
  const loadAlertStrategies = useCallback(async () => {
    try {
      setAlertLoading(true);
      const alertList = await getAllAlertStrategies();
      setAlertStrategies(alertList);
      console.log('[Strategy Rule] 알림 전략 목록 로드:', alertList.length, '개');
    } catch (err) {
      console.error('[Strategy Rule] 알림 전략 목록 로드 실패:', err);
    } finally {
      setAlertLoading(false);
    }
  }, []);

  useEffect(() => {
    loadRules();
    loadAlertStrategies();
  }, [loadRules, loadAlertStrategies]);

  // 규칙 생성
  const handleCreateRule = () => {
    setEditingRule(createEmptyRule(tabValue));
    setIsNewRule(true);
    setEditDialogOpen(true);
  };

  // 규칙 수정
  const handleEditRule = async (id: number) => {
    try {
      // DSL 전략 불러오기
      const dslStrategy = await getDSLStrategy(id);
      
      // DSL JSON 파싱
      let rootNode: LogicNode | null = null;
      try {
        rootNode = JSON.parse(dslStrategy.dslJson);
      } catch (parseErr) {
        console.error('❌ DSL JSON 파싱 실패:', parseErr);
        throw new Error('DSL JSON 파싱에 실패했습니다.');
      }
      
      // LogicNode → ConditionGroup 변환
      const conditionGroups = logicNodeToConditionGroups(rootNode);
      
      // StrategyRuleDto 형식으로 변환
      const rule: StrategyRuleDto = {
        id: dslStrategy.id,
        name: dslStrategy.name,
        description: dslStrategy.description,
        signalType: 'ALL' as SignalType,
        trendType: undefined,
        enabled: dslStrategy.enabled ?? true,
        strategyType: 'DSL' as any,
        createdByUi: 'FORM' as any,
        // 변환된 조건 그룹
        buyConditionGroups: conditionGroups,
        sellConditionGroups: [],
        // 레거시 필드
        enableMaBuy: false,
        enableMaSell: false,
        enableIchimokuBuy: false,
        enableIchimokuSell: false,
        enableBollingerBuy: false,
        enableBollingerSell: false,
        enableAuxBuy: false,
        enableAuxSell: false,
      };
      
      console.log('📖 [DSL 불러오기] 변환된 조건 그룹:', conditionGroups);
      
      setEditingRule(rule);
      setIsNewRule(false);
      setEditDialogOpen(true);
    } catch (err: any) {
      console.error('❌ [DSL 불러오기] 전략 로드 실패:', err);
      setError(err.response?.data?.error || err.message || '전략을 불러오는데 실패했습니다.');
    }
  };

  // 규칙 삭제
  const handleDeleteRule = async (id: number) => {
    if (!window.confirm('이 규칙을 삭제하시겠습니까?')) return;
    
    try {
      await deleteStrategyRule(id);
      await loadRules();
    } catch (err: any) {
      setError(err.response?.data?.error || '규칙 삭제에 실패했습니다.');
    }
  };

  // 규칙 토글
  const handleToggleRule = async (id: number, enabled: boolean) => {
    try {
      await toggleStrategyRule(id, enabled);
      await loadRules();
    } catch (err: any) {
      setError(err.response?.data?.error || '규칙 상태 변경에 실패했습니다.');
    }
  };

  // 규칙 저장 (DSL 기반)
  const handleSaveRule = async () => {
    if (!editingRule) return;
    
    // 조건 그룹 검증
    const buyGroups = editingRule.buyConditionGroups || [];
    const sellGroups = editingRule.sellConditionGroups || [];
    
    const hasBuyConditions = buyGroups.some(g => g.conditions && g.conditions.length > 0);
    const hasSellConditions = sellGroups.some(g => g.conditions && g.conditions.length > 0);
    
    if (!hasBuyConditions && !hasSellConditions) {
      alert('매수 또는 매도 조건을 최소 1개 이상 추가해야 합니다.');
      return;
    }
    
    try {
      console.log('📝 [DSL 저장] 규칙 저장 시작:', {
        isNew: isNewRule,
        name: editingRule.name,
        buyGroups: buyGroups.length,
        sellGroups: sellGroups.length,
      });
      
      // description 길이 제한
      let description = editingRule.description || '';
      const MAX_DESCRIPTION_LENGTH = 60000;
      if (description.length > MAX_DESCRIPTION_LENGTH) {
        console.warn(`⚠️ description이 너무 깁니다 (${description.length}자). ${MAX_DESCRIPTION_LENGTH}자로 잘라냅니다.`);
        description = description.substring(0, MAX_DESCRIPTION_LENGTH) + '\n\n... (내용이 잘렸습니다)';
      }
      
      // 매수/매도 조건을 LogicNode로 변환
      const buyLogicNode = hasBuyConditions ? conditionGroupsToLogicNode(buyGroups) : null;
      const sellLogicNode = hasSellConditions ? conditionGroupsToLogicNode(sellGroups) : null;
      
      // DSL JSON 생성 (매수/매도를 OR로 결합)
      let rootNode: LogicNode;
      if (buyLogicNode && sellLogicNode) {
        // 매수와 매도 조건이 모두 있으면 OR로 결합
        rootNode = {
          id: generateNodeId(),
          type: 'OR',
          children: [buyLogicNode, sellLogicNode],
          description: '매수 OR 매도',
        };
      } else if (buyLogicNode) {
        rootNode = buyLogicNode;
      } else if (sellLogicNode) {
        rootNode = sellLogicNode;
      } else {
        throw new Error('조건이 없습니다');
      }
      
      const dslJson = JSON.stringify(rootNode, null, 2);
      
      console.log('🎨 [DSL 저장] 생성된 DSL JSON:', dslJson);
      
      // DSL API로 저장
      let savedStrategy;
      if (isNewRule) {
        savedStrategy = await createDSLStrategy({
          name: editingRule.name,
          description,
          dslJson,
          enabled: editingRule.enabled ?? true,
          useFsm: false,
        });
        console.log('✅ [DSL 저장] 새 전략 생성 완료:', savedStrategy);
      } else if (editingRule.id) {
        savedStrategy = await updateDSLStrategy(editingRule.id, {
          name: editingRule.name,
          description,
          dslJson,
          enabled: editingRule.enabled,
          useFsm: false,
        });
        console.log('✅ [DSL 저장] 전략 수정 완료:', savedStrategy);
      }
      
      setEditDialogOpen(false);
      setEditingRule(null);
      
      // 전략 목록 새로고침
      await loadRules();
      
      alert(`전략이 성공적으로 ${isNewRule ? '생성' : '수정'}되었습니다.`);
    } catch (err: any) {
      console.error('❌ [DSL 저장] 규칙 저장 실패:', err);
      setError(err.response?.data?.error || err.message || '규칙 저장에 실패했습니다.');
    }
  };

  // 종목 검색
  const handleSearchSymbols = async (ruleId: number) => {
    setSearchingRuleId(ruleId);
    setSearchResults([]);
    setSearchDialogOpen(true);
  };

  const executeSearch = async () => {
    if (!searchingRuleId) return;
    
    setSearching(true);
    try {
      const results = await searchSymbolsByRule(searchingRuleId, searchRequest);
      setSearchResults(results);
    } catch (err: any) {
      setError(err.response?.data?.error || '종목 검색에 실패했습니다.');
    } finally {
      setSearching(false);
    }
  };

  // 템플릿에서 생성
  const handleCreateFromTemplate = (template: StrategyRuleDto) => {
    setEditingRule({ ...template, id: undefined });
    setIsNewRule(true);
    setTemplateDialogOpen(false);
    setEditDialogOpen(true);
  };

  // 규칙 복제
  const handleDuplicateRule = async (id: number) => {
    try {
      const rule = await getStrategyRule(id);
      setEditingRule({
        ...rule,
        id: undefined,
        name: `${rule.name} (복사본)`,
      });
      setIsNewRule(true);
      setEditDialogOpen(true);
    } catch (err: any) {
      setError(err.response?.data?.error || '규칙 복제에 실패했습니다.');
    }
  };

  // 알림 전략 토글
  const handleToggleAlert = async (strategyRuleId: number) => {
    try {
      setAlertLoading(true);
      
      // 현재 알림 전략 목록에서 해당 전략이 있는지 확인
      const existingAlert = alertStrategies.find(a => a.strategyRuleId === strategyRuleId);
      
      if (existingAlert) {
        // 이미 추가되어 있으면 삭제
        console.log('[Strategy Rule] 알림 전략 삭제:', strategyRuleId);
        await deleteAlertStrategy(existingAlert.id!);
        await loadAlertStrategies();
        setSuccessMessage('알림 전략이 해제되었습니다.');
      } else {
        // 추가되어 있지 않으면 추가
        console.log('[Strategy Rule] 알림 전략 추가:', strategyRuleId);
        await addAlertStrategy(strategyRuleId);
        await loadAlertStrategies();
        setSuccessMessage('알림 전략이 추가되었습니다.');
      }
      
      // 성공 메시지 3초 후 자동 제거
      setTimeout(() => setSuccessMessage(null), 3000);
    } catch (err: any) {
      console.error('[Strategy Rule] 알림 전략 토글 실패:', err);
      const errorMessage = err.response?.data?.message || err.message || '알림 전략 토글에 실패했습니다.';
      setError(errorMessage);
    } finally {
      setAlertLoading(false);
    }
  };

  // ==================== Export/Import 기능 ====================
  
  /**
   * 전략 목록을 JSON 파일로 내보내기
   */
  const handleExport = useCallback(async () => {
    try {
      // 모든 전략 규칙 가져오기 (필터링 없이)
      const allBuyRules = await getStrategyRules('BUY');
      const allSellRules = await getStrategyRules('SELL');
      const allRules = [...allBuyRules, ...allSellRules];

      if (allRules.length === 0) {
        setError('내보낼 전략이 없습니다.');
        return;
      }

      // ID 및 시간 정보를 제외한 순수 전략 데이터만 추출
      const exportData = allRules.map(rule => {
        const { id, createdAt, updatedAt, ...ruleData } = rule;
        
        // 조건 그룹에서도 ID 제거
        const cleanConditionGroups = (groups?: ConditionGroupDto[]) => {
          return groups?.map(group => {
            const { id: groupId, strategyRuleId, ...groupData } = group;
            return {
              ...groupData,
              conditions: group.conditions.map(cond => {
                const { id: condId, conditionGroupId, ...condData } = cond;
                return condData;
              })
            };
          });
        };

        return {
          ...ruleData,
          buyConditionGroups: cleanConditionGroups(rule.buyConditionGroups),
          sellConditionGroups: cleanConditionGroups(rule.sellConditionGroups),
        };
      });

      // JSON 파일로 다운로드
      const dataStr = JSON.stringify(exportData, null, 2);
      const dataBlob = new Blob([dataStr], { type: 'application/json' });
      const url = URL.createObjectURL(dataBlob);
      
      const link = document.createElement('a');
      link.href = url;
      link.download = `strategy-rules-${new Date().toISOString().split('T')[0]}.json`;
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
      URL.revokeObjectURL(url);

      setSuccessMessage(`${allRules.length}개의 전략 규칙을 내보냈습니다.`);
      setTimeout(() => setSuccessMessage(null), 3000);
    } catch (err: any) {
      setError('전략 내보내기 실패: ' + (err.message || '알 수 없는 오류'));
    }
  }, []);

  /**
   * JSON 파일에서 전략 목록 불러오기
   */
  const handleImportFile = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
    const file = event.target.files?.[0];
    if (!file) return;

    const reader = new FileReader();
    reader.onload = (e) => {
      try {
        const content = e.target?.result as string;
        const data = JSON.parse(content) as StrategyRuleDto[];
        
        // 데이터 유효성 검증
        if (!Array.isArray(data)) {
          setError('올바른 전략 파일 형식이 아닙니다.');
          return;
        }

        if (data.length === 0) {
          setError('파일에 전략 데이터가 없습니다.');
          return;
        }

        // 필수 필드 검증
        const isValid = data.every(rule => 
          rule.name && rule.signalType && 
          (rule.signalType === 'BUY' || rule.signalType === 'SELL')
        );

        if (!isValid) {
          setError('일부 전략의 필수 필드가 누락되었습니다.');
          return;
        }

        setImportData(data);
        setImportDialogOpen(true);
      } catch (err: any) {
        setError('파일 읽기 실패: ' + (err.message || '올바른 JSON 형식이 아닙니다.'));
      }
    };

    reader.onerror = () => {
      setError('파일을 읽을 수 없습니다.');
    };

    reader.readAsText(file);
    
    // 동일한 파일을 다시 선택할 수 있도록 값 초기화
    event.target.value = '';
  }, []);

  /**
   * Import된 전략들을 DB에 저장
   */
  const handleConfirmImport = useCallback(async () => {
    if (!importData) return;

    setImportLoading(true);
    let successCount = 0;
    let failCount = 0;
    const errors: string[] = [];

    try {
      // 기존 전략 이름 목록 가져오기
      const allBuyRules = await getStrategyRules('BUY');
      const allSellRules = await getStrategyRules('SELL');
      const existingNames = new Set([...allBuyRules, ...allSellRules].map(r => r.name));

      for (const rule of importData) {
        try {
          // 이름 중복 체크 및 자동 변경
          let newName = rule.name;
          let counter = 1;
          while (existingNames.has(newName)) {
            newName = `${rule.name} (${counter})`;
            counter++;
          }

          // 새 이름을 목록에 추가 (다음 전략 체크용)
          existingNames.add(newName);

          // enabled가 undefined면 기본값 true로 설정
          const ruleToCreate = {
            ...rule,
            name: newName,
            enabled: rule.enabled ?? true,
          };
          
          await createStrategyRule(ruleToCreate);
          successCount++;

          if (newName !== rule.name) {
            console.log(`전략 이름 변경: "${rule.name}" → "${newName}"`);
          }
        } catch (err: any) {
          failCount++;
          const errorMsg = err.response?.data?.message || err.message || '알 수 없는 오류';
          errors.push(`"${rule.name}": ${errorMsg}`);
        }
      }

      // 결과 표시
      if (successCount > 0) {
        setSuccessMessage(`${successCount}개의 전략을 성공적으로 가져왔습니다.`);
        setTimeout(() => setSuccessMessage(null), 3000);
        loadRules(); // 목록 새로고침
      }

      if (failCount > 0) {
        setError(
          `${failCount}개의 전략을 가져오는데 실패했습니다:\n` + 
          errors.slice(0, 3).join('\n') +
          (errors.length > 3 ? `\n... 외 ${errors.length - 3}개` : '')
        );
      }

      setImportDialogOpen(false);
      setImportData(null);
    } catch (err: any) {
      setError('전략 가져오기 중 오류 발생: ' + (err.message || '알 수 없는 오류'));
    } finally {
      setImportLoading(false);
    }
  }, [importData, loadRules]);

  /**
   * 현재 탭의 모든 전략 삭제
   */
  const handleDeleteAll = useCallback(async () => {
    if (rules.length === 0) {
      setError('삭제할 전략이 없습니다.');
      return;
    }

    setDeletingAll(true);
    let successCount = 0;
    let failCount = 0;
    const errors: string[] = [];

    try {
      for (const rule of rules) {
        if (!rule.id) continue;
        
        try {
          await deleteStrategyRule(rule.id);
          successCount++;
        } catch (err: any) {
          failCount++;
          const errorMsg = err.response?.data?.message || err.message || '알 수 없는 오류';
          errors.push(`"${rule.name}": ${errorMsg}`);
        }
      }

      // 결과 표시
      if (successCount > 0) {
        setSuccessMessage(`${successCount}개의 전략을 성공적으로 삭제했습니다.`);
        setTimeout(() => setSuccessMessage(null), 3000);
        loadRules(); // 목록 새로고침
      }

      if (failCount > 0) {
        setError(
          `${failCount}개의 전략 삭제에 실패했습니다:\n` + 
          errors.slice(0, 3).join('\n') +
          (errors.length > 3 ? `\n... 외 ${errors.length - 3}개` : '')
        );
      }

      setDeleteAllDialogOpen(false);
    } catch (err: any) {
      setError('전략 삭제 중 오류 발생: ' + (err.message || '알 수 없는 오류'));
    } finally {
      setDeletingAll(false);
    }
  }, [rules, loadRules]);

  return (
    <Box className="fade-in" sx={{ p: 3 }}>
      {/* 헤더 - Sticky 적용 */}
      <Box 
        sx={{ 
          position: 'sticky',
          top: 0,
          zIndex: 10,
          bgcolor: 'background.default',
          pb: 2,
          mb: 1,
          borderBottom: '1px solid',
          borderColor: 'divider',
        }}
      >
        <Box sx={{ 
          display: 'flex', 
          justifyContent: 'space-between', 
          alignItems: { xs: 'flex-start', md: 'center' },
          flexDirection: { xs: 'column', md: 'row' },
          gap: 2,
        }}>
          <Typography variant="h4" sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
            {tabValue === 'BUY' ? <TrendingUp color="success" /> : <TrendingDown color="error" />}
            전략 규칙 관리
          </Typography>
          <Box sx={{ 
            display: 'flex', 
            gap: 1, 
            flexWrap: 'wrap',
            justifyContent: { xs: 'flex-start', md: 'flex-end' },
          }}>
            <Button
              variant="outlined"
              startIcon={<FileDownloadIcon />}
              onClick={handleExport}
              size="small"
            >
              Export
            </Button>
            <Button
              variant="outlined"
              component="label"
              startIcon={<FileUploadIcon />}
              size="small"
            >
              Import
              <input
                type="file"
                hidden
                accept=".json"
                onChange={handleImportFile}
              />
            </Button>
            <Button
              variant="outlined"
              color="error"
              startIcon={<DeleteSweepIcon />}
              onClick={() => setDeleteAllDialogOpen(true)}
              disabled={rules.length === 0}
              size="small"
            >
              전체삭제
            </Button>
            <Button
              variant="outlined"
              startIcon={<CopyIcon />}
              onClick={() => setTemplateDialogOpen(true)}
              size="small"
            >
              템플릿
            </Button>
            <Button
              variant="contained"
              startIcon={<AddIcon />}
              onClick={handleCreateRule}
              size="small"
            >
              새 규칙
            </Button>
          </Box>
        </Box>
      </Box>

      {/* 에러 메시지 */}
      {error && (
        <Alert severity="error" sx={{ mb: 2 }} onClose={() => setError(null)}>
          {error}
        </Alert>
      )}

      {/* 성공 메시지 */}
      {successMessage && (
        <Alert severity="success" sx={{ mb: 2 }} onClose={() => setSuccessMessage(null)}>
          {successMessage}
        </Alert>
      )}

      {/* 탭 */}
      <Paper sx={{ mb: 3 }}>
        <Tabs
          value={tabValue}
          onChange={(_, v) => setTabValue(v)}
          sx={{ borderBottom: 1, borderColor: 'divider' }}
        >
          <Tab
            value="BUY"
            label={
              <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
                <TrendingUp color="success" />
                매수 규칙
                <Chip size="small" label={rules.filter(r => r.signalType === 'BUY').length} />
              </Box>
            }
          />
          <Tab
            value="SELL"
            label={
              <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
                <TrendingDown color="error" />
                매도 규칙
                <Chip size="small" label={rules.filter(r => r.signalType === 'SELL').length} />
              </Box>
            }
          />
        </Tabs>

        {/* 규칙 목록 */}
        <TableContainer>
          <Table>
            <TableHead>
              <TableRow>
                <TableCell>상태</TableCell>
                <TableCell>규칙명</TableCell>
                <TableCell>설명</TableCell>
                <TableCell>조건 요약</TableCell>
                <TableCell>조건 수</TableCell>
                <TableCell>생성일</TableCell>
                <TableCell align="right">작업</TableCell>
              </TableRow>
            </TableHead>
            <TableBody>
              {loading ? (
                <TableRow>
                  <TableCell colSpan={7} align="center" sx={{ py: 4 }}>
                    <CircularProgress />
                  </TableCell>
                </TableRow>
              ) : rules.length === 0 ? (
                <TableRow>
                  <TableCell colSpan={7} align="center" sx={{ py: 4 }}>
                    <Typography color="text.secondary">
                      등록된 {tabValue === 'BUY' ? '매수' : '매도'} 규칙이 없습니다.
                    </Typography>
                    <Button
                      variant="outlined"
                      startIcon={<AddIcon />}
                      onClick={handleCreateRule}
                      sx={{ mt: 2 }}
                    >
                      새 규칙 추가
                    </Button>
                  </TableCell>
                </TableRow>
              ) : (
                rules.map((rule) => (
                  <TableRow key={rule.id} hover>
                    <TableCell>
                      <Switch
                        checked={rule.enabled}
                        onChange={(e) => rule.id && handleToggleRule(rule.id, e.target.checked)}
                        color={rule.signalType === 'BUY' ? 'success' : 'error'}
                      />
                    </TableCell>
                    <TableCell>
                      <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
                        <Typography fontWeight="medium">{rule.name}</Typography>
                        <Chip
                          label={rule.strategyType === 'DSL' ? '드래그' : '폼'}
                          size="small"
                          color={rule.strategyType === 'DSL' ? 'primary' : 'default'}
                          variant="outlined"
                          sx={{ height: 20, fontSize: '11px' }}
                        />
                      </Box>
                    </TableCell>
                    <TableCell>
                      {rule.description ? (
                        <Button
                          size="small"
                          variant="outlined"
                          onClick={() => handleViewDescription(rule.name, rule.description || '')}
                          sx={{ fontSize: '12px', py: 0.5 }}
                        >
                          설명보기
                        </Button>
                      ) : (
                        <Typography variant="body2" color="text.secondary">-</Typography>
                      )}
                    </TableCell>
                    <TableCell>
                      <Typography variant="body2" noWrap sx={{ maxWidth: 250 }}>
                        {rule.conditionSummary || '-'}
                      </Typography>
                    </TableCell>
                    <TableCell>
                      <Chip size="small" label={rule.totalConditionCount || 0} />
                    </TableCell>
                    <TableCell>
                      {rule.createdAt ? new Date(rule.createdAt).toLocaleDateString() : '-'}
                    </TableCell>
                    <TableCell align="right">
                      <Tooltip title="종목 검색">
                        <IconButton
                          size="small"
                          color="primary"
                          onClick={() => rule.id && handleSearchSymbols(rule.id)}
                        >
                          <SearchIcon />
                        </IconButton>
                      </Tooltip>
                      <Tooltip title={alertStrategies.some(a => a.strategyRuleId === rule.id) ? "알림 해제" : "알림 추가"}>
                        <IconButton
                          size="small"
                          color={alertStrategies.some(a => a.strategyRuleId === rule.id) ? "warning" : "default"}
                          onClick={() => rule.id && handleToggleAlert(rule.id)}
                          disabled={alertLoading}
                        >
                          {alertStrategies.some(a => a.strategyRuleId === rule.id) ? (
                            <NotificationsActive />
                          ) : (
                            <NotificationsOff />
                          )}
                        </IconButton>
                      </Tooltip>
                      <Tooltip title="복제">
                        <IconButton
                          size="small"
                          onClick={() => rule.id && handleDuplicateRule(rule.id)}
                        >
                          <CopyIcon />
                        </IconButton>
                      </Tooltip>
                      <Tooltip title="수정">
                        <IconButton
                          size="small"
                          onClick={() => rule.id && handleEditRule(rule.id)}
                        >
                          <EditIcon />
                        </IconButton>
                      </Tooltip>
                      <Tooltip title="삭제">
                        <IconButton
                          size="small"
                          color="error"
                          onClick={() => rule.id && handleDeleteRule(rule.id)}
                        >
                          <DeleteIcon />
                        </IconButton>
                      </Tooltip>
                    </TableCell>
                  </TableRow>
                ))
              )}
            </TableBody>
          </Table>
        </TableContainer>
      </Paper>

      {/* 규칙 편집 다이얼로그 */}
      <RuleEditDialog
        open={editDialogOpen}
        rule={editingRule}
        isNew={isNewRule}
        signalType={tabValue}
        onClose={() => setEditDialogOpen(false)}
        onChange={setEditingRule}
        onSave={handleSaveRule}
        indicatorOptions={FINAL_INDICATOR_OPTIONS}
        getConditionOptions={getConditionOptionsForIndicatorDynamic}
        indicatorConfig={FINAL_INDICATOR_CONFIG}
      />


      {/* 설명 보기 다이얼로그 */}
      <Dialog
        open={descriptionDialogOpen}
        onClose={() => setDescriptionDialogOpen(false)}
        maxWidth="md"
        fullWidth
        sx={{ zIndex: 9999 }}
      >
        <DialogTitle sx={{ 
          display: 'flex', 
          justifyContent: 'space-between', 
          alignItems: 'center',
          bgcolor: '#1976d2',
          color: '#fff',
          py: 1.2,
        }}>
          <Box>
            <Typography variant="h6" sx={{ fontWeight: 600, fontSize: '16px', color: '#fff' }}>
              전략 설명
            </Typography>
            <Typography variant="caption" sx={{ color: 'rgba(255,255,255,0.8)' }}>
              {selectedDescription.name}
            </Typography>
          </Box>
          <IconButton onClick={() => setDescriptionDialogOpen(false)} size="small" sx={{ color: '#fff' }}>
            <Close />
          </IconButton>
        </DialogTitle>
        <DialogContent dividers>
          <Box sx={{ 
            bgcolor: isDark ? 'rgba(30, 35, 50, 0.9)' : 'grey.100', 
            color: isDark ? '#e0e0e0' : 'text.primary',
            border: isDark ? '1px solid rgba(80, 100, 160, 0.3)' : 'none',
            p: 2, 
            borderRadius: 1, 
            fontFamily: 'monospace',
            fontSize: '13px',
            whiteSpace: 'pre-wrap',
            lineHeight: 1.6,
            minHeight: 200,
            maxHeight: 500,
            overflow: 'auto'
          }}>
            {selectedDescription.description}
          </Box>
        </DialogContent>
      </Dialog>

      {/* Import 확인 다이얼로그 */}
      <Dialog 
        open={importDialogOpen} 
        onClose={() => !importLoading && setImportDialogOpen(false)} 
        maxWidth="md" 
        fullWidth
      >
        <DialogTitle>
          <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
            <FileUploadIcon color="primary" />
            전략 규칙 가져오기 확인
          </Box>
        </DialogTitle>
        <DialogContent>
          <Alert severity="info" sx={{ mb: 2 }}>
            다음 {importData?.length || 0}개의 전략 규칙을 가져옵니다.
          </Alert>
          
          {importData && (
            <TableContainer component={Paper} variant="outlined" sx={{ maxHeight: 400 }}>
              <Table size="small">
                <TableHead>
                  <TableRow>
                    <TableCell><strong>전략명</strong></TableCell>
                    <TableCell align="center"><strong>신호</strong></TableCell>
                    <TableCell align="center"><strong>조건 수</strong></TableCell>
                    <TableCell align="center"><strong>활성화</strong></TableCell>
                  </TableRow>
                </TableHead>
                <TableBody>
                  {importData.map((rule, index) => (
                    <TableRow key={index}>
                      <TableCell>{rule.name}</TableCell>
                      <TableCell align="center">
                        <Chip
                          label={rule.signalType === 'BUY' ? '매수' : '매도'}
                          color={rule.signalType === 'BUY' ? 'success' : 'error'}
                          size="small"
                        />
                      </TableCell>
                      <TableCell align="center">
                        {rule.totalConditionCount || 
                         (rule.buyConditionGroups?.reduce((sum, g) => sum + g.conditions.length, 0) || 0) +
                         (rule.sellConditionGroups?.reduce((sum, g) => sum + g.conditions.length, 0) || 0)}
                      </TableCell>
                      <TableCell align="center">
                        {rule.enabled !== false ? '✓' : '✗'}
                      </TableCell>
                    </TableRow>
                  ))}
                </TableBody>
              </Table>
            </TableContainer>
          )}

          <Alert severity="warning" sx={{ mt: 2 }}>
            <strong>주의:</strong> 가져온 전략은 새로운 전략으로 추가됩니다. 기존 전략은 삭제되지 않습니다.
          </Alert>
        </DialogContent>
        <DialogActions>
          <Button 
            onClick={() => setImportDialogOpen(false)} 
            disabled={importLoading}
          >
            취소
          </Button>
          <Button
            variant="contained"
            onClick={handleConfirmImport}
            disabled={importLoading}
            startIcon={importLoading ? <CircularProgress size={16} /> : <FileUploadIcon />}
          >
            {importLoading ? '가져오는 중...' : `${importData?.length || 0}개 전략 가져오기`}
          </Button>
        </DialogActions>
      </Dialog>

      {/* 전체삭제 확인 다이얼로그 */}
      <Dialog 
        open={deleteAllDialogOpen} 
        onClose={() => !deletingAll && setDeleteAllDialogOpen(false)}
        maxWidth="sm"
        fullWidth
      >
        <DialogTitle>
          <Box sx={{ display: 'flex', alignItems: 'center', gap: 1, color: 'error.main' }}>
            <DeleteSweepIcon />
            전체 전략 규칙 삭제
          </Box>
        </DialogTitle>
        <DialogContent>
          <Alert severity="warning" sx={{ mb: 2 }}>
            <strong>경고:</strong> 이 작업은 되돌릴 수 없습니다!
          </Alert>
          <Typography variant="body1" gutterBottom>
            현재 <strong>{tabValue === 'BUY' ? '매수' : '매도'}</strong> 탭의 모든 전략 규칙 <strong>{rules.length}개</strong>를 삭제하시겠습니까?
          </Typography>
          <Box sx={{ mt: 2, p: 2, bgcolor: 'grey.100', borderRadius: 1 }}>
            <Typography variant="body2" color="text.secondary">
              삭제될 전략:
            </Typography>
            <Box sx={{ maxHeight: 200, overflow: 'auto', mt: 1 }}>
              {rules.map((rule, index) => (
                <Typography key={rule.id || index} variant="body2" sx={{ py: 0.5 }}>
                  • {rule.name}
                </Typography>
              ))}
            </Box>
          </Box>
        </DialogContent>
        <DialogActions>
          <Button 
            onClick={() => setDeleteAllDialogOpen(false)} 
            disabled={deletingAll}
          >
            취소
          </Button>
          <Button
            variant="contained"
            color="error"
            onClick={handleDeleteAll}
            disabled={deletingAll}
            startIcon={deletingAll ? <CircularProgress size={16} /> : <DeleteSweepIcon />}
          >
            {deletingAll ? '삭제 중...' : `${rules.length}개 전략 모두 삭제`}
          </Button>
        </DialogActions>
      </Dialog>
    </Box>
  );
};

// ==================== 하위 컴포넌트 ====================

/**
 * 규칙 편집 다이얼로그
 */
interface RuleEditDialogProps {
  open: boolean;
  rule: StrategyRuleDto | null;
  isNew: boolean;
  signalType: SignalType; // 매수/매도 탭 구분
  onClose: () => void;
  onChange: (rule: StrategyRuleDto) => void;
  onSave: () => void;
  // 동적 데이터 props
  indicatorOptions: { value: IndicatorType; label: string }[];
  getConditionOptions: (indicatorType: IndicatorType, signalType?: SignalType) => { value: ConditionType; label: string }[];
  indicatorConfig: { [key: string]: { min: number; max: number; step: number; defaultValue: number; required: boolean; helperText?: string } };
}

const RuleEditDialog: React.FC<RuleEditDialogProps> = ({
  open, rule, isNew, signalType, onClose, onChange, onSave,
  indicatorOptions, getConditionOptions, indicatorConfig
}) => {
  const [validating, setValidating] = useState(false);
  const [validationResult, setValidationResult] = useState<StrategyValidationResultDto | null>(null);
  const [activeTab, setActiveTab] = useState(0); // 0: 매수 조건, 1: 매도 조건
  
  // 기존 하드코딩 필드를 배열로 마이그레이션 (최초 로드 시)
  React.useEffect(() => {
    if (rule && (!rule.maConditionsBuy || !rule.maConditionsSell)) {
      const migratedRule = migrateMaConditionsToArray(rule);
      onChange(migratedRule);
    }
  }, [rule]);
  
  if (!rule) return null;

  // 탭 변경 (조건 설정 탭 전환, signalType은 변경하지 않음)
  const handleTabChange = (_event: React.SyntheticEvent, newValue: number) => {
    setActiveTab(newValue);
    // signalType은 유지 - 탭은 매수/매도 조건 설정용이며 신호 타입을 변경하지 않음
  };

  // 조건 변경 시 검증 결과 초기화
  const handleConditionChange = (updatedRule: StrategyRuleDto) => {
    onChange(updatedRule);
    // 조건 변경 시 검증 결과 초기화
    setValidationResult(null);
  };

  // 현재 탭의 조건 그룹 가져오기
  const getCurrentConditionGroups = () => {
    return isBuyTab ? (rule.buyConditionGroups || []) : (rule.sellConditionGroups || []);
  };

  const handleGroupChange = (index: number, group: ConditionGroupDto) => {
    const currentGroups = getCurrentConditionGroups();
    const newGroups = [...currentGroups];
    newGroups[index] = group;
    
    const updatedRule = isBuyTab 
      ? { ...rule, buyConditionGroups: newGroups }
      : { ...rule, sellConditionGroups: newGroups };
    handleConditionChange(updatedRule);
  };

  const addGroup = (operator: LogicalOperator = 'OR') => {
    const currentGroups = getCurrentConditionGroups();
    const groupType = isBuyTab ? 'BUY' : 'SELL';
    
    // 이전 그룹의 논리 연산자 설정
    if (currentGroups.length > 0) {
      const updatedGroups = [...currentGroups];
      updatedGroups[updatedGroups.length - 1] = {
        ...updatedGroups[updatedGroups.length - 1],
        interGroupOperator: operator,
      };
      updatedGroups.push(createEmptyGroup(currentGroups.length, groupType));
      
      const updatedRule = isBuyTab
        ? { ...rule, buyConditionGroups: updatedGroups }
        : { ...rule, sellConditionGroups: updatedGroups };
      handleConditionChange(updatedRule);
    } else {
      const newGroups = [createEmptyGroup(0, groupType)];
      const updatedRule = isBuyTab
        ? { ...rule, buyConditionGroups: newGroups }
        : { ...rule, sellConditionGroups: newGroups };
      handleConditionChange(updatedRule);
    }
  };

  const removeGroup = (index: number) => {
    const currentGroups = getCurrentConditionGroups();
    if (currentGroups.length <= 1) return;
    const newGroups = currentGroups.filter((_, i) => i !== index);
    
    // 마지막 그룹의 논리 연산자 제거
    if (newGroups.length > 0) {
      newGroups[newGroups.length - 1] = {
        ...newGroups[newGroups.length - 1],
        interGroupOperator: undefined,
      };
    }
    
    const updatedRule = isBuyTab
      ? { ...rule, buyConditionGroups: newGroups }
      : { ...rule, sellConditionGroups: newGroups };
    handleConditionChange(updatedRule);
  };
  
  const updateGroupOperator = (index: number, operator: LogicalOperator) => {
    const currentGroups = getCurrentConditionGroups();
    const newGroups = [...currentGroups];
    newGroups[index] = {
      ...newGroups[index],
      interGroupOperator: operator,
    };
    
    const updatedRule = isBuyTab
      ? { ...rule, buyConditionGroups: newGroups }
      : { ...rule, sellConditionGroups: newGroups };
    handleConditionChange(updatedRule);
  };

  // MA 조건 관리 함수들
  const getCurrentMaConditions = (): MaConditionDto[] => {
    return isBuyTab ? (rule.maConditionsBuy || []) : (rule.maConditionsSell || []);
  };

  const handleMaConditionChange = (index: number, condition: MaConditionDto) => {
    const currentConditions = getCurrentMaConditions();
    const newConditions = [...currentConditions];
    newConditions[index] = condition;
    
    const updatedRule = isBuyTab
      ? { ...rule, maConditionsBuy: newConditions }
      : { ...rule, maConditionsSell: newConditions };
    handleConditionChange(updatedRule);
  };

  const addMaCondition = (operator: LogicalOperator = 'OR') => {
    const currentConditions = getCurrentMaConditions();
    const newCondition = createEmptyMaCondition(currentConditions.length, isBuyTab ? 'BUY' : 'SELL');
    
    // 이전 조건의 논리 연산자 설정
    if (currentConditions.length > 0) {
      const updatedConditions = [...currentConditions];
      updatedConditions[updatedConditions.length - 1] = {
        ...updatedConditions[updatedConditions.length - 1],
        logicalOperator: operator,
      };
      updatedConditions.push(newCondition);
      
      const updatedRule = isBuyTab
        ? { ...rule, maConditionsBuy: updatedConditions }
        : { ...rule, maConditionsSell: updatedConditions };
      handleConditionChange(updatedRule);
    } else {
      const updatedRule = isBuyTab
        ? { ...rule, maConditionsBuy: [newCondition] }
        : { ...rule, maConditionsSell: [newCondition] };
      handleConditionChange(updatedRule);
    }
  };

  const removeMaCondition = (index: number) => {
    const currentConditions = getCurrentMaConditions();
    if (currentConditions.length <= 1) return;
    
    const newConditions = currentConditions.filter((_, i) => i !== index);
    
    // 마지막 조건의 논리 연산자 제거
    if (newConditions.length > 0) {
      newConditions[newConditions.length - 1] = {
        ...newConditions[newConditions.length - 1],
        logicalOperator: undefined,
      };
    }
    
    const updatedRule = isBuyTab
      ? { ...rule, maConditionsBuy: newConditions }
      : { ...rule, maConditionsSell: newConditions };
    handleConditionChange(updatedRule);
  };

  const updateMaConditionOperator = (index: number, operator: LogicalOperator) => {
    const currentConditions = getCurrentMaConditions();
    const newConditions = [...currentConditions];
    newConditions[index] = {
      ...newConditions[index],
      logicalOperator: operator,
    };
    
    const updatedRule = isBuyTab
      ? { ...rule, maConditionsBuy: newConditions }
      : { ...rule, maConditionsSell: newConditions };
    handleConditionChange(updatedRule);
  };
  
  // 전체 조건 설명 생성 (코드 로직 포함) - DB에 저장된 값만 사용
  const generateFullConditionDescription = (): string => {
    // 매수 조건
    const buyConditions: string[] = [];
    const buyLogic: string[] = [];
    
    // 새로운 배열 구조 사용
    if (rule.enableMaBuy && rule.maConditionsBuy && rule.maConditionsBuy.length > 0) {
      const maConditions = rule.maConditionsBuy;
      const maDescriptions: string[] = [];
      const maLogics: string[] = [];
      
      maConditions.forEach((condition, index) => {
        const crossType = condition.maCrossType === 'GOLDEN_CROSS' ? '상향돌파' : '하향돌파';
        const maDesc = `MA(${condition.maShortPeriod}) ${crossType} MA(${condition.maLongPeriod})`;
        
        // ✅ 실제 백엔드 로직 반영 (v2.0.1)
        let maLogic = '';
        if (condition.maCrossType === 'GOLDEN_CROSS') {
          // 골든크로스: 4단계 필터링 로직
          maLogic = `[고급 골든크로스]\n` +
                    `    1️⃣ 교차: MA${condition.maShortPeriod}(t-1) < MA${condition.maLongPeriod}(t-1) AND MA${condition.maShortPeriod}(t) >= MA${condition.maLongPeriod}(t)\n` +
                    `    2️⃣ 기울기: MA${condition.maShortPeriod}(t) > MA${condition.maShortPeriod}(t-1) AND MA${condition.maLongPeriod}(t) >= MA${condition.maLongPeriod}(t-1)\n` +
                    `    3️⃣ 가격동조: Close(t) >= MA${condition.maShortPeriod}(t) AND Close(t) >= MA${condition.maLongPeriod}(t)\n` +
                    `    4️⃣ 거래량: Volume(t) >= Avg20(Volume) × 1.2`;
        } else if (condition.maCrossType === 'DEAD_CROSS') {
          // 데드크로스 → TREND_BREAK_SELL 로직으로 대체 (v2.0.1)
          maLogic = `[추세붕괴 매도 (TREND_BREAK_SELL)] - 다음 중 하나라도 충족 시 매도\n` +
                    `    1️⃣ 추세붕괴: MA${condition.maShortPeriod}(t) < MA${condition.maShortPeriod}(t-1)\n` +
                    `    2️⃣ 가격이탈: Close(t) < MA${condition.maShortPeriod}(t)\n` +
                    `    3️⃣ 약세확인: Close(t) < Open(t) AND Volume(t) < Volume(t-1)`;
        } else {
          // 기타: 단순 비교
          const crossSymbol = condition.maCrossType === 'GOLDEN_CROSS' ? '>' : '<';
          maLogic = `MA${condition.maShortPeriod} ${crossSymbol} MA${condition.maLongPeriod}`;
        }
        
        maDescriptions.push(`조건${index + 1}: ${maDesc}`);
        maLogics.push(maLogic);
      });
      
      // 조건들을 논리 연산자로 연결
      let combinedLogic = maLogics[0];
      for (let i = 0; i < maConditions.length - 1; i++) {
        const operator = maConditions[i].logicalOperator || 'OR';
        combinedLogic = `${combinedLogic} ${operator} ${maLogics[i + 1]}`;
      }
      
      buyConditions.push(`[이동평균선] ${maDescriptions.join(', ')}`);
      buyLogic.push(maConditions.length > 1 ? `(${combinedLogic})` : combinedLogic);
    }
    
    if (rule.enableIchimokuBuy && rule.ichimokuTenkanBuy && rule.ichimokuKijunBuy) {
      const crossType = rule.ichimokuCrossTypeBuy === 'CROSS_UP' ? '상향돌파' : '하향돌파';
      const crossSymbol = rule.ichimokuCrossTypeBuy === 'CROSS_UP' ? '>' : '<';
      const ichDesc = `전환선(${rule.ichimokuTenkanBuy}) ${crossType} 기준선(${rule.ichimokuKijunBuy})`;
      buyConditions.push(`[일목균형표] ${ichDesc}`);
      buyLogic.push(`Tenkan(${rule.ichimokuTenkanBuy}) ${crossSymbol} Kijun(${rule.ichimokuKijunBuy})`);
    }
    
    // 볼린저밴드 조건
    if (rule.enableBollingerBuy && rule.bollingerConditionsBuy && rule.bollingerConditionsBuy.length > 0) {
      const bbConditions = rule.bollingerConditionsBuy;
      const bbDescriptions: string[] = [];
      const bbLogics: string[] = [];
      
      bbConditions.forEach((condition, index) => {
        const bandName = condition.bandType === 'UPPER' ? '상단밴드' : condition.bandType === 'MIDDLE' ? '중간밴드' : '하단밴드';
        const crossType = condition.crossType === 'CROSS_UP' ? '상향돌파' : '하향돌파';
        const bbDesc = `가격이 ${bandName}(${condition.period}일, ${condition.stdDev}σ)를 ${crossType}`;
        const crossSymbol = condition.crossType === 'CROSS_UP' ? '>' : '<';
        const bbLogic = `Close ${crossSymbol} BB_${condition.bandType}(${condition.period}, ${condition.stdDev})`;
        
        bbDescriptions.push(`조건${index + 1}: ${bbDesc}`);
        bbLogics.push(bbLogic);
      });
      
      // 조건들을 논리 연산자로 연결
      let combinedLogic = bbLogics[0];
      for (let i = 0; i < bbConditions.length - 1; i++) {
        const operator = bbConditions[i].logicalOperator || 'OR';
        combinedLogic = `${combinedLogic} ${operator} ${bbLogics[i + 1]}`;
      }
      
      buyConditions.push(`[볼린저밴드] ${bbDescriptions.join(', ')}`);
      buyLogic.push(bbConditions.length > 1 ? `(${combinedLogic})` : combinedLogic);
    }
    
    // 매도 조건
    const sellConditions: string[] = [];
    const sellLogic: string[] = [];
    
    // 새로운 배열 구조 사용
    if (rule.enableMaSell && rule.maConditionsSell && rule.maConditionsSell.length > 0) {
      const maConditions = rule.maConditionsSell;
      const maDescriptions: string[] = [];
      const maLogics: string[] = [];
      
      maConditions.forEach((condition, index) => {
        const crossType = condition.maCrossType === 'GOLDEN_CROSS' ? '상향돌파' : '하향돌파';
        const maDesc = `MA(${condition.maShortPeriod}) ${crossType} MA(${condition.maLongPeriod})`;
        
        // ✅ 실제 백엔드 로직 반영 (v2.0.1)
        let maLogic = '';
        if (condition.maCrossType === 'GOLDEN_CROSS') {
          // 골든크로스: 4단계 필터링 로직
          maLogic = `[고급 골든크로스]\n` +
                    `    1️⃣ 교차: MA${condition.maShortPeriod}(t-1) < MA${condition.maLongPeriod}(t-1) AND MA${condition.maShortPeriod}(t) >= MA${condition.maLongPeriod}(t)\n` +
                    `    2️⃣ 기울기: MA${condition.maShortPeriod}(t) > MA${condition.maShortPeriod}(t-1) AND MA${condition.maLongPeriod}(t) >= MA${condition.maLongPeriod}(t-1)\n` +
                    `    3️⃣ 가격동조: Close(t) >= MA${condition.maShortPeriod}(t) AND Close(t) >= MA${condition.maLongPeriod}(t)\n` +
                    `    4️⃣ 거래량: Volume(t) >= Avg20(Volume) × 1.2`;
        } else if (condition.maCrossType === 'DEAD_CROSS') {
          // 데드크로스 → TREND_BREAK_SELL 로직으로 대체 (v2.0.1)
          maLogic = `[추세붕괴 매도 (TREND_BREAK_SELL)] - 다음 중 하나라도 충족 시 매도\n` +
                    `    1️⃣ 추세붕괴: MA${condition.maShortPeriod}(t) < MA${condition.maShortPeriod}(t-1)\n` +
                    `    2️⃣ 가격이탈: Close(t) < MA${condition.maShortPeriod}(t)\n` +
                    `    3️⃣ 약세확인: Close(t) < Open(t) AND Volume(t) < Volume(t-1)`;
        } else {
          // 기타: 단순 비교
          const crossSymbol = condition.maCrossType === 'GOLDEN_CROSS' ? '>' : '<';
          maLogic = `MA${condition.maShortPeriod} ${crossSymbol} MA${condition.maLongPeriod}`;
        }
        
        maDescriptions.push(`조건${index + 1}: ${maDesc}`);
        maLogics.push(maLogic);
      });
      
      // 조건들을 논리 연산자로 연결
      let combinedLogic = maLogics[0];
      for (let i = 0; i < maConditions.length - 1; i++) {
        const operator = maConditions[i].logicalOperator || 'OR';
        combinedLogic = `${combinedLogic} ${operator} ${maLogics[i + 1]}`;
      }
      
      sellConditions.push(`[이동평균선] ${maDescriptions.join(', ')}`);
      sellLogic.push(maConditions.length > 1 ? `(${combinedLogic})` : combinedLogic);
    }
    
    if (rule.enableIchimokuSell && rule.ichimokuTenkanSell && rule.ichimokuKijunSell) {
      const crossType = rule.ichimokuCrossTypeSell === 'CROSS_UP' ? '상향돌파' : '하향돌파';
      const crossSymbol = rule.ichimokuCrossTypeSell === 'CROSS_UP' ? '>' : '<';
      const ichDesc = `전환선(${rule.ichimokuTenkanSell}) ${crossType} 기준선(${rule.ichimokuKijunSell})`;
      sellConditions.push(`[일목균형표] ${ichDesc}`);
      sellLogic.push(`Tenkan(${rule.ichimokuTenkanSell}) ${crossSymbol} Kijun(${rule.ichimokuKijunSell})`);
    }
    
    // 볼린저밴드 조건
    if (rule.enableBollingerSell && rule.bollingerConditionsSell && rule.bollingerConditionsSell.length > 0) {
      const bbConditions = rule.bollingerConditionsSell;
      const bbDescriptions: string[] = [];
      const bbLogics: string[] = [];
      
      bbConditions.forEach((condition, index) => {
        const bandName = condition.bandType === 'UPPER' ? '상단밴드' : condition.bandType === 'MIDDLE' ? '중간밴드' : '하단밴드';
        const crossType = condition.crossType === 'CROSS_UP' ? '상향돌파' : '하향돌파';
        const bbDesc = `가격이 ${bandName}(${condition.period}일, ${condition.stdDev}σ)를 ${crossType}`;
        const crossSymbol = condition.crossType === 'CROSS_UP' ? '>' : '<';
        const bbLogic = `Close ${crossSymbol} BB_${condition.bandType}(${condition.period}, ${condition.stdDev})`;
        
        bbDescriptions.push(`조건${index + 1}: ${bbDesc}`);
        bbLogics.push(bbLogic);
      });
      
      // 조건들을 논리 연산자로 연결
      let combinedLogic = bbLogics[0];
      for (let i = 0; i < bbConditions.length - 1; i++) {
        const operator = bbConditions[i].logicalOperator || 'OR';
        combinedLogic = `${combinedLogic} ${operator} ${bbLogics[i + 1]}`;
      }
      
      sellConditions.push(`[볼린저밴드] ${bbDescriptions.join(', ')}`);
      sellLogic.push(bbConditions.length > 1 ? `(${combinedLogic})` : combinedLogic);
    }
    
    // 매수 보조지표 조건 - enableAuxBuy가 true일 때만 표시 (백엔드와 동일한 로직)
    const buyAuxConditions: string[] = [];
    const buyAuxLogic: string[] = [];
    
    // enableAuxBuy가 명시적으로 true일 때만 보조지표 조건 표시
    if (rule.enableAuxBuy) {
      (rule.buyConditionGroups || []).forEach((group, gIdx) => {
        const enabledConditions = (group.conditions || []).filter(c => c.enabled);
        if (enabledConditions.length > 0) {
          const groupConditions = enabledConditions.map((cond) => {
            // generateConditionDescription 사용하여 DB 값 기반 설명 생성
            return generateConditionDescription(cond);
          });
          
          // 각 조건의 logicalOperator를 사용하여 연결 (백엔드와 동일한 로직)
          let combinedConditions = groupConditions[0];
          for (let i = 1; i < groupConditions.length; i++) {
            // 이전 조건의 logicalOperator 사용
            const prevCondition = enabledConditions[i - 1];
            const operator = prevCondition.logicalOperator || 'OR';
            combinedConditions = `${combinedConditions} ${operator} ${groupConditions[i]}`;
          }
          
          const groupName = group.name || `그룹${gIdx + 1}`;
          buyAuxConditions.push(`[${groupName}] ${combinedConditions}`);
          buyAuxLogic.push(groupConditions.length > 1 ? `(${combinedConditions})` : combinedConditions);
        }
      });
    }
    
    // 매도 보조지표 조건 - enableAuxSell이 true일 때만 표시 (백엔드와 동일한 로직)
    const sellAuxConditions: string[] = [];
    const sellAuxLogic: string[] = [];
    
    // enableAuxSell이 명시적으로 true일 때만 보조지표 조건 표시
    if (rule.enableAuxSell) {
      (rule.sellConditionGroups || []).forEach((group, gIdx) => {
        const enabledConditions = (group.conditions || []).filter(c => c.enabled);
        if (enabledConditions.length > 0) {
          const groupConditions = enabledConditions.map((cond) => {
            // generateConditionDescription 사용하여 DB 값 기반 설명 생성
            return generateConditionDescription(cond);
          });
          
          // 각 조건의 logicalOperator를 사용하여 연결 (백엔드와 동일한 로직)
          let combinedConditions = groupConditions[0];
          for (let i = 1; i < groupConditions.length; i++) {
            // 이전 조건의 logicalOperator 사용
            const prevCondition = enabledConditions[i - 1];
            const operator = prevCondition.logicalOperator || 'OR';
            combinedConditions = `${combinedConditions} ${operator} ${groupConditions[i]}`;
          }
          
          const groupName = group.name || `그룹${gIdx + 1}`;
          sellAuxConditions.push(`[${groupName}] ${combinedConditions}`);
          sellAuxLogic.push(groupConditions.length > 1 ? `(${combinedConditions})` : combinedConditions);
        }
      });
    }
    
    // 전체 설명 조합
    let fullDesc = '━━━ 전략 조건 요약 ━━━\n\n';
    
    if (buyConditions.length > 0 || buyAuxConditions.length > 0) {
      fullDesc += '📈 매수 조건:\n';
      if (buyConditions.length > 0) {
        fullDesc += buyConditions.map(c => `  • ${c}`).join('\n') + '\n';
      }
      if (buyAuxConditions.length > 0) {
        fullDesc += '  [보조지표]\n';
        fullDesc += buyAuxConditions.map(c => `    • ${c}`).join('\n') + '\n';
      }
      fullDesc += '\n';
    }
    
    if (sellConditions.length > 0 || sellAuxConditions.length > 0) {
      fullDesc += '📉 매도 조건:\n';
      if (sellConditions.length > 0) {
        fullDesc += sellConditions.map(c => `  • ${c}`).join('\n') + '\n';
      }
      if (sellAuxConditions.length > 0) {
        fullDesc += '  [보조지표]\n';
        fullDesc += sellAuxConditions.map(c => `    • ${c}`).join('\n') + '\n';
      }
      fullDesc += '\n';
    }
    
    // 코드 로직 설명
    fullDesc += '━━━ 코드 로직 ━━━\n\n';
    
    // 보조지표 그룹 간 연산자 적용 (사용자 설정값 사용, 기본값: OR - 백엔드와 일치)
    const buyInterGroupOp = rule.interGroupOperatorBuy || 'OR';
    const sellInterGroupOp = rule.interGroupOperatorSell || 'OR';
    
    const allBuyLogic = [...buyLogic];
    if (buyAuxLogic.length > 0) {
      // 그룹 간 연산자를 사용자가 설정한 값으로 연결
      allBuyLogic.push(`(${buyAuxLogic.join(` ${buyInterGroupOp} `)})`);
    }
    
    const allSellLogic = [...sellLogic];
    if (sellAuxLogic.length > 0) {
      // 그룹 간 연산자를 사용자가 설정한 값으로 연결
      allSellLogic.push(`(${sellAuxLogic.join(` ${sellInterGroupOp} `)})`);
    }
    
    if (allBuyLogic.length > 0) {
      fullDesc += `매수 신호 = ${allBuyLogic.join(' AND ')}\n`;
    }
    if (allSellLogic.length > 0) {
      fullDesc += `매도 신호 = ${allSellLogic.join(' AND ')}\n`;
    }
    
    const hasAnyCondition = buyConditions.length > 0 || sellConditions.length > 0 || 
                           buyAuxConditions.length > 0 || sellAuxConditions.length > 0;
    
    if (!hasAnyCondition) {
      fullDesc += '⚠️ 설정된 조건이 없습니다.\n';
    }
    
    return fullDesc;
  };

  // 전략 검증 실행
  const handleValidate = async () => {
    setValidating(true);
    
    // 로컬 검증 수행
    const errors: ValidationError[] = [];
    const warnings: ValidationWarning[] = [];
    
    // 기본 유효성 검사 - 이름은 검증 시 경고만 표시, 저장 시 필수
    if (!rule.name || rule.name.trim() === '') {
      warnings.push({ groupIndex: 0, conditionIndex: 0, warningType: 'MISSING_NAME', message: '규칙 이름이 입력되지 않았습니다. 저장 시 이름이 필요합니다.' });
    }
    
    // 조건 존재 여부 검사 (ON 상태인 조건만 체크)
    const hasBuyMa = rule.enableMaBuy;
    const hasBuyIchimoku = rule.enableIchimokuBuy;
    const hasBuyBollinger = rule.enableBollingerBuy;
    const hasBuyAux = rule.enableAuxBuy && (rule.buyConditionGroups || []).some(g => g.conditions && g.conditions.length > 0);
    const hasSellMa = rule.enableMaSell;
    const hasSellIchimoku = rule.enableIchimokuSell;
    const hasSellBollinger = rule.enableBollingerSell;
    const hasSellAux = rule.enableAuxSell && (rule.sellConditionGroups || []).some(g => g.conditions && g.conditions.length > 0);
    
    // 매수, 매도 전체 통틀어 하나라도 ON인 조건이 있어야 함
    const hasAnyBuyCondition = hasBuyMa || hasBuyIchimoku || hasBuyBollinger || hasBuyAux;
    const hasAnySellCondition = hasSellMa || hasSellIchimoku || hasSellBollinger || hasSellAux;
    
    if (!hasAnyBuyCondition && !hasAnySellCondition) {
      errors.push({ 
        groupIndex: 0, 
        conditionIndex: 0, 
        errorType: 'NO_CONDITIONS', 
        message: '매수 또는 매도에서 최소 하나 이상의 조건을 ON으로 설정해야 합니다.',
        suggestion: '이동평균선, 일목균형표, 볼린저밴드, 보조지표 중 하나 이상을 ON으로 설정하세요.'
      });
    }
    
    // MA 조건 검증
    if (rule.enableMaBuy) {
      if ((rule.maShortPeriod1Buy || 0) >= (rule.maLongPeriod1Buy || 0)) {
        errors.push({ 
          groupIndex: 0, 
          conditionIndex: 0, 
          errorType: 'INVALID_PERIOD',
          message: '매수 MA 조건1: 단기 MA 기간이 장기 MA 기간보다 작아야 합니다.',
          suggestion: '단기 MA를 더 짧은 기간으로 설정하세요.'
        });
      }
      if (rule.enableMaCondition2Buy && (rule.maShortPeriod2Buy || 0) >= (rule.maLongPeriod2Buy || 0)) {
        errors.push({ 
          groupIndex: 0, 
          conditionIndex: 0, 
          errorType: 'INVALID_PERIOD',
          message: '매수 MA 조건2: 단기 MA 기간이 장기 MA 기간보다 작아야 합니다.',
          suggestion: '단기 MA를 더 짧은 기간으로 설정하세요.'
        });
      }
    }
    
    if (rule.enableMaSell) {
      if ((rule.maShortPeriod1Sell || 0) >= (rule.maLongPeriod1Sell || 0)) {
        errors.push({ 
          groupIndex: 0, 
          conditionIndex: 0, 
          errorType: 'INVALID_PERIOD',
          message: '매도 MA 조건1: 단기 MA 기간이 장기 MA 기간보다 작아야 합니다.',
          suggestion: '단기 MA를 더 짧은 기간으로 설정하세요.'
        });
      }
      if (rule.enableMaCondition2Sell && (rule.maShortPeriod2Sell || 0) >= (rule.maLongPeriod2Sell || 0)) {
        errors.push({ 
          groupIndex: 0, 
          conditionIndex: 0, 
          errorType: 'INVALID_PERIOD',
          message: '매도 MA 조건2: 단기 MA 기간이 장기 MA 기간보다 작아야 합니다.',
          suggestion: '단기 MA를 더 짧은 기간으로 설정하세요.'
        });
      }
    }
    
    // 일목균형표 조건 검증 - DB 값이 있는 경우에만 검증
    if (rule.enableIchimokuBuy && rule.ichimokuTenkanBuy && rule.ichimokuKijunBuy) {
      if (rule.ichimokuTenkanBuy >= rule.ichimokuKijunBuy) {
        warnings.push({ 
          groupIndex: 0, 
          conditionIndex: 0, 
          warningType: 'UNUSUAL_PERIOD',
          message: `매수 일목균형표: 전환선(${rule.ichimokuTenkanBuy}) 기간이 기준선(${rule.ichimokuKijunBuy}) 기간보다 작은 것이 일반적입니다.`
        });
      }
    }
    
    if (rule.enableIchimokuSell && rule.ichimokuTenkanSell && rule.ichimokuKijunSell) {
      if (rule.ichimokuTenkanSell >= rule.ichimokuKijunSell) {
        warnings.push({ 
          groupIndex: 0, 
          conditionIndex: 0, 
          warningType: 'UNUSUAL_PERIOD',
          message: `매도 일목균형표: 전환선(${rule.ichimokuTenkanSell}) 기간이 기준선(${rule.ichimokuKijunSell}) 기간보다 작은 것이 일반적입니다.`
        });
      }
    }
    
    // 조건 설명 생성
    const conditionDescription = generateFullConditionDescription();
    
    // 총 조건 수 계산 (ON 상태인 조건만)
    let totalConditions = 0;
    if (rule.enableMaBuy) totalConditions += rule.enableMaCondition2Buy ? 2 : 1;
    if (rule.enableIchimokuBuy) totalConditions++;
    if (rule.enableMaSell) totalConditions += rule.enableMaCondition2Sell ? 2 : 1;
    if (rule.enableIchimokuSell) totalConditions++;
    if (rule.enableAuxBuy) {
      (rule.buyConditionGroups || []).forEach(g => {
        totalConditions += (g.conditions?.length || 0);
      });
    }
    if (rule.enableAuxSell) {
      (rule.sellConditionGroups || []).forEach(g => {
        totalConditions += (g.conditions?.length || 0);
      });
    }
    
    // 결과 설정
    const status = errors.length > 0 ? 'FAIL' : (warnings.length > 0 ? 'WARNING' : 'PASS');
    
    // 검증 결과와 상관없이 설명란에 조건 설명 및 코드 로직 표시
    onChange({ ...rule, description: conditionDescription });
    
    setValidationResult({
      valid: errors.length === 0,
      status,
      message: conditionDescription,
      errors,
      warnings,
      totalConditions,
      errorCount: errors.length,
      warningCount: warnings.length,
    });
    
    setValidating(false);
  };

  // MA 기간 옵션
  const maShortPeriodOptions = [5, 10, 20];  // 단기 MA: 5일, 10일, 20일
  const maLongPeriodOptions = [10, 20, 60];  // 장기 MA: 10일, 20일, 60일
  
  // 일목균형표 기간 옵션
  const ichimokuTenkanOptions = [7, 9, 12];
  const ichimokuKijunOptions = [22, 26, 30, 52];

  // 현재 탭이 매수인지 매도인지
  const isBuyTab = activeTab === 0;

  // 매수/매도 탭에 따른 필드명 헬퍼
  const getFieldValue = <K extends keyof StrategyRuleDto>(buyKey: K, sellKey: K): StrategyRuleDto[K] => {
    return isBuyTab ? rule[buyKey] : rule[sellKey];
  };

  const setFieldValue = (buyKey: keyof StrategyRuleDto, sellKey: keyof StrategyRuleDto, value: any) => {
    const key = isBuyTab ? buyKey : sellKey;
    onChange({ ...rule, [key]: value });
  };
  
  // 이동평균선 조건 ON 토글 핸들러 (기본값 강제 설정)
  const handleMaToggle = (checked: boolean) => {
    if (isBuyTab) {
      const currentConditions = rule.maConditionsBuy || [];
      const defaultConditions = checked && currentConditions.length === 0
        ? [createEmptyMaCondition(0, 'BUY')]
        : currentConditions;
      
      onChange({
        ...rule,
        enableMaBuy: checked,
        maConditionsBuy: defaultConditions,
        // ON 시 기본값 설정 (값이 없으면 기본값 적용) - 레거시 지원
        maShortPeriod1Buy: checked ? (rule.maShortPeriod1Buy || 20) : rule.maShortPeriod1Buy,
        maLongPeriod1Buy: checked ? (rule.maLongPeriod1Buy || 60) : rule.maLongPeriod1Buy,
        maCrossType1Buy: checked ? (rule.maCrossType1Buy || 'GOLDEN_CROSS') : rule.maCrossType1Buy,
      });
    } else {
      const currentConditions = rule.maConditionsSell || [];
      const defaultConditions = checked && currentConditions.length === 0
        ? [createEmptyMaCondition(0, 'SELL')]
        : currentConditions;
      
      onChange({
        ...rule,
        enableMaSell: checked,
        maConditionsSell: defaultConditions,
        maShortPeriod1Sell: checked ? (rule.maShortPeriod1Sell || 20) : rule.maShortPeriod1Sell,
        maLongPeriod1Sell: checked ? (rule.maLongPeriod1Sell || 60) : rule.maLongPeriod1Sell,
        maCrossType1Sell: checked ? (rule.maCrossType1Sell || 'DEAD_CROSS') : rule.maCrossType1Sell,
      });
    }
  };
  
  // 일목균형표 조건 ON 토글 핸들러 (기본값 강제 설정)
  const handleIchimokuToggle = (checked: boolean) => {
    if (isBuyTab) {
      onChange({
        ...rule,
        enableIchimokuBuy: checked,
        ichimokuTenkanBuy: checked ? (rule.ichimokuTenkanBuy || 9) : rule.ichimokuTenkanBuy,
        ichimokuKijunBuy: checked ? (rule.ichimokuKijunBuy || 26) : rule.ichimokuKijunBuy,
        ichimokuCrossTypeBuy: checked ? (rule.ichimokuCrossTypeBuy || 'CROSS_UP') : rule.ichimokuCrossTypeBuy,
      });
    } else {
      onChange({
        ...rule,
        enableIchimokuSell: checked,
        ichimokuTenkanSell: checked ? (rule.ichimokuTenkanSell || 9) : rule.ichimokuTenkanSell,
        ichimokuKijunSell: checked ? (rule.ichimokuKijunSell || 26) : rule.ichimokuKijunSell,
        ichimokuCrossTypeSell: checked ? (rule.ichimokuCrossTypeSell || 'CROSS_DOWN') : rule.ichimokuCrossTypeSell,
      });
    }
  };
  
  // 볼린저밴드 조건 ON 토글 핸들러
  const handleBollingerToggle = (checked: boolean) => {
    if (isBuyTab) {
      const defaultConditions = checked && (!rule.bollingerConditionsBuy || rule.bollingerConditionsBuy.length === 0)
        ? [createEmptyBollingerCondition(0, 'BUY')]
        : rule.bollingerConditionsBuy;
      
      onChange({
        ...rule,
        enableBollingerBuy: checked,
        bollingerConditionsBuy: defaultConditions,
      });
    } else {
      const defaultConditions = checked && (!rule.bollingerConditionsSell || rule.bollingerConditionsSell.length === 0)
        ? [createEmptyBollingerCondition(0, 'SELL')]
        : rule.bollingerConditionsSell;
      
      onChange({
        ...rule,
        enableBollingerSell: checked,
        bollingerConditionsSell: defaultConditions,
      });
    }
  };
  
  // 볼린저밴드 조건 가져오기
  const getCurrentBollingerConditions = () => {
    return isBuyTab 
      ? (rule.bollingerConditionsBuy || [createEmptyBollingerCondition(0, 'BUY')])
      : (rule.bollingerConditionsSell || [createEmptyBollingerCondition(0, 'SELL')]);
  };
  
  // 볼린저밴드 조건 변경
  const handleBollingerConditionChange = (index: number, condition: any) => {
    const conditions = [...getCurrentBollingerConditions()];
    conditions[index] = condition;
    
    if (isBuyTab) {
      onChange({ ...rule, bollingerConditionsBuy: conditions });
    } else {
      onChange({ ...rule, bollingerConditionsSell: conditions });
    }
  };
  
  // 볼린저밴드 조건 추가
  const addBollingerCondition = (operator: LogicalOperator) => {
    const conditions = getCurrentBollingerConditions();
    const newCondition = createEmptyBollingerCondition(conditions.length, isBuyTab ? 'BUY' : 'SELL');
    
    // 이전 조건에 논리 연산자 설정
    if (conditions.length > 0) {
      conditions[conditions.length - 1].logicalOperator = operator;
    }
    
    if (isBuyTab) {
      onChange({ ...rule, bollingerConditionsBuy: [...conditions, newCondition] });
    } else {
      onChange({ ...rule, bollingerConditionsSell: [...conditions, newCondition] });
    }
  };
  
  // 볼린저밴드 조건 삭제
  const removeBollingerCondition = (index: number) => {
    const conditions = getCurrentBollingerConditions();
    if (conditions.length <= 1) return;
    
    const newConditions = conditions.filter((_, i) => i !== index);
    
    if (isBuyTab) {
      onChange({ ...rule, bollingerConditionsBuy: newConditions });
    } else {
      onChange({ ...rule, bollingerConditionsSell: newConditions });
    }
  };
  
  // 볼린저밴드 조건 논리 연산자 업데이트
  const updateBollingerConditionOperator = (index: number, operator: LogicalOperator) => {
    const conditions = [...getCurrentBollingerConditions()];
    conditions[index].logicalOperator = operator;
    
    if (isBuyTab) {
      onChange({ ...rule, bollingerConditionsBuy: conditions });
    } else {
      onChange({ ...rule, bollingerConditionsSell: conditions });
    }
  };
  
  // 보조지표 토글 핸들러 (ON 시 기본 그룹 1개 생성)
  const handleAuxToggle = (checked: boolean) => {
    const enableKey = isBuyTab ? 'enableAuxBuy' : 'enableAuxSell';
    const groupsKey = isBuyTab ? 'buyConditionGroups' : 'sellConditionGroups';
    const currentGroups = getCurrentConditionGroups();
    
    if (checked && currentGroups.length === 0) {
      // ON으로 변경하고 그룹이 없으면 기본 1개 그룹 생성
      const groupType = isBuyTab ? 'BUY' : 'SELL';
      onChange({
        ...rule,
        [enableKey]: true,
        [groupsKey]: [createEmptyGroup(0, groupType)],
      });
    } else {
      // 단순히 토글만 변경
      onChange({ ...rule, [enableKey]: checked });
    }
  };

  // DSL 전략 여부 확인
  const isDSLStrategy = rule.strategyType === 'DSL' || rule.createdByUi === 'TREE';

  return (
    <Dialog open={open} onClose={onClose} maxWidth="lg" fullWidth>
      <DialogTitle>
        {isNew ? '새 규칙 생성' : '규칙 수정'}
        <Chip
          size="small"
          label={rule.signalType === 'BUY' ? '매수' : '매도'}
          color={rule.signalType === 'BUY' ? 'success' : 'error'}
          sx={{ ml: 2 }}
        />
        {isDSLStrategy && (
          <Chip
            size="small"
            label="드래그"
            color="primary"
            sx={{ ml: 1 }}
          />
        )}
      </DialogTitle>
      <DialogContent dividers>
        {/* ✅ DSL 전략 편집 안내 */}
        {isDSLStrategy && (
          <Alert severity="info" sx={{ mb: 2 }}>
            이 전략은 <strong>전략(드래그)</strong> 화면에서 생성되었습니다. 
            기본 정보(이름, 설명, 활성화)는 여기서 수정 가능하지만, 
            조건을 편집하려면 <strong>전략(드래그)</strong> 화면을 이용하세요.
          </Alert>
        )}
        
        {/* 기본 정보 섹션 */}
        <Grid container spacing={2}>
          <Grid item xs={12}>
            <TextField
              fullWidth
              label="규칙명"
              value={rule.name}
              onChange={(e) => onChange({ ...rule, name: e.target.value })}
              required
            />
          </Grid>
        </Grid>

        <Divider sx={{ my: 3 }} />

        {/* 매수/매도 탭 */}
        <Box sx={{ borderBottom: 1, borderColor: 'divider', mb: 3 }}>
          <Tabs 
            value={activeTab} 
            onChange={handleTabChange}
            sx={{
              '& .MuiTab-root': { fontWeight: 'bold' },
            }}
          >
            <Tab 
              label="매수 조건" 
              icon={<TrendingUp />} 
              iconPosition="start"
              sx={{ 
                color: activeTab === 0 ? 'success.main' : 'text.secondary',
                '&.Mui-selected': { color: 'success.main' }
              }}
            />
            <Tab 
              label="매도 조건" 
              icon={<TrendingDown />} 
              iconPosition="start"
              sx={{ 
                color: activeTab === 1 ? 'error.main' : 'text.secondary',
                '&.Mui-selected': { color: 'error.main' }
              }}
            />
          </Tabs>
        </Box>

        {/* 이동평균선 조건그룹 - 레거시 UI 제거됨 */}

        {/* 일목균형표 조건그룹 - 레거시 UI 제거됨 */}

        {/* 볼린저밴드 조건그룹 - 레거시 UI 제거됨 */}

        <Divider sx={{ my: 3 }} />

        {/* 보조지표 조건 그룹 (탭별 분리) */}
        <Card variant="outlined" sx={{ mb: 2, borderColor: isBuyTab ? 'success.main' : 'error.main', borderWidth: 2 }}>
          <CardContent>
            <Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 2 }}>
              <Typography variant="subtitle1" fontWeight="bold" sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
                {isBuyTab ? <TrendingUp color="success" /> : <TrendingDown color="error" />}
                {isBuyTab ? '매수' : '매도'} 보조지표 조건그룹
              </Typography>
              <Switch
                checked={(isBuyTab ? rule.enableAuxBuy : rule.enableAuxSell) || false}
                onChange={(e) => handleAuxToggle(e.target.checked)}
                color={isBuyTab ? 'success' : 'error'}
                disabled={isDSLStrategy}
              />
            </Box>

            <Collapse in={(isBuyTab ? rule.enableAuxBuy : rule.enableAuxSell) || false}>
              <Box sx={{
                pointerEvents: isDSLStrategy ? 'none' : 'auto',
                opacity: isDSLStrategy ? 0.6 : 1
              }}>
                {/* 안내 메시지 */}
                <Alert severity="info" sx={{ mb: 2 }}>
                  각 그룹마다 조건을 설정하고, 그룹 간의 논리 관계(AND/OR)를 개별적으로 선택할 수 있습니다.
                </Alert>

                {getCurrentConditionGroups().map((group, index) => (
                <Box key={`${isBuyTab ? 'buy' : 'sell'}-${index}`}>
                  <ConditionGroupEditor
                    group={group}
                    groupIndex={index}
                    canRemove={getCurrentConditionGroups().length > 1}
                    onChange={(g) => handleGroupChange(index, g)}
                    onRemove={() => removeGroup(index)}
                    signalType={isBuyTab ? 'BUY' : 'SELL'}
                    indicatorOptions={indicatorOptions}
                    getConditionOptions={getConditionOptions}
                    indicatorConfig={indicatorConfig}
                  />
                  
                  {/* 그룹 간 논리 연산자 선택 (마지막 그룹이 아닐 때만 표시) */}
                  {index < getCurrentConditionGroups().length - 1 && (
                    <Box sx={{ 
                      display: 'flex', 
                      alignItems: 'center', 
                      justifyContent: 'center',
                      gap: 2, 
                      my: 2, 
                      py: 1.5,
                      px: 2,
                      bgcolor: 'action.hover',
                      borderRadius: 1
                    }}>
                      <Typography variant="body2" color="text.secondary" fontWeight="medium">
                        다음 그룹과의 관계:
                      </Typography>
                      <ToggleButtonGroup
                        value={getCurrentConditionGroups()[index].interGroupOperator || 'OR'}
                        exclusive
                        onChange={(_, value) => {
                          if (value !== null) {
                            updateGroupOperator(index, value as LogicalOperator);
                          }
                        }}
                        size="small"
                      >
                        <ToggleButton value="AND" sx={{ px: 3 }}>
                          AND
                        </ToggleButton>
                        <ToggleButton value="OR" sx={{ px: 3 }}>
                          OR
                        </ToggleButton>
                      </ToggleButtonGroup>
                      <Typography variant="caption" color="text.secondary">
                        {getCurrentConditionGroups()[index].interGroupOperator === 'AND' ? '(모두 만족)' : '(하나만 만족)'}
                      </Typography>
                    </Box>
                  )}
                </Box>
                ))}

                {/* 그룹 추가 버튼 */}
                <Box sx={{ display: 'flex', gap: 1, mt: 3, mb: 2 }}>
                  <Button
                    size="small"
                    startIcon={<AddIcon />}
                    onClick={() => addGroup('AND')}
                    variant="outlined"
                    sx={{ flex: 1 }}
                  >
                    AND 그룹 추가
                  </Button>
                  <Button
                    size="small"
                    startIcon={<AddIcon />}
                    onClick={() => addGroup('OR')}
                    variant="outlined"
                    color="warning"
                    sx={{ flex: 1 }}
                  >
                    OR 그룹 추가
                  </Button>
                </Box>
              </Box>
            </Collapse>
          </CardContent>
        </Card>

        {/* 검증 결과 표시 영역 */}
        {validationResult && (
          <Box sx={{ mt: 3 }}>
            <Alert 
              severity={
                validationResult.status === 'PASS' ? 'success' : 
                validationResult.status === 'WARNING' ? 'warning' : 'error'
              }
              icon={
                validationResult.status === 'PASS' ? <CheckCircle /> :
                validationResult.status === 'WARNING' ? <WarningIcon /> : <ErrorIcon />
              }
              sx={{ mb: 2 }}
            >
              <Typography variant="subtitle2" sx={{ mb: 1 }}>
                {validationResult.status === 'PASS' ? '✅ 검증 통과 (PASS)' :
                 validationResult.status === 'WARNING' ? '⚠️ 검증 통과 (경고 있음)' : 
                 '❌ 검증 실패 (FAIL)'}
                {' '} - 총 {validationResult.totalConditions}개 조건
              </Typography>
            </Alert>
            
            {/* 오류 목록 */}
            {validationResult.errors && validationResult.errors.length > 0 && (
              <Box sx={{ mb: 2 }}>
                <Typography variant="subtitle2" color="error" gutterBottom>
                  오류 ({validationResult.errorCount}개)
                </Typography>
                <List dense sx={{ bgcolor: 'error.light', borderRadius: 1, opacity: 0.9 }}>
                  {validationResult.errors.map((error, idx) => (
                    <ListItem key={idx}>
                      <ListItemText
                        primary={
                          <Typography variant="body2" sx={{ color: 'error.contrastText' }}>
                            {error.groupIndex > 0 ? `그룹 ${error.groupIndex}` : '기본 정보'}
                            {error.conditionIndex > 0 ? `, 조건 ${error.conditionIndex}` : ''}: {error.message}
                          </Typography>
                        }
                        secondary={error.suggestion && (
                          <Typography variant="caption" sx={{ color: 'error.contrastText', opacity: 0.8 }}>
                            💡 {error.suggestion}
                          </Typography>
                        )}
                      />
                    </ListItem>
                  ))}
                </List>
              </Box>
            )}
            
            {/* 경고 목록 */}
            {validationResult.warnings && validationResult.warnings.length > 0 && (
              <Box sx={{ mb: 2 }}>
                <Typography variant="subtitle2" color="warning.dark" gutterBottom>
                  경고 ({validationResult.warningCount}개)
                </Typography>
                <List dense sx={{ bgcolor: 'warning.light', borderRadius: 1, opacity: 0.9 }}>
                  {validationResult.warnings.map((warning, idx) => (
                    <ListItem key={idx}>
                      <ListItemText
                        primary={
                          <Typography variant="body2" sx={{ color: 'warning.contrastText' }}>
                            {warning.groupIndex > 0 ? `그룹 ${warning.groupIndex}` : '기본 정보'}
                            {warning.conditionIndex > 0 ? `, 조건 ${warning.conditionIndex}` : ''}: {warning.message}
                          </Typography>
                        }
                      />
                    </ListItem>
                  ))}
                </List>
              </Box>
            )}
            
            {/* 설명란 (전략검증 시 조건 설명 및 코드 로직 자동 입력) */}
            <Box sx={{ mt: 2 }}>
              <TextField
                fullWidth
                label="설명 (전체 조건 설명 및 코드 로직)"
                value={rule.description || ''}
                onChange={(e) => onChange({ ...rule, description: e.target.value })}
                multiline
                rows={10}
                InputProps={{
                  sx: {
                    fontFamily: 'monospace',
                    fontSize: '12px',
                  }
                }}
                helperText="전략검증 시 조건 설명 및 코드 로직이 자동 입력됩니다"
              />
            </Box>
          </Box>
        )}
      </DialogContent>
      <DialogActions>
        <Button onClick={onClose}>취소</Button>
        <Button
          variant="outlined"
          color="info"
          startIcon={validating ? <CircularProgress size={20} /> : <VerifyIcon />}
          onClick={handleValidate}
          disabled={validating}
        >
          {validating ? '검증 중...' : '전략 검증'}
        </Button>
        <Button
          variant="contained"
          onClick={onSave}
          disabled={!rule.name}
        >
          저장
        </Button>
      </DialogActions>
    </Dialog>
  );
};

// ============================================================
// ConditionGroupEditor 컴포넌트
// ============================================================

interface ConditionGroupEditorProps {
  group: ConditionGroupDto;
  groupIndex: number;
  canRemove: boolean;
  onChange: (group: ConditionGroupDto) => void;
  onRemove: () => void;
  signalType: SignalType;
  indicatorOptions: { value: IndicatorType; label: string }[];
  getConditionOptions: (indicatorType: IndicatorType, signalType?: SignalType) => { value: ConditionType; label: string }[];
  indicatorConfig: { [key: string]: { min: number; max: number; step: number; defaultValue: number; required: boolean; helperText?: string } };
}

const ConditionGroupEditor: React.FC<ConditionGroupEditorProps> = ({
  group, groupIndex, canRemove, onChange, onRemove, signalType,
  indicatorOptions, getConditionOptions, indicatorConfig
}) => {
  const [expanded, setExpanded] = useState(true);
  const indicatorSettings = loadIndicatorSettings();

  const handleConditionChange = (index: number, condition: StrategyConditionDto) => {
    const newConditions = [...group.conditions];
    newConditions[index] = condition;
    onChange({ ...group, conditions: newConditions });
  };

  const addCondition = (operator: LogicalOperator = 'AND') => {
    const currentConditions = group.conditions;
    
    const newCondition = createEmptyCondition(currentConditions.length);
    const defaultIndicatorType = newCondition.indicatorType || 'RSI';
    const conditionOptions = getConditionOptions(defaultIndicatorType, signalType);
    
    // 기본값 설정
    newCondition.conditionType = conditionOptions.length > 0 ? conditionOptions[0].value : 'ABOVE';
    newCondition.period = defaultIndicatorType === 'RSI' ? indicatorSettings.rsiPeriod : undefined;
    
    // 기준값 설정 (조건 타입이 필요로 하는 경우)
    if (isTargetValueRequired(defaultIndicatorType, newCondition.conditionType)) {
      const presets = INDICATOR_PRESET_VALUES[defaultIndicatorType] || [];
      const filteredPresets = presets.filter(p => p.value !== null);
      if (filteredPresets.length > 0) {
        newCondition.targetValue = filteredPresets[0].value!;
      }
    }
    
    // 이전 조건의 논리 연산자 설정
    if (currentConditions.length > 0) {
      const updatedConditions = [...currentConditions];
      updatedConditions[updatedConditions.length - 1] = {
        ...updatedConditions[updatedConditions.length - 1],
        logicalOperator: operator,
      };
      updatedConditions.push(newCondition);
      
      onChange({ ...group, conditions: updatedConditions });
    } else {
      onChange({ ...group, conditions: [newCondition] });
    }
  };

  const removeCondition = (index: number) => {
    const newConditions = group.conditions.filter((_, i) => i !== index);
    
    // 마지막 조건이 제거된 경우, 이전 조건의 logicalOperator 제거
    if (newConditions.length > 0) {
      newConditions[newConditions.length - 1] = {
        ...newConditions[newConditions.length - 1],
        logicalOperator: undefined,
      };
    }
    
    onChange({ ...group, conditions: newConditions });
  };
  
  const updateConditionOperator = (index: number, operator: LogicalOperator) => {
    const newConditions = [...group.conditions];
    newConditions[index] = {
      ...newConditions[index],
      logicalOperator: operator,
    };
    onChange({ ...group, conditions: newConditions });
  };

  return (
    <Card sx={{ mb: 2, border: '1px solid', borderColor: 'divider' }}>
      <CardContent sx={{ pb: 1 }}>
        <Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
          <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
            <IconButton size="small" onClick={() => setExpanded(!expanded)}>
              {expanded ? <ExpandLessIcon /> : <ExpandMoreIcon />}
            </IconButton>
            <Typography variant="subtitle1" fontWeight="bold">
              그룹 {groupIndex + 1}
            </Typography>
          </Box>
          {canRemove && (
            <IconButton size="small" color="error" onClick={onRemove}>
              <DeleteIcon />
            </IconButton>
          )}
        </Box>

        <Collapse in={expanded}>
          <Box sx={{ mt: 2 }}>
            {group.conditions.map((condition, index) => (
              <Box key={index}>
                <ConditionEditor
                  condition={condition}
                  conditionIndex={index}
                  logicalOperator={condition.logicalOperator}
                  canRemove={group.conditions.length > 1}
                  onChange={(c) => handleConditionChange(index, c)}
                  onRemove={() => removeCondition(index)}
                  onLogicalOperatorChange={(op) => updateConditionOperator(index, op)}
                  indicatorOptions={indicatorOptions}
                  getConditionOptions={getConditionOptions}
                  indicatorConfig={indicatorConfig}
                  signalType={signalType}
                />
              </Box>
            ))}

            {/* 조건 추가 버튼 */}
            <Box sx={{ display: 'flex', gap: 1, mt: 2 }}>
              <Button
                size="small"
                startIcon={<AddIcon />}
                onClick={() => addCondition('AND')}
                variant="outlined"
                sx={{ flex: 1 }}
              >
                AND 조건 추가
              </Button>
              <Button
                size="small"
                startIcon={<AddIcon />}
                onClick={() => addCondition('OR')}
                variant="outlined"
                color="warning"
                sx={{ flex: 1 }}
              >
                OR 조건 추가
              </Button>
            </Box>
          </Box>
        </Collapse>
      </CardContent>
    </Card>
  );
};

// ============================================================
// ConditionEditor 컴포넌트
// ============================================================

interface ConditionEditorProps {
  condition: StrategyConditionDto;
  conditionIndex: number;
  logicalOperator?: LogicalOperator;
  canRemove: boolean;
  onChange: (condition: StrategyConditionDto) => void;
  onRemove: () => void;
  onLogicalOperatorChange: (operator: LogicalOperator) => void;
  indicatorOptions: { value: IndicatorType; label: string }[];
  getConditionOptions: (indicatorType: IndicatorType, signalType?: SignalType) => { value: ConditionType; label: string }[];
  indicatorConfig: { [key: string]: { min: number; max: number; step: number; defaultValue: number; required: boolean; helperText?: string } };
  signalType: SignalType;
}

const ConditionEditor: React.FC<ConditionEditorProps> = ({
  condition, conditionIndex, logicalOperator, canRemove, onChange, onRemove, onLogicalOperatorChange,
  indicatorOptions, getConditionOptions, indicatorConfig, signalType
}) => {
  const indicatorSettings = loadIndicatorSettings();

  // 지표별 허용 조건 목록
  const conditionOptions = getConditionOptions(condition.indicatorType, signalType);
  
  // 기준값 필요 여부 판단
  const needsTargetValue = condition.indicatorType === 'MACD' ? false : isTargetValueRequired(condition.indicatorType, condition.conditionType);
  const needsSecondaryValue = condition.conditionType === 'BETWEEN';
  
  // 기간 설정이 필요한 지표 여부
  const needsPeriod = ['RSI', 'STOCHASTIC', 'CCI', 'ADX', 'TRIX', 'DISPARITY', 
                       'NEW_PSYCHOLOGICAL', 'INVEST_PSYCHOLOGICAL', 'WILLIAMS_R', 
                       'BWI', 'VOLUME_OSC', 'VR', 'DMI', 'MACD', 'OBV'].includes(condition.indicatorType);
  
  // 해당 지표의 기간 옵션 가져오기
  const periodOptions = condition.indicatorType === 'MACD' 
    ? [
        { value: indicatorSettings.macdSignalPeriod, label: 'MACD선' },
      ]
    : condition.indicatorType === 'STOCHASTIC'
    ? [
        { value: indicatorSettings.stochasticKPeriod, label: '%K' },
        { value: indicatorSettings.stochasticDPeriod, label: '%D' },
      ]
    : condition.indicatorType === 'RSI'
    ? [
        { value: indicatorSettings.rsiPeriod, label: 'RSI' },
      ]
    : [];

  return (
    <Box sx={{ mb: 2, p: 2, bgcolor: 'action.hover', borderRadius: 1 }}>
      {conditionIndex > 0 && (
        <Typography variant="caption" color="text.secondary" sx={{ display: 'block', mb: 1 }}>
          {logicalOperator}
        </Typography>
      )}
      <Grid container spacing={2} alignItems="center">
        {/* 캔들 범위 선택 */}
        <Grid item xs={12} md={2}>
          <FormControl fullWidth size="small">
            <InputLabel>캔들 범위</InputLabel>
            <Select
              value={condition.candleRange || 1}
              label="캔들 범위"
              onChange={(e) => onChange({ ...condition, candleRange: Number(e.target.value) })}
            >
              <MenuItem value={1}>현재 캔들</MenuItem>
              <MenuItem value={2}>2개 캔들</MenuItem>
              <MenuItem value={3}>3개 캔들</MenuItem>
              <MenuItem value={4}>4개 캔들</MenuItem>
              <MenuItem value={5}>5개 캔들</MenuItem>
            </Select>
          </FormControl>
        </Grid>
        
        {/* 지표 선택 */}
        <Grid item xs={12} md={3}>
          <FormControl fullWidth size="small">
            <InputLabel>지표</InputLabel>
            <Select
              value={condition.indicatorType}
              label="지표"
              onChange={(e) => {
                const newIndicatorType = e.target.value as IndicatorType;
                const conditionOptions = getConditionOptions(newIndicatorType, signalType);
                onChange({
                  ...condition,
                  indicatorType: newIndicatorType,
                  conditionType: conditionOptions.length > 0 ? conditionOptions[0].value : 'ABOVE',
                  targetValue: undefined,
                  secondaryValue: undefined,
                  period: newIndicatorType === 'RSI' ? indicatorSettings.rsiPeriod : undefined,
                  comparePeriod: undefined,
                });
              }}
            >
              {indicatorOptions.map((opt) => (
                <MenuItem key={opt.value} value={opt.value}>
                  {opt.label}
                </MenuItem>
              ))}
            </Select>
          </FormControl>
        </Grid>

        {/* 조건 타입 선택 */}
        <Grid item xs={12} md={3}>
          <FormControl fullWidth size="small">
            <InputLabel>조건</InputLabel>
            <Select
              value={condition.conditionType}
              label="조건"
              onChange={(e) => onChange({ ...condition, conditionType: e.target.value as ConditionType })}
            >
              {getConditionOptions(condition.indicatorType, signalType).map((opt) => (
                <MenuItem key={opt.value} value={opt.value}>
                  {opt.label}
                </MenuItem>
              ))}
            </Select>
          </FormControl>
        </Grid>

        {/* 동적 입력 필드 (기간, 기준값, 비교 기간 등) */}
        {needsPeriod && (
          <Grid item xs={12} md={2}>
            <FormControl fullWidth size="small">
              <InputLabel>기간</InputLabel>
              <Select
                value={condition.period || ''}
                label="기간"
                onChange={(e) => onChange({ ...condition, period: Number(e.target.value) })}
              >
                {periodOptions.map((opt) => (
                  <MenuItem key={opt.value} value={opt.value}>
                    {opt.label}
                  </MenuItem>
                ))}
              </Select>
            </FormControl>
          </Grid>
        )}

        {needsTargetValue && (
          <Grid item xs={12} md={2}>
            <FormControl fullWidth size="small">
              <InputLabel>기준값</InputLabel>
              <Select
                value={condition.targetValue ?? ''}
                label="기준값"
                onChange={(e) => onChange({ ...condition, targetValue: Number(e.target.value) })}
              >
                {(INDICATOR_PRESET_VALUES[condition.indicatorType] || []).map((preset) => (
                  <MenuItem key={preset.label} value={preset.value || 0}>
                    {preset.label}
                  </MenuItem>
                ))}
              </Select>
            </FormControl>
          </Grid>
        )}

        {needsSecondaryValue && (
          <Grid item xs={12} md={2}>
            <TextField
              fullWidth
              size="small"
              label="상한 기준값"
              type="number"
              value={condition.secondaryValue ?? ''}
              onChange={(e) => onChange({ ...condition, secondaryValue: Number(e.target.value) })}
            />
          </Grid>
        )}

        {/* 삭제 버튼 */}
        <Grid item xs={12} md={1}>
          <IconButton
            size="small"
            color="error"
            onClick={onRemove}
            disabled={!canRemove}
          >
            <DeleteIcon />
          </IconButton>
        </Grid>
      </Grid>

      {/* 논리 연산자 선택 */}
      {conditionIndex < 20 && logicalOperator && (
        <Box sx={{ 
          display: 'flex', 
          alignItems: 'center', 
          gap: 2, 
          my: 1.5, 
          py: 1,
          px: 2,
          bgcolor: 'background.paper',
          borderRadius: 1
        }}>
          <Typography variant="body2" color="text.secondary">
            다음 조건과의 관계:
          </Typography>
          <ToggleButtonGroup
            value={logicalOperator}
            exclusive
            onChange={(_, value) => {
              if (value !== null) {
                onLogicalOperatorChange(value as LogicalOperator);
              }
            }}
            size="small"
          >
            <ToggleButton value="AND" sx={{ px: 2 }}>
              AND
            </ToggleButton>
            <ToggleButton value="OR" sx={{ px: 2 }}>
              OR
            </ToggleButton>
          </ToggleButtonGroup>
          <Typography variant="caption" color="text.secondary">
            {logicalOperator === 'AND' ? '(모두 만족)' : '(하나만 만족)'}
          </Typography>
        </Box>
      )}
    </Box>
  );
};

// ============================================================
// 보조 함수들
// ============================================================

// 지표 설명 생성 함수
const getIndicatorDescription = (indicatorType: IndicatorType): string => {
  const descriptions: Partial<Record<IndicatorType, string>> = {
    'VOLUME': 'Volume - 거래량',
    'RSI': 'Relative Strength Index - 과매수/과매도 지표',
    'STOCHASTIC': '스토캐스틱 - 가격 모멘텀 지표',
    'MACD': 'MACD - 이동평균 수렴확산',
    'CCI': 'Commodity Channel Index - 상품채널지수',
    'ADX': 'Average Directional Index - 추세 강도',
    'TRIX': 'Triple Exponential Average - 변동률 지표',
    'DISPARITY': 'Disparity Index - 이격도',
    'NEW_PSYCHOLOGICAL': 'New Psychological Line - 신심리선',
    'INVEST_PSYCHOLOGICAL': 'Invest Psychological Line - 투자심리선',
    'WILLIAMS_R': 'Williams %R - 모멘텀 지표',
    'BWI': 'Bollinger Width Index - 볼린저 밴드 폭',
    'VOLUME_OSC': 'Volume Oscillator - 거래량 오실레이터',
    'VR': 'Volume Ratio - 거래량 비율',
    'DMI': 'Directional Movement Index - 방향성 지표',
    'OBV': 'On Balance Volume - 거래량 누적',
  };
  return descriptions[indicatorType] || indicatorType;
};

export default StrategyRulePage;
