import type { ChartSlot, StockInfo } from '../stores/useDashboardStore';

/** 멀티차트 / 알림목록 우측 그리드에 둘 수 있는 종목(차트) 상한 */
export const MAX_MULTI_CHART_COUNT = 10;

/** 행×열 (예: 2×3 = 2행 3열) — 드롭다운·persist에 사용 */
export const MULTI_CHART_GRID_PRESETS = [
  '1x1',
  '1x2',
  '1x3',
  '2x1',
  '2x2',
  '2x3',
  '3x1',
  '3x2',
  '3x3',
] as const;

export type MultiChartGridPreset = (typeof MULTI_CHART_GRID_PRESETS)[number];

/** 하위 호환 */
export type GridViewMode = MultiChartGridPreset;
export type ColumnCount = MultiChartGridPreset;

const PRESET_ORDER = new Set<string>(MULTI_CHART_GRID_PRESETS);

export function isMultiChartGridPreset(s: string): s is MultiChartGridPreset {
  return PRESET_ORDER.has(s);
}

export function presetSlotCount(p: MultiChartGridPreset): number {
  const [r, c] = p.split('x').map(Number);
  return r * c;
}

export function gridRowsForPreset(p: MultiChartGridPreset): number {
  const [r] = p.split('x').map(Number);
  return r;
}

export function gridColsForPreset(p: MultiChartGridPreset): number {
  const [, c] = p.split('x').map(Number);
  return c;
}

/** @deprecated `gridColsForPreset` 사용 */
export const gridColsForMode = gridColsForPreset;

/** 예전 숫자 columnCount(1|2|4|6|9) 또는 열 수 → 프리셋 */
export function migrateLegacyColumnCount(raw: unknown): MultiChartGridPreset {
  const n = Number(raw);
  // 예전 1열(세로 다종목)은 1x1이면 종목 대부분 유실 → 3x3으로 최대 9칸까지 보존
  if (n === 1) return '3x3';
  if (n === 2) return '1x2';
  if (n === 4) return '2x2';
  if (n === 6) return '2x3';
  if (n === 9) return '3x3';
  if (n === 3) return '3x3';
  return '1x2';
}

export function normalizeMultiChartGridPreset(raw: unknown): MultiChartGridPreset {
  if (typeof raw === 'string' && isMultiChartGridPreset(raw)) return raw;
  if (typeof raw === 'number') return migrateLegacyColumnCount(raw);
  return '1x2';
}

/** @deprecated `normalizeMultiChartGridPreset` 사용 */
export function normalizeGridViewMode(n: number): MultiChartGridPreset {
  return migrateLegacyColumnCount(n);
}

export function sortSlotsByGridIndex(slots: ChartSlot[]): ChartSlot[] {
  return [...slots].sort((a, b) => a.gridIndex - b.gridIndex);
}

export function flatStocksRowMajor(slots: ChartSlot[]): (StockInfo | null)[] {
  return sortSlotsByGridIndex(slots).map((s) => s.stock);
}

export function stocksInGridOrder(slots: ChartSlot[]): StockInfo[] {
  const r: StockInfo[] = [];
  for (const s of sortSlotsByGridIndex(slots)) {
    if (s.stock) r.push(s.stock);
  }
  return r;
}

/**
 * 고정 그리드: `preset`의 칸 수만큼 슬롯을 채우고 부족분은 null.
 */
export function packFlatForGridView(stocks: StockInfo[], preset: MultiChartGridPreset): (StockInfo | null)[] {
  const n = presetSlotCount(preset);
  const out: (StockInfo | null)[] = stocks.slice(0, n);
  while (out.length < n) out.push(null);
  return out;
}

/**
 * 2·3열(레거시): 마지막 행을 cols로 맞추고, 마지막 행이 모두 종목이면 빈 행 한 줄 추가.
 * 1열: 종목만 나열. 단, 맨 끝 null 한 칸은 "차트 추가" 플레이스홀더로 유지.
 */
export function packFlatForColumns(flat: (StockInfo | null)[], cols: number): (StockInfo | null)[] {
  if (cols === 1) {
    const stocks = flat.filter((x): x is StockInfo => x != null);
    const trailingPlaceholder = flat.length > 0 && flat[flat.length - 1] == null;
    if (trailingPlaceholder) return [...stocks, null];
    return stocks;
  }
  let out = [...flat];
  while (out.length > 0 && out[out.length - 1] == null) out.pop();
  while (out.length % cols !== 0) out.push(null);
  if (out.length === 0) out = Array(cols).fill(null);
  const last = out.slice(-cols);
  if (last.length === cols && last.every((x) => x != null)) {
    out = [...out, ...Array(cols).fill(null)];
  }
  return out;
}

