/**
 * 멀티차트 대시보드 상태 관리 (Zustand)
 */
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import type { Layout } from 'react-grid-layout';
import type { MultiChartGridPreset } from '../utils/multiChartLayoutUtils';
import {
  clampSlotStocksToMax,
  countStocksInSlots,
  flatToSlots,
  isMultiChartGridPreset,
  MAX_MULTI_CHART_COUNT,
  normalizeMultiChartGridPreset,
  packFlatForGridView,
  presetSlotCount,
  repackFlatForGridModeChange,
  slotsAfterStockChange,
  sortSlotsByGridIndex,
  trimTrailingEmptyRows,
} from '../utils/multiChartLayoutUtils';
import type { MALineConfig } from '../contexts/IndicatorVisibilityContext';
import { defaultMALines } from '../contexts/IndicatorVisibilityContext';

export type { ColumnCount, GridViewMode, MultiChartGridPreset } from '../utils/multiChartLayoutUtils';

/** 레이아웃 옵션: rows x cols (멀티차트 그리드 + 기타) */
export type LayoutPreset =
  | '1x1'
  | '1x2'
  | '1x3'
  | '2x1'
  | '2x2'
  | '2x3'
  | '3x1'
  | '3x2'
  | '3x3'
  | '3x4'
  | '4x3'
  | '4x4';

/** 차트 유형: 사용자 선택 / 상승추세 순 / 거래량 순 / 알림종목 */
export type ChartType = 'user' | 'uptrend' | 'volume' | 'alert';

export interface StockInfo {
  symbol: string;
  koreanName: string;
  englishName?: string;
  /** 등락률 (%) - 상승추세 표시용 */
  changeRate?: number;
  /** 거래량 - 표시용 */
  volume?: number;
}

export interface ChartSlot {
  id: string;
  stock: StockInfo | null;
  /** react-grid-layout용 위치 (i는 grid index) */
  gridIndex: number;
}

export interface LayoutConfig {
  rows: number;
  /** CSS 그리드 열 수 (`gridLayout`과 동기화) */
  cols: number;
  preset: LayoutPreset;
}

/** 멀티차트 상단 일목(토글 + 기간 + 색·굵기·구름) — persist + 설정 다이얼로그와 동기 */
export interface MultiChartIchimokuState {
  tenkan: boolean;
  kijun: boolean;
  chikou: boolean;
  senkouA: boolean;
  senkouB: boolean;
  tenkanPeriod: number;
  kijunPeriod: number;
  chikouPeriod: number;
  senkouAPeriod: number;
  senkouBPeriod: number;
  tenkanColor: string;
  kijunColor: string;
  chikouColor: string;
  senkouAColor: string;
  senkouBColor: string;
  tenkanWidth: number;
  kijunWidth: number;
  chikouWidth: number;
  senkouAWidth: number;
  senkouBWidth: number;
  cloudBullish: string;
  cloudBearish: string;
}

/** 멀티차트 볼린저(토글·기간·색·굵기·선종류·배경) */
export interface MultiChartBollingerState {
  upper: boolean;
  middle: boolean;
  lower: boolean;
  period: number;
  stdDev: number;
  upperColor: string;
  middleColor: string;
  lowerColor: string;
  upperWidth: number;
  middleWidth: number;
  lowerWidth: number;
  upperLineStyle: 'solid' | 'dashed' | 'dotted';
  middleLineStyle: 'solid' | 'dashed' | 'dotted';
  lowerLineStyle: 'solid' | 'dashed' | 'dotted';
  bandBackgroundColor: string;
}

export const defaultMultiChartIchimoku = (): MultiChartIchimokuState => ({
  tenkan: false,
  kijun: false,
  chikou: false,
  senkouA: false,
  senkouB: false,
  tenkanPeriod: 9,
  kijunPeriod: 26,
  chikouPeriod: 26,
  senkouAPeriod: 26,
  senkouBPeriod: 52,
  tenkanColor: '#EF5350',
  kijunColor: '#42A5F5',
  chikouColor: '#66BB6A',
  senkouAColor: '#26A69A',
  senkouBColor: '#EF5350',
  tenkanWidth: 1,
  kijunWidth: 1.5,
  chikouWidth: 1,
  senkouAWidth: 1,
  senkouBWidth: 1,
  cloudBullish: 'rgba(239, 83, 80, 0.2)',
  cloudBearish: 'rgba(38, 166, 154, 0.2)',
});

