/**
 * 화면 디자이너 정렬/배분/동일크기 유틸리티
 *
 * 다중 선택된 컴포넌트에 대해 정렬, 균등 배분, 동일 크기 맞추기 기능을 제공합니다.
 */

import { ComponentData } from "@/types/screen";

// 정렬 모드 타입
export type AlignMode = "left" | "centerX" | "right" | "top" | "centerY" | "bottom";

// 배분 방향 타입
export type DistributeDirection = "horizontal" | "vertical";

// 크기 맞추기 모드 타입
export type MatchSizeMode = "width" | "height" | "both";

/**
 * 컴포넌트 정렬
 * 선택된 컴포넌트들을 지정된 방향으로 정렬합니다.
 */
export function alignComponents(
  components: ComponentData[],
  selectedIds: string[],
  mode: AlignMode
): ComponentData[] {
  const selected = components.filter((c) => selectedIds.includes(c.id));
  if (selected.length < 2) return components;

  let targetValue: number;

  switch (mode) {
    case "left":
      // 가장 왼쪽 x값으로 정렬
      targetValue = Math.min(...selected.map((c) => c.position.x));
      return components.map((c) => {
        if (!selectedIds.includes(c.id)) return c;
        return { ...c, position: { ...c.position, x: targetValue } };
      });

    case "right":
      // 가장 오른쪽 (x + width)로 정렬
      targetValue = Math.max(...selected.map((c) => c.position.x + (c.size?.width || 100)));
      return components.map((c) => {
        if (!selectedIds.includes(c.id)) return c;
        const width = c.size?.width || 100;
        return { ...c, position: { ...c.position, x: targetValue - width } };
      });

    case "centerX":
      // 가로 중앙 정렬 (전체 범위의 중앙)
      {
        const minX = Math.min(...selected.map((c) => c.position.x));
        const maxX = Math.max(...selected.map((c) => c.position.x + (c.size?.width || 100)));
        const centerX = (minX + maxX) / 2;
        return components.map((c) => {
          if (!selectedIds.includes(c.id)) return c;
          const width = c.size?.width || 100;
          return { ...c, position: { ...c.position, x: Math.round(centerX - width / 2) } };
        });
      }

    case "top":
      // 가장 위쪽 y값으로 정렬
      targetValue = Math.min(...selected.map((c) => c.position.y));
      return components.map((c) => {
        if (!selectedIds.includes(c.id)) return c;
        return { ...c, position: { ...c.position, y: targetValue } };
      });

    case "bottom":
      // 가장 아래쪽 (y + height)로 정렬
      targetValue = Math.max(...selected.map((c) => c.position.y + (c.size?.height || 40)));
      return components.map((c) => {
        if (!selectedIds.includes(c.id)) return c;
        const height = c.size?.height || 40;
        return { ...c, position: { ...c.position, y: targetValue - height } };
      });

    case "centerY":
      // 세로 중앙 정렬 (전체 범위의 중앙)
      {
        const minY = Math.min(...selected.map((c) => c.position.y));
        const maxY = Math.max(...selected.map((c) => c.position.y + (c.size?.height || 40)));
        const centerY = (minY + maxY) / 2;
        return components.map((c) => {
          if (!selectedIds.includes(c.id)) return c;
          const height = c.size?.height || 40;
          return { ...c, position: { ...c.position, y: Math.round(centerY - height / 2) } };
        });
      }

    default:
      return components;
  }
}

/**
 * 컴포넌트 균등 배분
 * 선택된 컴포넌트들 간의 간격을 균등하게 분배합니다.
 * 최소 3개 이상의 컴포넌트가 필요합니다.
 */
export function distributeComponents(
  components: ComponentData[],
  selectedIds: string[],
  direction: DistributeDirection
): ComponentData[] {
  const selected = components.filter((c) => selectedIds.includes(c.id));
  if (selected.length < 3) return components;

  if (direction === "horizontal") {
    // x 기준 정렬
    const sorted = [...selected].sort((a, b) => a.position.x - b.position.x);
    const first = sorted[0];
    const last = sorted[sorted.length - 1];

    // 첫 번째 ~ 마지막 컴포넌트 사이의 총 공간
    const totalSpace = last.position.x + (last.size?.width || 100) - first.position.x;
    // 컴포넌트들이 차지하는 총 너비
    const totalComponentWidth = sorted.reduce((sum, c) => sum + (c.size?.width || 100), 0);
    // 균등 간격
    const gap = (totalSpace - totalComponentWidth) / (sorted.length - 1);

    // ID -> 새 x 좌표 매핑
    const newPositions = new Map<string, number>();
    let currentX = first.position.x;
    for (const comp of sorted) {
      newPositions.set(comp.id, Math.round(currentX));
      currentX += (comp.size?.width || 100) + gap;
    }

    return components.map((c) => {
      if (!selectedIds.includes(c.id)) return c;
      const newX = newPositions.get(c.id);
      if (newX === undefined) return c;
      return { ...c, position: { ...c.position, x: newX } };
    });
  } else {
    // y 기준 정렬
    const sorted = [...selected].sort((a, b) => a.position.y - b.position.y);
    const first = sorted[0];
    const last = sorted[sorted.length - 1];

    const totalSpace = last.position.y + (last.size?.height || 40) - first.position.y;
    const totalComponentHeight = sorted.reduce((sum, c) => sum + (c.size?.height || 40), 0);
    const gap = (totalSpace - totalComponentHeight) / (sorted.length - 1);

    const newPositions = new Map<string, number>();
    let currentY = first.position.y;
    for (const comp of sorted) {
      newPositions.set(comp.id, Math.round(currentY));
      currentY += (comp.size?.height || 40) + gap;
    }

    return components.map((c) => {
      if (!selectedIds.includes(c.id)) return c;
      const newY = newPositions.get(c.id);
      if (newY === undefined) return c;
      return { ...c, position: { ...c.position, y: newY } };
    });
  }
}

