/**
 * 🔄 Width를 컬럼 스팬으로 변환하는 마이그레이션 유틸리티
 *
 * 기존 픽셀 기반 width 값을 새로운 그리드 시스템의 컬럼 스팬으로 변환
 */

import { ColumnSpanPreset, COLUMN_SPAN_VALUES, getColumnSpanValue } from "@/lib/constants/columnSpans";
import { ComponentData, LayoutData } from "@/types/screen";

/**
 * 픽셀 width를 가장 가까운 ColumnSpanPreset으로 변환
 *
 * @param width 픽셀 너비
 * @param canvasWidth 캔버스 전체 너비 (기본: 1920px)
 * @returns 가장 가까운 컬럼 스팬 프리셋
 */
export function convertWidthToColumnSpan(width: number, canvasWidth: number = 1920): ColumnSpanPreset {
  if (width <= 0 || canvasWidth <= 0) {
    return "half"; // 기본값
  }

  const percentage = (width / canvasWidth) * 100;

  // 각 프리셋의 백분율 계산
  const presetPercentages: Array<[ColumnSpanPreset, number]> = [
    ["full", 100],
    ["threeQuarters", 75],
    ["twoThirds", 67],
    ["half", 50],
    ["third", 33],
    ["quarter", 25],
    ["label", 25],
    ["input", 75],
    ["small", 17],
    ["medium", 33],
    ["large", 67],
  ];

  // 가장 가까운 값 찾기
  let closestPreset: ColumnSpanPreset = "half";
  let minDiff = Infinity;

  for (const [preset, presetPercentage] of presetPercentages) {
    const diff = Math.abs(percentage - presetPercentage);
    if (diff < minDiff) {
      minDiff = diff;
      closestPreset = preset;
    }
  }

  return closestPreset;
}

/**
 * Y 좌표를 기준으로 행 인덱스 계산
 *
 * @param components 컴포넌트 배열
 * @param threshold 같은 행으로 간주할 Y 좌표 차이 (기본: 50px)
 * @returns 행 인덱스가 추가된 컴포넌트 배열
 */
export function calculateRowIndices(components: ComponentData[], threshold: number = 50): ComponentData[] {
  if (components.length === 0) return [];

  // Y 좌표로 정렬
  const sorted = [...components].sort((a, b) => a.position.y - b.position.y);

  let currentRowIndex = 0;
  let currentY = sorted[0]?.position.y ?? 0;

  return sorted.map((component) => {
    if (Math.abs(component.position.y - currentY) > threshold) {
      currentRowIndex++;
      currentY = component.position.y;
    }

    return {
      ...component,
      gridRowIndex: currentRowIndex,
    };
  });
}

/**
 * 같은 행 내에서 X 좌표로 시작 컬럼 계산
 *
 * @param components 컴포넌트 배열 (gridRowIndex 필요)
 * @returns 시작 컬럼이 추가된 컴포넌트 배열
 */
export function calculateColumnStarts(components: ComponentData[]): ComponentData[] {
  // 행별로 그룹화
  const rowGroups = new Map<number, ComponentData[]>();

  for (const component of components) {
    const rowIndex = component.gridRowIndex ?? 0;
    if (!rowGroups.has(rowIndex)) {
      rowGroups.set(rowIndex, []);
    }
    rowGroups.get(rowIndex)!.push(component);
  }

  // 각 행 내에서 X 좌표로 정렬하고 시작 컬럼 계산
  const result: ComponentData[] = [];

  for (const [rowIndex, rowComponents] of rowGroups) {
    // X 좌표로 정렬
    const sorted = rowComponents.sort((a, b) => a.position.x - b.position.x);

    let currentColumn = 1;

    for (const component of sorted) {
      const columnSpan = component.gridColumnSpan || "half";
      const spanValue = getColumnSpanValue(columnSpan);

      // 현재 컬럼이 12를 넘으면 다음 줄로 (실제로는 같은 행이지만 자동 줄바꿈)
      if (currentColumn + spanValue > 13) {
        currentColumn = 1;
      }

      result.push({
        ...component,
        gridColumnStart: currentColumn,
      });

      // 다음 컴포넌트는 현재 컴포넌트 뒤에 배치
      currentColumn += spanValue;
    }
  }

  return result;
}