export const defaultMultiChartBollinger = (): MultiChartBollingerState => ({
  upper: false,
  middle: false,
  lower: false,
  period: 20,
  stdDev: 2,
  upperColor: '#FF9800',
  middleColor: '#2196F3',
  lowerColor: '#4CAF50',
  upperWidth: 1.5,
  middleWidth: 1.5,
  lowerWidth: 1.5,
  upperLineStyle: 'solid',
  middleLineStyle: 'solid',
  lowerLineStyle: 'solid',
  bandBackgroundColor: 'rgba(33, 150, 243, 0.15)',
});

export const mergeIchimokuPersisted = (raw: unknown): MultiChartIchimokuState => {
  const d = defaultMultiChartIchimoku();
  if (!raw || typeof raw !== 'object') return d;
  const s = raw as Partial<MultiChartIchimokuState>;
  return { ...d, ...s };
};

export const mergeBollingerPersisted = (raw: unknown): MultiChartBollingerState => {
  const d = defaultMultiChartBollinger();
  if (!raw || typeof raw !== 'object') return d;
  const s = raw as Partial<MultiChartBollingerState>;
  return { ...d, ...s };
};

interface DashboardState {
  /** 멀티차트 그리드 (행×열). 예전 persist 필드명 `columnCount`는 migrate에서 읽기만 함 */
  gridLayout: MultiChartGridPreset;
  /** 신규 슬롯 id 발급용 */
  slotIdSerial: number;
  layout: LayoutConfig;
  /** react-grid-layout 잔여 필드 (보내기 호환). UI에서는 미사용 */
  layoutItems: Layout;
  slots: ChartSlot[];
  /** 사용자 모드에서 수동 선택한 슬롯 (차트유형 '사용자' 전환 시 복원) */
  userSlots: ChartSlot[] | null;
  timeframe: string;
  /** 슬롯별 시간봉 (없으면 상단 timeframe 사용) */
  slotTimeframes: Record<string, string>;
  /** 슬롯별 보조지표 설정 (slotId -> { volume: bool, macd: bool, ... }) */
  slotIndicatorSettings: Record<string, Record<string, boolean>>;
  /** 슬롯별 보조지표 표시 순서 (slotId -> [volume, macd, ...]) */
  slotIndicatorOrder: Record<string, string[]>;
  /** 슬롯별 캔들 영역 높이 비율 (0.3~0.9, 보조지표 있을 때) */
  slotCandleHeightRatio: Record<string, number>;
  chartType: ChartType;
  /** 멀티차트 공통 이동평균선(상단 메뉴·차트 카드) */
  multiChartMaLines: MALineConfig[];
  /** 멀티차트 공통 일목 표시 토글 */
  multiChartIchimoku: MultiChartIchimokuState;
  /** 멀티차트 공통 볼린저 표시·기간 */
  multiChartBollinger: MultiChartBollingerState;
  setMultiChartMaLines: (value: MALineConfig[] | ((prev: MALineConfig[]) => MALineConfig[])) => void;
  setMultiChartIchimoku: (
    value: MultiChartIchimokuState | ((prev: MultiChartIchimokuState) => MultiChartIchimokuState)
  ) => void;
  setMultiChartBollinger: (
    value: MultiChartBollingerState | ((prev: MultiChartBollingerState) => MultiChartBollingerState)
  ) => void;
  setGridLayout: (preset: MultiChartGridPreset) => void;
  /** 하위 호환: preset → 열 수로 매핑 */
  setLayout: (preset: LayoutPreset) => void;
  setLayoutItems: (items: Layout) => void;
  /** 1열 모드에서 하단 "차트 추가" 클릭 시 빈 칸 부착 */
  appendAddPlaceholderOneColumn: () => void;
  /** 1열 하단 드롭 존에 종목 놓을 때: 맨 아래에 차트 추가 */
  appendStockOneColumn: (stock: StockInfo) => void;
  setStockAtSlot: (slotIndex: number, stock: StockInfo | null) => void;
  setStockAtGridIndex: (gridIndex: number, stock: StockInfo | null) => void;
  swapSlots: (fromIndex: number, toIndex: number) => void;
  setTimeframe: (tf: string) => void;
  /** 슬롯별 시간봉 설정 (해당 슬롯만 변경) */
  setSlotTimeframe: (slotId: string, tf: string) => void;
  /** 상단 전체 시간봉 변경 시 모든 슬롯에 적용 */
  setAllTimeframes: (tf: string) => void;
  /** 슬롯별 보조지표 설정 */
  setSlotIndicatorSettings: (slotId: string, settings: Record<string, boolean>) => void;
  /** 슬롯별 보조지표 순서 변경 */
  setSlotIndicatorOrder: (slotId: string, order: string[]) => void;
  /** 슬롯별 캔들 높이 비율 설정 */
  setSlotCandleHeightRatio: (slotId: string, ratio: number) => void;
  setChartType: (type: ChartType) => void;
  clearSlot: (gridIndex: number) => void;
  /** 여러 슬롯을 한 번에 지정 (상승추세/거래량 자동 채우기용) */
  setSlotsFromStocks: (stocks: StockInfo[]) => void;
  /** 사용자 모드 슬롯 복원 */
  restoreUserSlots: () => void;
  /** 설정 내보내기 (JSON 문자열) */
  exportConfig: () => string;
  /** 설정 가져오기 (JSON 문자열 → 적용) */
  importConfig: (json: string) => boolean;
}