/**
 * 컴포넌트 동일 크기 맞추기
 * 선택된 컴포넌트들의 크기를 첫 번째 선택된 컴포넌트 기준으로 맞춥니다.
 */
export function matchComponentSize(
  components: ComponentData[],
  selectedIds: string[],
  mode: MatchSizeMode,
  referenceId?: string
): ComponentData[] {
  const selected = components.filter((c) => selectedIds.includes(c.id));
  if (selected.length < 2) return components;

  // 기준 컴포넌트 (지정하지 않으면 첫 번째 선택된 컴포넌트)
  const reference = referenceId
    ? selected.find((c) => c.id === referenceId) || selected[0]
    : selected[0];

  const refWidth = reference.size?.width || 100;
  const refHeight = reference.size?.height || 40;

  return components.map((c) => {
    if (!selectedIds.includes(c.id)) return c;

    const currentWidth = c.size?.width || 100;
    const currentHeight = c.size?.height || 40;

    let newWidth = currentWidth;
    let newHeight = currentHeight;

    if (mode === "width" || mode === "both") {
      newWidth = refWidth;
    }
    if (mode === "height" || mode === "both") {
      newHeight = refHeight;
    }

    return {
      ...c,
      size: {
        ...c.size,
        width: newWidth,
        height: newHeight,
      },
    };
  });
}

/**
 * 컴포넌트 라벨 일괄 토글
 * 모든 컴포넌트의 라벨 표시/숨기기를 토글합니다.
 * 숨겨진 라벨이 하나라도 있으면 모두 표시, 모두 표시되어 있으면 모두 숨기기
 */
/**
 * 라벨 토글 대상 타입 판별
 * label 속성이 있고, style.labelDisplay를 지원하는 컴포넌트인지 확인
 */
function hasLabelSupport(component: ComponentData): boolean {
  // 라벨이 없는 컴포넌트는 제외
  if (!component.label) return false;

  // 그룹, datatable 등은 라벨 토글 대상에서 제외
  const excludedTypes = ["group", "datatable"];
  if (excludedTypes.includes(component.type)) return false;

  // 나머지 (widget, component, container, file, flow 등)는 대상
  return true;
}

/**
 * @param components - 전체 컴포넌트 배열
 * @param selectedIds - 선택된 컴포넌트 ID 목록 (빈 배열이면 전체 대상)
 * @param forceShow - 강제 표시/숨기기 (지정하지 않으면 자동 토글)
 */
export function toggleAllLabels(
  components: ComponentData[],
  selectedIds: string[] = [],
  forceShow?: boolean
): ComponentData[] {
  // 대상 컴포넌트 필터: selectedIds가 있으면 선택된 것만, 없으면 전체
  const targetComponents = components.filter((c) => {
    if (!hasLabelSupport(c)) return false;
    if (selectedIds.length > 0) return selectedIds.includes(c.id);
    return true;
  });

  // 대상 중 라벨이 숨겨진 컴포넌트가 있는지 확인
  const hasHiddenLabel = targetComponents.some(
    (c) => (c.style as any)?.labelDisplay === false
  );

  // forceShow가 지정되면 그 값 사용, 아니면 자동 판단
  const shouldShow = forceShow !== undefined ? forceShow : hasHiddenLabel;

  // 대상 ID Set (빠른 조회용)
  const targetIdSet = new Set(targetComponents.map((c) => c.id));

  return components.map((c) => {
    // 대상이 아닌 컴포넌트는 건드리지 않음
    if (!targetIdSet.has(c.id)) return c;

    return {
      ...c,
      style: {
        ...(c.style || {}),
        labelDisplay: shouldShow,
      } as any,
    };
  });
}

/**
 * 컴포넌트 nudge (화살표 키 이동)
 * 선택된 컴포넌트를 지정 방향으로 이동합니다.
 */
export function nudgeComponents(
  components: ComponentData[],
  selectedIds: string[],
  direction: "up" | "down" | "left" | "right",
  distance: number = 1 // 기본 1px, Shift 누르면 10px
): ComponentData[] {
  const dx = direction === "left" ? -distance : direction === "right" ? distance : 0;
  const dy = direction === "up" ? -distance : direction === "down" ? distance : 0;

  return components.map((c) => {
    if (!selectedIds.includes(c.id)) return c;
    return {
      ...c,
      position: {
        ...c.position,
        x: Math.max(0, c.position.x + dx),
        y: Math.max(0, c.position.y + dy),
      },
    };
  });
}