/**
 * 컴포넌트 배열에서 width를 gridColumnSpan으로 일괄 변환
 *
 * @param components 컴포넌트 배열
 * @param canvasWidth 캔버스 너비 (기본: 1920px)
 * @returns gridColumnSpan이 추가된 컴포넌트 배열
 */
export function migrateComponentsToColumnSpan(
  components: ComponentData[],
  canvasWidth: number = 1920,
): ComponentData[] {
  return components.map((component) => {
    // 이미 gridColumnSpan이 있으면 유지
    if (component.gridColumnSpan) {
      return component;
    }

    // width를 컬럼 스팬으로 변환
    const gridColumnSpan = convertWidthToColumnSpan(component.size.width, canvasWidth);

    return {
      ...component,
      gridColumnSpan,
      gridRowIndex: component.gridRowIndex ?? 0, // 초기값
    };
  });
}

/**
 * 전체 레이아웃 마이그레이션
 *
 * @param layout 기존 레이아웃 데이터
 * @param canvasWidth 캔버스 너비 (기본: 1920px)
 * @returns 새로운 그리드 시스템으로 변환된 레이아웃
 */
export function migrateLayoutToGridSystem(layout: LayoutData, canvasWidth: number = 1920): LayoutData {
  // 1단계: width를 gridColumnSpan으로 변환
  let migratedComponents = migrateComponentsToColumnSpan(layout.components, canvasWidth);

  // 2단계: Y 좌표로 행 인덱스 계산
  migratedComponents = calculateRowIndices(migratedComponents);

  // 3단계: 같은 행 내에서 X 좌표로 gridColumnStart 계산
  migratedComponents = calculateColumnStarts(migratedComponents);

  return {
    ...layout,
    components: migratedComponents,
  };
}

/**
 * 단일 컴포넌트 마이그레이션
 *
 * @param component 기존 컴포넌트
 * @param canvasWidth 캔버스 너비
 * @returns 마이그레이션된 컴포넌트
 */
export function migrateComponent(component: ComponentData, canvasWidth: number = 1920): ComponentData {
  // 이미 그리드 속성이 있으면 그대로 반환
  if (component.gridColumnSpan && component.gridRowIndex !== undefined) {
    return component;
  }

  const gridColumnSpan = component.gridColumnSpan || convertWidthToColumnSpan(component.size.width, canvasWidth);

  return {
    ...component,
    gridColumnSpan,
    gridRowIndex: component.gridRowIndex ?? 0,
    gridColumnStart: component.gridColumnStart,
  };
}

/**
 * 마이그레이션 필요 여부 확인
 *
 * @param layout 레이아웃 데이터
 * @returns 마이그레이션 필요 여부
 */
export function needsMigration(layout: LayoutData): boolean {
  return layout.components.some((c) => !c.gridColumnSpan || c.gridRowIndex === undefined);
}

/**
 * 안전한 마이그레이션 (에러 처리 포함)
 *
 * @param layout 레이아웃 데이터
 * @param canvasWidth 캔버스 너비
 * @returns 마이그레이션된 레이아웃 또는 원본 (실패 시)
 */
export function safeMigrateLayout(layout: LayoutData, canvasWidth: number = 1920): LayoutData {
  try {
    if (!needsMigration(layout)) {
      return layout;
    }

    return migrateLayoutToGridSystem(layout, canvasWidth);
  } catch (error) {
    console.error("❌ 마이그레이션 실패:", error);
    console.warn("⚠️ 원본 레이아웃 반환 - 수동 확인 필요");
    return layout;
  }
}

/**
 * 백업 데이터 생성
 *
 * @param layout 레이아웃 데이터
 * @returns JSON 문자열
 */
export function createLayoutBackup(layout: LayoutData): string {
  return JSON.stringify(layout, null, 2);
}

/**
 * 백업에서 복원
 *
 * @param backupJson JSON 문자열
 * @returns 레이아웃 데이터
 */
export function restoreFromBackup(backupJson: string): LayoutData {
  return JSON.parse(backupJson);
}