export function countStocksInSlots(slots: ChartSlot[]): number {
  return flatStocksRowMajor(slots).filter((s): s is StockInfo => s != null).length;
}

/** 저장/가져오기 등: 종목 수를 상한으로 자르고 패킹만 다시 계산 */
export function clampSlotStocksToMax(
  slots: ChartSlot[],
  preset: MultiChartGridPreset,
  maxCount: number = MAX_MULTI_CHART_COUNT
): (StockInfo | null)[] {
  const sorted = sortSlotsByGridIndex(slots);
  const stocks = sorted.map((s) => s.stock).filter((x): x is StockInfo => x != null).slice(0, maxCount);
  return packFlatForGridView(stocks, preset);
}

/** 레거시: 숫자 열 수만 알 때(가져오기 등) */
export function repackFlatForColumnChange(
  flat: (StockInfo | null)[],
  oldCols: number,
  newCols: number
): (StockInfo | null)[] {
  const rows = Math.max(1, Math.ceil(flat.length / oldCols));
  const seq: (StockInfo | null)[] = [];
  for (let r = 0; r < rows; r++) {
    for (let c = 0; c < oldCols; c++) {
      const idx = r * oldCols + c;
      if (idx < flat.length) seq.push(flat[idx]);
    }
  }
  while (seq.length && seq[seq.length - 1] == null) seq.pop();
  if (newCols === 1) {
    const stocks = seq.filter((x): x is StockInfo => x != null);
    return stocks;
  }
  return packFlatForColumns(seq, newCols);
}

/** 프리셋 전환 시 flat(행 우선, null 포함)을 새 그리드에 맞게 재배치 */
export function repackFlatForGridModeChange(
  flat: (StockInfo | null)[],
  oldPreset: MultiChartGridPreset,
  newPreset: MultiChartGridPreset
): (StockInfo | null)[] {
  const seq: StockInfo[] = [];
  const oldCols = gridColsForPreset(oldPreset);
  const oldLen = Math.max(flat.length, presetSlotCount(oldPreset));
  const rows = Math.max(1, Math.ceil(oldLen / oldCols));
  for (let r = 0; r < rows; r++) {
    for (let c = 0; c < oldCols; c++) {
      const idx = r * oldCols + c;
      if (idx < flat.length && flat[idx]) seq.push(flat[idx]!);
    }
  }
  return packFlatForGridView(seq.slice(0, MAX_MULTI_CHART_COUNT), newPreset);
}

export function flatToSlots(flat: (StockInfo | null)[], prevSlots: ChartSlot[], nextId: () => string): ChartSlot[] {
  const sortedPrev = sortSlotsByGridIndex(prevSlots);
  return flat.map((stock, i) => {
    const prev = sortedPrev.find((p) => p.gridIndex === i);
    if (prev) return { ...prev, gridIndex: i, stock };
    return { id: nextId(), gridIndex: i, stock };
  });
}

export function slotsAfterStockChange(slots: ChartSlot[], preset: MultiChartGridPreset, nextId: () => string): ChartSlot[] {
  const sorted = sortSlotsByGridIndex(slots);
  const stocks = stocksInGridOrder(sorted);
  const packed = packFlatForGridView(stocks, preset);
  return flatToSlots(packed, sorted, nextId);
}

export function removeSlotAtGridIndexOneColumn(slots: ChartSlot[], gridIndex: number, nextId: () => string): ChartSlot[] {
  const sorted = sortSlotsByGridIndex(slots);
  const idx = sorted.findIndex((s) => s.gridIndex === gridIndex);
  if (idx < 0) return sorted;
  const remaining = sorted.filter((_, i) => i !== idx);
  const flat = remaining.map((s) => s.stock);
  const packed = packFlatForColumns(flat, 1);
  return flatToSlots(packed, remaining, nextId);
}

export function trimTrailingEmptyRows(slots: ChartSlot[], preset: MultiChartGridPreset, nextId: () => string): ChartSlot[] {
  const stocks = stocksInGridOrder(slots);
  const flat = packFlatForGridView(stocks, preset);
  return flatToSlots(flat, sortSlotsByGridIndex(slots), nextId);
}

/** 1열: 맨 아래에 빈 슬롯(추가용) 한 칸 부착 */
export function appendTrailingPlaceholderOneColumn(slots: ChartSlot[], nextId: () => string): ChartSlot[] {
  const sorted = sortSlotsByGridIndex(slots);
  const flat = sorted.map((s) => s.stock);
  const packed = packFlatForColumns(flat, 1);
  if (packed.length > 0 && packed[packed.length - 1] == null) {
    return flatToSlots(packed, sorted, nextId);
  }
  const withNull = [...packed, null];
  return flatToSlots(withNull, sorted, nextId);
}