/** 내보내기/가져오기용 설정 타입 */
export interface DashboardConfigExport {
  version: number;
  gridLayout?: MultiChartGridPreset;
  /** v10 이하 */
  columnCount?: number;
  slotIdSerial?: number;
  layout: LayoutConfig;
  layoutItems: Layout;
  slots: ChartSlot[];
  userSlots: ChartSlot[] | null;
  timeframe: string;
  slotTimeframes: Record<string, string>;
  slotIndicatorSettings: Record<string, Record<string, boolean>>;
  slotIndicatorOrder: Record<string, string[]>;
  slotCandleHeightRatio: Record<string, number>;
  chartType: ChartType;
  multiChartMaLines?: MALineConfig[];
  multiChartIchimoku?: MultiChartIchimokuState;
  multiChartBollinger?: MultiChartBollingerState;
  /** 멀티차트2보내기 파일 호환 */
  multiChart2MaLines?: MALineConfig[];
  multiChart2Ichimoku?: MultiChartIchimokuState;
  multiChart2Bollinger?: MultiChartBollingerState;
  exportedAt: string;
}

const LAYOUT_MAP: Record<LayoutPreset, { rows: number; cols: number }> = {
  '1x1': { rows: 1, cols: 1 },
  '1x2': { rows: 1, cols: 2 },
  '1x3': { rows: 1, cols: 3 },
  '2x1': { rows: 2, cols: 1 },
  '2x2': { rows: 2, cols: 2 },
  '2x3': { rows: 2, cols: 3 },
  '3x1': { rows: 3, cols: 1 },
  '3x2': { rows: 3, cols: 2 },
  '3x3': { rows: 3, cols: 3 },
  '3x4': { rows: 3, cols: 4 },
  '4x3': { rows: 4, cols: 3 },
  '4x4': { rows: 4, cols: 4 },
};

const createEmptySlots = (count: number): ChartSlot[] =>
  Array.from({ length: count }, (_, i) => ({
    id: `slot-${i}`,
    stock: null,
    gridIndex: i,
  }));

/** 레이아웃 변경 시 기존 종목 정보 유지 (슬롯 수가 줄면 초과분 제거, 늘면 빈 슬롯 추가) */
const migrateSlots = (currentSlots: ChartSlot[], newCount: number): ChartSlot[] => {
  const result: ChartSlot[] = [];
  for (let i = 0; i < newCount; i++) {
    const existing = currentSlots[i];
    result.push(
      existing
        ? { ...existing, gridIndex: i }
        : { id: `slot-${i}`, stock: null, gridIndex: i }
    );
  }
  return result;
};

/** 슬롯 배열로부터 Layout 생성 */
const buildLayoutItems = (slots: ChartSlot[], rows: number, cols: number): Layout =>
  slots.slice(0, rows * cols).map((slot, i) => ({
    i: slot.id,
    x: i % cols,
    y: Math.floor(i / cols),
    w: 1,
    h: 1,
    minW: 1,
    minH: 1,
  }));

const INITIAL_GRID_LAYOUT: MultiChartGridPreset = '1x2';
const INITIAL_PRESET = INITIAL_GRID_LAYOUT as LayoutPreset;
const INITIAL_SLOT_SERIAL_START = 2;
const INITIAL_FLAT = packFlatForGridView([], INITIAL_GRID_LAYOUT);
const INITIAL_SLOTS: ChartSlot[] = INITIAL_FLAT.map((stock, i) => ({
  id: `mc-init-${i}`,
  gridIndex: i,
  stock,
}));
const INITIAL_LAYOUT: LayoutConfig = {
  rows: LAYOUT_MAP[INITIAL_GRID_LAYOUT].rows,
  cols: LAYOUT_MAP[INITIAL_GRID_LAYOUT].cols,
  preset: INITIAL_PRESET,
};

const makeDashboardStore = (persistName: string) =>
  create<DashboardState>()(
  persist(
    (set, get) => {
      const bumpLayout = (slots: ChartSlot[], preset: MultiChartGridPreset) => {
        const { rows, cols } = LAYOUT_MAP[preset];
        return {
          layout: { rows, cols, preset } as LayoutConfig,
          layoutItems: [] as Layout,
        };
      };

      return {
      gridLayout: INITIAL_GRID_LAYOUT,
      slotIdSerial: INITIAL_SLOT_SERIAL_START,
      layout: INITIAL_LAYOUT,
      layoutItems: [] as Layout,
      slots: INITIAL_SLOTS,
      userSlots: null,
      timeframe: '1m',
      slotTimeframes: {},
      slotIndicatorSettings: {},
      slotIndicatorOrder: {},
      slotCandleHeightRatio: {},
      chartType: 'user' as ChartType,
      multiChartMaLines: defaultMALines.map((l) => ({ ...l })),
      multiChartIchimoku: defaultMultiChartIchimoku(),
      multiChartBollinger: defaultMultiChartBollinger(),

      setMultiChartMaLines: (value) =>
        set((state) => ({
          multiChartMaLines: typeof value === 'function' ? (value as (p: MALineConfig[]) => MALineConfig[])(state.multiChartMaLines) : value,
        })),

      setMultiChartIchimoku: (value) =>
        set((state) => ({
          multiChartIchimoku:
            typeof value === 'function'
              ? (value as (p: MultiChartIchimokuState) => MultiChartIchimokuState)(state.multiChartIchimoku)
              : { ...state.multiChartIchimoku, ...value },
        })),

      setMultiChartBollinger: (value) =>
        set((state) => ({
          multiChartBollinger:
            typeof value === 'function'
              ? (value as (p: MultiChartBollingerState) => MultiChartBollingerState)(state.multiChartBollinger)
              : { ...state.multiChartBollinger, ...value },
        })),

      setSlotCandleHeightRatio: (slotId, ratio) =>
        set((state) => ({
          slotCandleHeightRatio: { ...state.slotCandleHeightRatio, [slotId]: Math.max(0.3, Math.min(0.9, ratio)) },
        })),

      setSlotIndicatorOrder: (slotId, order) =>
        set((state) => ({
          slotIndicatorOrder: { ...state.slotIndicatorOrder, [slotId]: order },
        })),

      setSlotIndicatorSettings: (slotId, settings) =>
        set((state) => ({
          slotIndicatorSettings: { ...state.slotIndicatorSettings, [slotId]: settings },
        })),

      setLayoutItems: (items) => set({ layoutItems: items }),

      setSlotTimeframe: (slotId, tf) =>
        set((state) => ({
          slotTimeframes: { ...state.slotTimeframes, [slotId]: tf },
        })),

      setAllTimeframes: (tf) =>
        set((state) => {
          const next: Record<string, string> = {};
          state.slots.forEach((s) => {
            next[s.id] = tf;
          });
          return { timeframe: tf, slotTimeframes: next };
        }),

      setChartType: (chartType) => set({ chartType }),

      appendAddPlaceholderOneColumn: () => set({}),

      appendStockOneColumn: (stock) =>
        set((state) => {
          const preset = state.gridLayout;
          const sorted = sortSlotsByGridIndex(state.slots);
          let flat = sorted.map((s) => s.stock);
          const slotN = presetSlotCount(preset);
          while (flat.length < slotN) flat.push(null);
          flat = flat.slice(0, slotN);
          const stockCount = flat.filter((x): x is StockInfo => x != null).length;
          if (stockCount >= MAX_MULTI_CHART_COUNT) return state;
          const nullIdx = flat.findIndex((x) => x == null);
          if (nullIdx < 0) return state;
          const nextFlat = [...flat];
          nextFlat[nullIdx] = stock;
          let serial = state.slotIdSerial;
          const nextId = () => {
            serial += 1;
            return `mc-${serial}`;
          };
          const newSlots = flatToSlots(nextFlat, state.slots, nextId);
          const meta = bumpLayout(newSlots, preset);

          const newSlotTimeframes = { ...state.slotTimeframes };
          const timeframeSequence = ['3m', '5m', '10m', '15m', '30m', '1h', '4h', '1d', '1m'];
          newSlots.forEach((slot, index) => {
            if (slot.stock && !newSlotTimeframes[slot.id]) {
              newSlotTimeframes[slot.id] = timeframeSequence[index % timeframeSequence.length];
            }
          });

          const u: Record<string, unknown> = {
            slots: newSlots,
            slotIdSerial: serial,
            slotTimeframes: newSlotTimeframes,
            ...meta,
          };
          if (state.chartType === 'user') u.userSlots = newSlots;
          return u as Partial<DashboardState>;
        }),

      setGridLayout: (preset) =>
        set((state) => {
          const oldPreset = state.gridLayout;
          const sorted = sortSlotsByGridIndex(state.slots);
          const flat = sorted.map((s) => s.stock);
          const repacked = repackFlatForGridModeChange(flat, oldPreset, preset);
          let serial = state.slotIdSerial;
          const nextId = () => {
            serial += 1;
            return `mc-${serial}`;
          };
          const newSlots = flatToSlots(repacked, state.slots, nextId);
          const meta = bumpLayout(newSlots, preset);
          const update: Record<string, unknown> = {
            gridLayout: preset,
            slots: newSlots,
            slotIdSerial: serial,
            ...meta,
          };
          if (state.chartType === 'user') update.userSlots = newSlots;
          return update as Partial<DashboardState>;
        }),

      setSlotsFromStocks: (stocks) =>
        set((state) => {
          const preset = state.gridLayout;
          const capped = stocks.slice(0, MAX_MULTI_CHART_COUNT);
          const flat = packFlatForGridView(capped, preset);
          let serial = state.slotIdSerial;
          const nextId = () => {
            serial += 1;
            return `mc-${serial}`;
          };
          const newSlots = flatToSlots(flat, [], nextId);
          const meta = bumpLayout(newSlots, preset);
          
          // ✅ 각 슬롯에 위치별 기본 시간봉 자동 할당
          const newSlotTimeframes: Record<string, string> = {};
          const timeframeSequence = ['3m', '5m', '10m', '15m', '30m', '1h', '4h', '1d', '1m'];
          newSlots.forEach((slot, index) => {
            if (slot.stock) {
              newSlotTimeframes[slot.id] = timeframeSequence[index % timeframeSequence.length];
            }
          });
          
          return { 
            slots: newSlots, 
            slotIdSerial: serial, 
            slotTimeframes: newSlotTimeframes,  // ✅ 위치별 시간봉 설정
            ...meta, 
            layoutItems: [] as Layout 
          };
        }),

      restoreUserSlots: () =>
        set((state) => {
          const preset = state.gridLayout;
          let saved = state.userSlots;
          let serial = state.slotIdSerial;
          const nextId = () => {
            serial += 1;
            return `mc-${serial}`;
          };
          // userSlots가 비어 있어도 persist된 slots에 종목이 있으면 그걸 사용자 레이아웃으로 복원 (복귀·새로고침 후 빈 화면 방지)
          if ((!saved || saved.length === 0) && countStocksInSlots(state.slots) > 0) {
            saved = state.slots;
          }
          if (!saved || saved.length === 0) {
            const emptyFlat = packFlatForGridView([], preset);
            const emptySlots = flatToSlots(emptyFlat, [], nextId);
            const meta = bumpLayout(emptySlots, preset);
            return { slots: emptySlots, slotIdSerial: serial, ...meta, userSlots: emptySlots };
          }
          const packed = clampSlotStocksToMax(saved, preset, MAX_MULTI_CHART_COUNT);
          const newSlots = flatToSlots(packed, saved, nextId);
          const meta = bumpLayout(newSlots, preset);
          return { slots: newSlots, slotIdSerial: serial, ...meta, userSlots: newSlots };
        }),

      setLayout: (preset) => {
        if (isMultiChartGridPreset(preset)) {
          get().setGridLayout(preset);
          return;
        }
        const { rows, cols } = LAYOUT_MAP[preset];
        const slotCount = rows * cols;
        const fallback: MultiChartGridPreset =
          slotCount <= 1 ? '1x1' : slotCount <= 2 ? '1x2' : slotCount <= 3 ? '1x3' : slotCount <= 4 ? '2x2' : slotCount <= 6 ? '2x3' : '3x3';
        get().setGridLayout(fallback);
      },

      setStockAtSlot: (slotIndex, stock) =>
        set((state) => {
          const sorted = sortSlotsByGridIndex(state.slots);
          if (slotIndex < 0 || slotIndex >= sorted.length) return state;
          const wasEmpty = sorted[slotIndex].stock == null;
          if (wasEmpty && stock != null && countStocksInSlots(state.slots) >= MAX_MULTI_CHART_COUNT) return state;
          const next = [...sorted];
          next[slotIndex] = { ...next[slotIndex], stock };
          let serial = state.slotIdSerial;
          const nextId = () => {
            serial += 1;
            return `mc-${serial}`;
          };
          const newSlots = slotsAfterStockChange(next, state.gridLayout, nextId);
          const meta = bumpLayout(newSlots, state.gridLayout);
          const u: Record<string, unknown> = { slots: newSlots, slotIdSerial: serial, ...meta };
          if (state.chartType === 'user') u.userSlots = newSlots;
          return u as Partial<DashboardState>;
        }),

      setStockAtGridIndex: (gridIndex, stock) =>
        set((state) => {
          const idx = state.slots.findIndex((s) => s.gridIndex === gridIndex);
          if (idx < 0) return state;
          const wasEmpty = state.slots[idx].stock == null;
          if (wasEmpty && stock != null && countStocksInSlots(state.slots) >= MAX_MULTI_CHART_COUNT) return state;
          const next = [...state.slots];
          next[idx] = { ...next[idx], stock };
          let serial = state.slotIdSerial;
          const nextId = () => {
            serial += 1;
            return `mc-${serial}`;
          };
          const newSlots = slotsAfterStockChange(next, state.gridLayout, nextId);
          const meta = bumpLayout(newSlots, state.gridLayout);

          // ✅ 종목이 새로 설정될 때 해당 슬롯에 위치별 기본 시간봉 자동 할당
          const newSlotTimeframes = { ...state.slotTimeframes };
          if (stock) {
            const timeframeSequence = ['3m', '5m', '10m', '15m', '30m', '1h', '4h', '1d', '1m'];
            const slotId = next[idx].id;
            // gridIndex 기준으로 시간봉 할당
            newSlotTimeframes[slotId] = timeframeSequence[gridIndex % timeframeSequence.length];
          }
          
          const update: Record<string, unknown> = { 
            slots: newSlots, 
            slotIdSerial: serial, 
            slotTimeframes: newSlotTimeframes,  // ✅ 위치별 시간봉 설정
            ...meta 
          };
          if (state.chartType === 'user') update.userSlots = newSlots;
          return update as Partial<DashboardState>;
        }),

      swapSlots: (fromIndex, toIndex) =>
        set((state) => {
          const sorted = sortSlotsByGridIndex(state.slots);
          if (fromIndex < 0 || toIndex < 0 || fromIndex >= sorted.length || toIndex >= sorted.length)
            return state;
          const next = [...sorted];
          const fromStock = next[fromIndex].stock;
          const toStock = next[toIndex].stock;
          next[fromIndex] = { ...next[fromIndex], stock: toStock };
          next[toIndex] = { ...next[toIndex], stock: fromStock };
          let serial = state.slotIdSerial;
          const nextId = () => {
            serial += 1;
            return `mc-${serial}`;
          };
          const newSlots = slotsAfterStockChange(next, state.gridLayout, nextId);
          const meta = bumpLayout(newSlots, state.gridLayout);
          const u: Record<string, unknown> = { slots: newSlots, slotIdSerial: serial, ...meta };
          if (state.chartType === 'user') u.userSlots = newSlots;
          return u as Partial<DashboardState>;
        }),

      setTimeframe: (timeframe) =>
        set((state) => {
          const next: Record<string, string> = {};
          state.slots.forEach((s) => {
            next[s.id] = timeframe;
          });
          return { timeframe, slotTimeframes: next };
        }),

      clearSlot: (gridIndex) =>
        set((state) => {
          const preset = state.gridLayout;
          let serial = state.slotIdSerial;
          const nextId = () => {
            serial += 1;
            return `mc-${serial}`;
          };
          const target = state.slots.find((s) => s.gridIndex === gridIndex);
          if (!target) return state;
          const idx = state.slots.findIndex((s) => s.gridIndex === gridIndex);
          if (idx < 0) return state;
          const next = [...state.slots];
          next[idx] = { ...next[idx], stock: null };
          const trimmed = trimTrailingEmptyRows(next, preset, nextId);
          const meta = bumpLayout(trimmed, preset);
          const u: Record<string, unknown> = { slots: trimmed, slotIdSerial: serial, ...meta };
          if (state.chartType === 'user') u.userSlots = trimmed;
          return u as Partial<DashboardState>;
        }),

      exportConfig: () => {
        const state = get();
        const config: DashboardConfigExport = {
          version: 11,
          gridLayout: state.gridLayout,
          slotIdSerial: state.slotIdSerial,
          layout: state.layout,
          layoutItems: state.layoutItems,
          slots: state.slots,
          userSlots: state.userSlots,
          timeframe: state.timeframe,
          slotTimeframes: state.slotTimeframes,
          slotIndicatorSettings: state.slotIndicatorSettings,
          slotIndicatorOrder: state.slotIndicatorOrder,
          slotCandleHeightRatio: state.slotCandleHeightRatio,
          chartType: state.chartType,
          multiChartMaLines: state.multiChartMaLines,
          multiChartIchimoku: state.multiChartIchimoku,
          multiChartBollinger: state.multiChartBollinger,
          exportedAt: new Date().toISOString(),
        };
        return JSON.stringify(config, null, 2);
      },

      importConfig: (json: string) => {
        try {
          const parsed = JSON.parse(json) as Partial<DashboardConfigExport> & Record<string, unknown>;
          if (!parsed || typeof parsed !== 'object') return false;
          const rawLayout = parsed.gridLayout ?? parsed.columnCount ?? parsed.layout?.preset ?? parsed.layout?.cols ?? '1x2';
          const preset: MultiChartGridPreset =
            typeof rawLayout === 'string' && isMultiChartGridPreset(rawLayout)
              ? rawLayout
              : normalizeMultiChartGridPreset(rawLayout);
          const lm = LAYOUT_MAP[preset];
          let serial = parsed.slotIdSerial ?? INITIAL_SLOT_SERIAL_START;
          const nextId = () => {
            serial += 1;
            return `mc-${serial}`;
          };
          const rawSlots = parsed.slots ?? INITIAL_SLOTS;
          const packed = clampSlotStocksToMax(rawSlots, preset, MAX_MULTI_CHART_COUNT);
          const slotsNorm = flatToSlots(packed, rawSlots, nextId);
          const cur = get();
          const maFromFile =
            (Array.isArray(parsed.multiChartMaLines) ? parsed.multiChartMaLines : null) ??
            (Array.isArray(parsed.multiChart2MaLines) ? parsed.multiChart2MaLines : null);
          const ichFromFile = (parsed.multiChartIchimoku ?? parsed.multiChart2Ichimoku) as Record<string, unknown> | undefined;
          const bolFromFile = (parsed.multiChartBollinger ?? parsed.multiChart2Bollinger) as Record<string, unknown> | undefined;
          set({
            gridLayout: preset,
            slotIdSerial: serial,
            layout: { rows: lm.rows, cols: lm.cols, preset },
            layoutItems: [] as Layout,
            slots: slotsNorm,
            userSlots: parsed.userSlots ?? null,
            timeframe: parsed.timeframe ?? '1m',
            slotTimeframes: parsed.slotTimeframes ?? {},
            slotIndicatorSettings: parsed.slotIndicatorSettings ?? {},
            slotIndicatorOrder: parsed.slotIndicatorOrder ?? {},
            slotCandleHeightRatio: parsed.slotCandleHeightRatio ?? {},
            chartType: (parsed.chartType as ChartType) ?? 'user',
            multiChartMaLines: maFromFile ?? cur.multiChartMaLines,
            multiChartIchimoku: ichFromFile && typeof ichFromFile === 'object' ? mergeIchimokuPersisted(ichFromFile) : cur.multiChartIchimoku,
            multiChartBollinger: bolFromFile && typeof bolFromFile === 'object' ? mergeBollingerPersisted(bolFromFile) : cur.multiChartBollinger,
          });
          return true;
        } catch {
          return false;
        }
      },
    };
    },
    {
      name: persistName,
      version: 11,
      /** 저장할 설정 항목만 명시 (액션 함수 제외) - localStorage에 JSON 형태로 저장 */
      partialize: (state) => ({
        gridLayout: state.gridLayout,
        slotIdSerial: state.slotIdSerial,
        layout: state.layout,
        layoutItems: state.layoutItems,
        slots: state.slots,
        userSlots: state.userSlots,
        timeframe: state.timeframe,
        slotTimeframes: state.slotTimeframes,
        slotIndicatorSettings: state.slotIndicatorSettings,
        slotIndicatorOrder: state.slotIndicatorOrder,
        slotCandleHeightRatio: state.slotCandleHeightRatio,
        chartType: state.chartType,
        multiChartMaLines: state.multiChartMaLines,
        multiChartIchimoku: state.multiChartIchimoku,
        multiChartBollinger: state.multiChartBollinger,
      }),
      migrate: (persisted: unknown) => {
        const s = persisted as {
          gridLayout?: string;
          columnCount?: number;
          slotIdSerial?: number;
          layout?: LayoutConfig;
          slots?: ChartSlot[];
          layoutItems?: Layout[];
          chartType?: ChartType;
          userSlots?: ChartSlot[] | null;
          slotTimeframes?: Record<string, string>;
          slotIndicatorSettings?: Record<string, Record<string, boolean>>;
          slotIndicatorOrder?: Record<string, string[]>;
          slotCandleHeightRatio?: Record<string, number>;
          multiChartMaLines?: MALineConfig[];
          multiChartIchimoku?: MultiChartIchimokuState;
          multiChartBollinger?: MultiChartBollingerState;
          [k: string]: unknown;
        };
        if (!s) return undefined;
        const rawLayout = s.gridLayout ?? s.columnCount ?? s.layout?.preset ?? s.layout?.cols ?? '1x2';
        const gridLayout: MultiChartGridPreset =
          typeof rawLayout === 'string' && isMultiChartGridPreset(rawLayout)
            ? rawLayout
            : normalizeMultiChartGridPreset(rawLayout);
        const lm = LAYOUT_MAP[gridLayout];
        let serial = typeof s.slotIdSerial === 'number' ? s.slotIdSerial : INITIAL_SLOT_SERIAL_START;
        const nextId = () => {
          serial += 1;
          return `mc-${serial}`;
        };
        const rawSlots = s.slots ?? INITIAL_SLOTS;
        const packed = clampSlotStocksToMax(rawSlots, gridLayout, MAX_MULTI_CHART_COUNT);
        const slotsNorm = flatToSlots(packed, rawSlots, nextId);
        const layoutItems = [] as Layout;
        let userSlots = s.userSlots ?? (s.chartType === 'user' && slotsNorm.length ? slotsNorm : null);
        if ((!userSlots || userSlots.length === 0) && s.chartType === 'user' && countStocksInSlots(slotsNorm) > 0) {
          userSlots = slotsNorm;
        }
        const slotTimeframes = s.slotTimeframes ?? {};
        const slotIndicatorSettings = s.slotIndicatorSettings ?? {};
        const slotIndicatorOrder = s.slotIndicatorOrder ?? {};
        const slotCandleHeightRatio = s.slotCandleHeightRatio ?? {};
        const multiChartMaLines =
          Array.isArray(s.multiChartMaLines) && s.multiChartMaLines.length > 0
            ? s.multiChartMaLines
            : defaultMALines.map((l) => ({ ...l }));
        const multiChartIchimoku =
          s.multiChartIchimoku && typeof s.multiChartIchimoku === 'object'
            ? mergeIchimokuPersisted(s.multiChartIchimoku)
            : defaultMultiChartIchimoku();
        const multiChartBollinger =
          s.multiChartBollinger && typeof s.multiChartBollinger === 'object'
            ? mergeBollingerPersisted(s.multiChartBollinger)
            : defaultMultiChartBollinger();
        const { columnCount: _legacyCol, ...rest } = s as typeof s & { columnCount?: number };
        return {
          ...rest,
          gridLayout,
          slotIdSerial: serial,
          layout: { rows: lm.rows, cols: lm.cols, preset: gridLayout as LayoutPreset },
          layoutItems,
          slots: slotsNorm,
          chartType: s.chartType ?? 'user',
          userSlots,
          slotTimeframes,
          slotIndicatorSettings,
          slotIndicatorOrder,
          slotCandleHeightRatio,
          multiChartMaLines,
          multiChartIchimoku,
          multiChartBollinger,
        };
      },
    }
  )
  );

/** 멀티차트 v1 */
export const useDashboardStore = makeDashboardStore('multi-chart-dashboard');

/** 멀티차트2 — persist 키 분리 (v1과 설정 충돌 방지) */
export const useDashboardStoreMC2 = makeDashboardStore('multi-chart-2-dashboard');
