/**
 * V2/V2 컴포넌트 설정 스키마 및 병합 유틸리티
 *
 * V2 컴포넌트와 V2 컴포넌트의 overrides 스키마 및 기본값을 관리
 * - 저장: component_url + overrides (차이값만)
 * - 로드: 코드 기본값 + overrides 병합 (Zod)
 */
import { z } from "zod";

// ============================================
// 공통 스키마 (모든 구조 허용)
// ============================================
export const customConfigSchema = z.record(z.string(), z.any());

export type CustomConfig = z.infer<typeof customConfigSchema>;

// ============================================
// 깊은 병합 함수
// ============================================
export function deepMerge<T extends Record<string, any>>(target: T, source: Record<string, any>): T {
  const result = { ...target };

  for (const key of Object.keys(source)) {
    const sourceValue = source[key];
    const targetValue = result[key as keyof T];

    // 둘 다 객체이고 배열이 아니면 깊은 병합
    if (isPlainObject(sourceValue) && isPlainObject(targetValue)) {
      result[key as keyof T] = deepMerge(targetValue, sourceValue);
    } else if (sourceValue !== undefined) {
      // source 값이 있으면 덮어쓰기
      result[key as keyof T] = sourceValue;
    }
  }

  return result;
}

function isPlainObject(value: unknown): value is Record<string, any> {
  return (
    typeof value === "object" &&
    value !== null &&
    !Array.isArray(value) &&
    Object.prototype.toString.call(value) === "[object Object]"
  );
}

// ============================================
// 설정 병합 함수 (렌더링 시 사용)
// ============================================
export function mergeComponentConfig(
  defaultConfig: Record<string, any>,
  customConfig: Record<string, any> | null | undefined,
): Record<string, any> {
  if (!customConfig || Object.keys(customConfig).length === 0) {
    return { ...defaultConfig };
  }

  return deepMerge(defaultConfig, customConfig);
}

// ============================================
// 커스텀 설정 추출 함수 (저장 시 사용)
// ============================================
export function extractCustomConfig(
  fullConfig: Record<string, any>,
  defaultConfig: Record<string, any>,
): Record<string, any> {
  const customConfig: Record<string, any> = {};

  for (const key of Object.keys(fullConfig)) {
    const fullValue = fullConfig[key];
    const defaultValue = defaultConfig[key];

    // 기본값과 다른 경우만 커스텀으로 추출
    if (!isDeepEqual(fullValue, defaultValue)) {
      customConfig[key] = fullValue;
    }
  }

  return customConfig;
}

// ============================================
// 깊은 비교 함수
// ============================================
export function isDeepEqual(a: unknown, b: unknown): boolean {
  if (a === b) return true;
  if (a == null || b == null) return a === b;
  if (typeof a !== typeof b) return false;
  if (typeof a !== "object") return a === b;

  if (Array.isArray(a) !== Array.isArray(b)) return false;
  if (Array.isArray(a) && Array.isArray(b)) {
    if (a.length !== b.length) return false;
    for (let i = 0; i < a.length; i++) {
      if (!isDeepEqual(a[i], b[i])) return false;
    }
    return true;
  }

  const objA = a as Record<string, unknown>;
  const objB = b as Record<string, unknown>;
  const keysA = Object.keys(objA);
  const keysB = Object.keys(objB);

  if (keysA.length !== keysB.length) return false;

  for (const key of keysA) {
    if (!keysB.includes(key)) return false;
    if (!isDeepEqual(objA[key], objB[key])) return false;
  }

  return true;
}

// ============================================
// 컴포넌트 URL 생성 함수
// ============================================
export function getComponentUrl(componentType: string): string {
  return `@/lib/registry/components/${componentType}`;
}

// ============================================
// 컴포넌트 타입 추출 함수 (URL에서)
// ============================================
export function getComponentTypeFromUrl(componentUrl: string): string {
  // "@/lib/registry/components/v2-table-list" → "v2-table-list"
  const parts = componentUrl.split("/");
  return parts[parts.length - 1];
}

// ============================================
// V2 레이아웃 스키마
// ============================================
export const componentV2Schema = z.object({
  id: z.string(),
  url: z.string(),
  position: z.object({
    x: z.number().default(0),
    y: z.number().default(0),
  }),
  size: z.object({
    width: z.number().default(100),
    height: z.number().default(100),
  }),
  displayOrder: z.number().default(0),
  overrides: z.record(z.string(), z.any()).default({}),
});

// ============================================
// 레이어 스키마 정의
// ============================================
export const layerTypeSchema = z.enum(["base", "conditional", "modal", "drawer"]);

// 조건부 레이어 표시 영역 스키마
export const displayRegionSchema = z.object({
  x: z.number().default(0),
  y: z.number().default(0),
  width: z.number().default(800),
  height: z.number().default(200),
});

export const layerSchema = z.object({
  id: z.string(),
  name: z.string(),
  type: layerTypeSchema,
  zIndex: z.number().default(0),
  isVisible: z.boolean().default(true), // 초기 표시 여부
  isLocked: z.boolean().default(false), // 편집 잠금 여부

  // 조건부 표시 로직
  condition: z
    .object({
      targetComponentId: z.string(),
      operator: z.enum(["eq", "neq", "in"]),
      value: z.any(),
    })
    .optional(),

  // 조건부 레이어 표시 영역 (조건 미충족 시 사라짐)
  displayRegion: displayRegionSchema.optional(),

  // 모달/드로어 전용 설정
  overlayConfig: z
    .object({
      backdrop: z.boolean().default(true),
      closeOnBackdropClick: z.boolean().default(true),
      width: z.union([z.string(), z.number()]).optional(),
      height: z.union([z.string(), z.number()]).optional(),
      // 모달/드로어 스타일링
      backgroundColor: z.string().optional(),
      backdropBlur: z.number().optional(),
      // 드로어 전용
      position: z.enum(["left", "right", "top", "bottom"]).optional(),
    })
    .optional(),

  // 해당 레이어에 속한 컴포넌트들
  components: z.array(componentV2Schema).default([]),
});

export type Layer = z.infer<typeof layerSchema>;

export const layoutV2Schema = z.object({
  version: z.string().default("2.1"),
  layers: z.array(layerSchema).default([]), // 신규 필드
  components: z.array(componentV2Schema).default([]), // 하위 호환성 유지
  updatedAt: z.string().optional(),
  screenResolution: z
    .object({
      width: z.number().default(1920),
      height: z.number().default(1080),
    })
    .optional(),
  gridSettings: z.any().optional(),
});

export type ComponentV2 = z.infer<typeof componentV2Schema>;
export type LayoutV2 = z.infer<typeof layoutV2Schema>;

// ============================================
// V2 컴포넌트 overrides 스키마 정의
// ============================================

// v2-table-list
const v2TableListOverridesSchema = z
  .object({
    displayMode: z.enum(["table", "card"]).default("table"),
    showHeader: z.boolean().default(true),
    showFooter: z.boolean().default(true),
    height: z.string().default("auto"),
    checkbox: z
      .object({
        enabled: z.boolean().default(true),
        multiple: z.boolean().default(true),
        position: z.string().default("left"),
        selectAll: z.boolean().default(true),
      })
      .default({ enabled: true, multiple: true, position: "left", selectAll: true }),
    columns: z.array(z.any()).default([]),
    autoWidth: z.boolean().default(true),
    stickyHeader: z.boolean().default(false),
    pagination: z
      .object({
        enabled: z.boolean().default(true),
        pageSize: z.number().default(20),
        showSizeSelector: z.boolean().default(true),
        showPageInfo: z.boolean().default(true),
      })
      .default({ enabled: true, pageSize: 20, showSizeSelector: true, showPageInfo: true }),
    autoLoad: z.boolean().default(true),
  })
  .passthrough();

// v2-button-primary
const v2ButtonPrimaryOverridesSchema = z
  .object({
    text: z.string().default("저장"),
    actionType: z.string().default("button"),
    variant: z.string().default("primary"),
    action: z
      .object({
        type: z.string().default("save"),
        successMessage: z.string().optional(),
        errorMessage: z.string().optional(),
      })
      .optional(),
  })
  .passthrough();

// v2-text-display
const v2TextDisplayOverridesSchema = z
  .object({
    text: z.string().default("텍스트를 입력하세요"),
    fontSize: z.string().default("14px"),
    fontWeight: z.string().default("normal"),
    color: z.string().default("#212121"),
    textAlign: z.string().default("left"),
  })
  .passthrough();

// v2-split-panel-layout
const v2SplitPanelLayoutOverridesSchema = z
  .object({
    leftPanel: z
      .object({
        title: z.string().default("마스터"),
        showSearch: z.boolean().default(false),
        showAdd: z.boolean().default(false),
      })
      .default({ title: "마스터", showSearch: false, showAdd: false }),
    rightPanel: z
      .object({
        title: z.string().default("디테일"),
        showSearch: z.boolean().default(false),
        showAdd: z.boolean().default(false),
      })
      .default({ title: "디테일", showSearch: false, showAdd: false }),
    splitRatio: z.number().default(30),
    resizable: z.boolean().default(true),
    autoLoad: z.boolean().default(true),
    syncSelection: z.boolean().default(true),
  })
  .passthrough();

// v2-section-card
const v2SectionCardOverridesSchema = z
  .object({
    title: z.string().default("섹션 제목"),
    description: z.string().default(""),
    showHeader: z.boolean().default(true),
    padding: z.string().default("md"),
    backgroundColor: z.string().default("default"),
    borderStyle: z.string().default("solid"),
    collapsible: z.boolean().default(false),
    defaultOpen: z.boolean().default(true),
  })
  .passthrough();

// v2-section-paper
const v2SectionPaperOverridesSchema = z
  .object({
    backgroundColor: z.string().default("default"),
    padding: z.string().default("md"),
    roundedCorners: z.string().default("md"),
    shadow: z.string().default("none"),
    showBorder: z.boolean().default(false),
  })
  .passthrough();

// v2-divider-line
const v2DividerLineOverridesSchema = z
  .object({
    placeholder: z.string().default("텍스트를 입력하세요"),
    maxLength: z.number().default(255),
  })
  .passthrough();

// v2-repeat-container
const v2RepeatContainerOverridesSchema = z
  .object({
    dataSourceType: z.string().default("manual"),
    layout: z.string().default("vertical"),
    gridColumns: z.number().default(2),
    gap: z.string().default("16px"),
    showBorder: z.boolean().default(true),
    showShadow: z.boolean().default(false),
    emptyMessage: z.string().default("데이터가 없습니다"),
    usePaging: z.boolean().default(false),
    pageSize: z.number().default(10),
    clickable: z.boolean().default(false),
    selectionMode: z.string().default("single"),
  })
  .passthrough();

// v2-rack-structure
const v2RackStructureOverridesSchema = z
  .object({
    showPreview: z.boolean().default(true),
    showTemplate: z.boolean().default(true),
  })
  .passthrough();

// v2-numbering-rule
const v2NumberingRuleOverridesSchema = z
  .object({
    showPreview: z.boolean().default(true),
  })
  .passthrough();

// v2-category-manager
const v2CategoryManagerOverridesSchema = z
  .object({
    viewMode: z.string().default("tree"),
    maxDepth: z.number().default(3),
    showActions: z.boolean().default(true),
  })
  .passthrough();

// v2-pivot-grid
const v2PivotGridOverridesSchema = z
  .object({
    fields: z.array(z.any()).default([]),
    dataSource: z.any().optional(),
  })
  .passthrough();

// v2-location-swap-selector
const v2LocationSwapSelectorOverridesSchema = z
  .object({
    dataSource: z
      .object({
        type: z.string().default("static"),
        tableName: z.string().default(""),
        valueField: z.string().default("location_code"),
        labelField: z.string().default("location_name"),
      })
      .default({ type: "static", tableName: "", valueField: "location_code", labelField: "location_name" }),
    departureField: z.string().default("departure"),
    destinationField: z.string().default("destination"),
    departureLabel: z.string().default("출발지"),
    destinationLabel: z.string().default("도착지"),
    showSwapButton: z.boolean().default(true),
    variant: z.string().default("card"),
  })
  .passthrough();

// v2-aggregation-widget
const v2AggregationWidgetOverridesSchema = z
  .object({
    dataSourceType: z.string().default("table"),
    items: z.array(z.any()).default([]),
    filters: z.array(z.any()).default([]),
    filterLogic: z.string().default("AND"),
    layout: z.string().default("horizontal"),
    showLabels: z.boolean().default(true),
    showIcons: z.boolean().default(true),
    gap: z.string().default("16px"),
    autoRefresh: z.boolean().default(false),
    refreshOnFormChange: z.boolean().default(true),
  })
  .passthrough();

// v2-card-display
const v2CardDisplayOverridesSchema = z
  .object({
    cardsPerRow: z.number().default(3),
    cardSpacing: z.number().default(16),
    cardStyle: z
      .object({
        showTitle: z.boolean().default(true),
        showSubtitle: z.boolean().default(true),
        showDescription: z.boolean().default(true),
        showImage: z.boolean().default(false),
        showActions: z.boolean().default(true),
      })
      .default({ showTitle: true, showSubtitle: true, showDescription: true, showImage: false, showActions: true }),
    columnMapping: z.record(z.string(), z.any()).default({}),
    dataSource: z.string().default("table"),
    staticData: z.array(z.any()).default([]),
  })
  .passthrough();

// v2-table-search-widget
const v2TableSearchWidgetOverridesSchema = z
  .object({
    title: z.string().default("테이블 검색"),
    autoSelectFirstTable: z.boolean().default(true),
    showTableSelector: z.boolean().default(true),
  })
  .passthrough();

// v2-tabs-widget
const v2TabsWidgetOverridesSchema = z
  .object({
    tabs: z
      .array(
        z.object({
          id: z.string(),
          label: z.string(),
          order: z.number().default(0),
          disabled: z.boolean().default(false),
          components: z.array(z.any()).default([]),
        }),
      )
      .default([
        { id: "tab-1", label: "탭 1", order: 0, disabled: false, components: [] },
        { id: "tab-2", label: "탭 2", order: 1, disabled: false, components: [] },
      ]),
    defaultTab: z.string().default("tab-1"),
    orientation: z.enum(["horizontal", "vertical"]).default("horizontal"),
    variant: z.string().default("default"),
    allowCloseable: z.boolean().default(false),
    persistSelection: z.boolean().default(false),
  })
  .passthrough();

// v2-repeater
const v2V2RepeaterOverridesSchema = z
  .object({
    renderMode: z.enum(["inline", "modal", "button", "mixed"]).default("inline"),
    dataSource: z
      .object({
        tableName: z.string().default(""),
        foreignKey: z.string().default(""),
        referenceKey: z.string().default(""),
      })
      .default({ tableName: "", foreignKey: "", referenceKey: "" }),
    columns: z.array(z.any()).default([]),
    modal: z.object({ size: z.string().default("md") }).default({ size: "md" }),
    button: z
      .object({
        sourceType: z.string().default("manual"),
        manualButtons: z.array(z.any()).default([]),
        layout: z.string().default("horizontal"),
        style: z.string().default("outline"),
      })
      .default({ sourceType: "manual", manualButtons: [], layout: "horizontal", style: "outline" }),
    features: z
      .object({
        showAddButton: z.boolean().default(true),
        showDeleteButton: z.boolean().default(true),
        inlineEdit: z.boolean().default(false),
        dragSort: z.boolean().default(false),
        showRowNumber: z.boolean().default(false),
        selectable: z.boolean().default(false),
        multiSelect: z.boolean().default(false),
      })
      .default({
        showAddButton: true,
        showDeleteButton: true,
        inlineEdit: false,
        dragSort: false,
        showRowNumber: false,
        selectable: false,
        multiSelect: false,
      }),
  })
  .passthrough();

// ============================================
// V2 컴포넌트 overrides 스키마 정의
// ============================================

// v2-input
const v2InputOverridesSchema = z
  .object({
    inputType: z.string().default("text"),
    format: z.string().default("none"),
    placeholder: z.string().default(""),
  })
  .passthrough();

// v2-select
const v2SelectOverridesSchema = z
  .object({
    mode: z.string().default("dropdown"),
    source: z.string().default("static"),
    options: z.array(z.any()).default([]),
  })
  .passthrough();

// v2-date
const v2DateOverridesSchema = z
  .object({
    dateType: z.string().default("date"),
    format: z.string().default("YYYY-MM-DD"),
  })
  .passthrough();

// v2-list
const v2ListOverridesSchema = z
  .object({
    viewMode: z.string().default("table"),
    source: z.string().default("static"),
    columns: z.array(z.any()).default([]),
    pagination: z.boolean().default(true),
    sortable: z.boolean().default(true),
  })
  .passthrough();

// v2-layout
const v2LayoutOverridesSchema = z
  .object({
    layoutType: z.string().default("grid"),
    columns: z.number().default(2),
    gap: z.string().default("16"),
    use12Column: z.boolean().default(true),
  })
  .passthrough();

// v2-group
const v2GroupOverridesSchema = z
  .object({
    groupType: z.string().default("section"),
    title: z.string().default(""),
    collapsible: z.boolean().default(false),
    defaultOpen: z.boolean().default(true),
  })
  .passthrough();

// v2-media
const v2MediaOverridesSchema = z
  .object({
    mediaType: z.string().default("image"),
    multiple: z.boolean().default(false),
    preview: z.boolean().default(true),
  })
  .passthrough();

// v2-biz
const v2BizOverridesSchema = z
  .object({
    bizType: z.string().default("flow"),
  })
  .passthrough();

// v2-hierarchy
const v2HierarchyOverridesSchema = z
  .object({
    hierarchyType: z.string().default("tree"),
    viewMode: z.string().default("tree"),
    dataSource: z.string().default("static"),
  })
  .passthrough();

// v2-repeater
const v2RepeaterOverridesSchema = z
  .object({
    renderMode: z.enum(["inline", "modal"]).default("inline"),
    mainTableName: z.string().optional(),
    useCustomTable: z.boolean().default(false),
    foreignKeyColumn: z.string().optional(),
    foreignKeySourceColumn: z.string().optional(),
    dataSource: z
      .object({
        tableName: z.string().optional(),
        sourceTable: z.string().optional(),
        foreignKey: z.string().optional(),
        referenceKey: z.string().optional(),
        displayColumn: z.string().optional(),
      })
      .default({}),
    columns: z.array(z.any()).default([]),
    columnMappings: z.array(z.any()).default([]),
    calculationRules: z.array(z.any()).default([]),
    modal: z
      .object({
        size: z.enum(["sm", "md", "lg", "xl", "full"]).default("lg"),
        title: z.string().optional(),
        buttonText: z.string().optional(),
        sourceDisplayColumns: z.array(z.any()).default([]),
        searchFields: z.array(z.string()).default([]),
      })
      .default({ size: "lg", sourceDisplayColumns: [], searchFields: [] }),
    features: z
      .object({
        showAddButton: z.boolean().default(true),
        showDeleteButton: z.boolean().default(true),
        inlineEdit: z.boolean().default(true),
        dragSort: z.boolean().default(false),
        showRowNumber: z.boolean().default(false),
        selectable: z.boolean().default(false),
        multiSelect: z.boolean().default(true),
      })
      .default({
        showAddButton: true,
        showDeleteButton: true,
        inlineEdit: true,
        dragSort: false,
        showRowNumber: false,
        selectable: false,
        multiSelect: true,
      }),
    style: z
      .object({
        maxHeight: z.string().optional(),
        minHeight: z.string().optional(),
        borderless: z.boolean().default(false),
        compact: z.boolean().default(false),
      })
      .optional(),
  })
  .passthrough();

// ============================================
// 컴포넌트별 overrides 스키마 레지스트리
// ============================================
const componentOverridesSchemaRegistry: Record<string, z.ZodType<Record<string, any>>> = {
  // V2 컴포넌트 (16개)
  "v2-table-list": v2TableListOverridesSchema,
  "v2-button-primary": v2ButtonPrimaryOverridesSchema,
  "v2-text-display": v2TextDisplayOverridesSchema,
  "v2-split-panel-layout": v2SplitPanelLayoutOverridesSchema,
  "v2-section-card": v2SectionCardOverridesSchema,
  "v2-section-paper": v2SectionPaperOverridesSchema,
  "v2-divider-line": v2DividerLineOverridesSchema,
  "v2-repeat-container": v2RepeatContainerOverridesSchema,
  "v2-rack-structure": v2RackStructureOverridesSchema,
  "v2-numbering-rule": v2NumberingRuleOverridesSchema,
  "v2-category-manager": v2CategoryManagerOverridesSchema,
  "v2-pivot-grid": v2PivotGridOverridesSchema,
  "v2-location-swap-selector": v2LocationSwapSelectorOverridesSchema,
  "v2-aggregation-widget": v2AggregationWidgetOverridesSchema,
  "v2-card-display": v2CardDisplayOverridesSchema,
  "v2-table-search-widget": v2TableSearchWidgetOverridesSchema,
  "v2-tabs-widget": v2TabsWidgetOverridesSchema,
  "v2-repeater": v2V2RepeaterOverridesSchema,

  // V2 컴포넌트 (9개)
  "v2-split-line": z.object({
    resizable: z.boolean().default(true),
    lineColor: z.string().default("#e2e8f0"),
    lineWidth: z.number().default(4),
  }).passthrough(),

  "v2-input": v2InputOverridesSchema,
  "v2-select": v2SelectOverridesSchema,
  "v2-date": v2DateOverridesSchema,
  "v2-list": v2ListOverridesSchema,
  "v2-layout": v2LayoutOverridesSchema,
  "v2-group": v2GroupOverridesSchema,
  "v2-media": v2MediaOverridesSchema,
  "v2-biz": v2BizOverridesSchema,
  "v2-hierarchy": v2HierarchyOverridesSchema,
};

// ============================================
// 컴포넌트별 기본값 레지스트리 (fallback용)
// ============================================
const componentDefaultsRegistry: Record<string, Record<string, any>> = {
  // V2 컴포넌트
  "v2-table-list": {
    displayMode: "table",
    showHeader: true,
    showFooter: true,
    height: "auto",
    checkbox: { enabled: true, multiple: true, position: "left", selectAll: true },
    columns: [],
    autoWidth: true,
    stickyHeader: false,
    pagination: { enabled: true, pageSize: 20, showSizeSelector: true, showPageInfo: true },
    autoLoad: true,
  },
  "v2-button-primary": {
    text: "저장",
    actionType: "button",
    variant: "primary",
    action: { type: "save", successMessage: "저장되었습니다.", errorMessage: "저장 중 오류가 발생했습니다." },
  },
  "v2-text-display": {
    text: "텍스트를 입력하세요",
    fontSize: "14px",
    fontWeight: "normal",
    color: "#212121",
    textAlign: "left",
  },
  "v2-split-panel-layout": {
    leftPanel: { title: "마스터", showSearch: false, showAdd: false },
    rightPanel: { title: "디테일", showSearch: false, showAdd: false },
    splitRatio: 30,
    resizable: true,
    autoLoad: true,
    syncSelection: true,
  },
  "v2-split-line": {
    resizable: true,
    lineColor: "#e2e8f0",
    lineWidth: 4,
  },
  "v2-section-card": {
    title: "섹션 제목",
    description: "",
    showHeader: true,
    padding: "md",
    backgroundColor: "default",
    borderStyle: "solid",
    collapsible: false,
    defaultOpen: true,
  },
  "v2-section-paper": {
    backgroundColor: "default",
    padding: "md",
    roundedCorners: "md",
    shadow: "none",
    showBorder: false,
  },
  "v2-divider-line": {
    placeholder: "텍스트를 입력하세요",
    maxLength: 255,
  },
  "v2-repeat-container": {
    dataSourceType: "manual",
    layout: "vertical",
    gridColumns: 2,
    gap: "16px",
    showBorder: true,
    showShadow: false,
    emptyMessage: "데이터가 없습니다",
    usePaging: false,
    pageSize: 10,
    clickable: false,
    selectionMode: "single",
  },
  "v2-rack-structure": {
    showPreview: true,
    showTemplate: true,
  },
  "v2-numbering-rule": {
    showPreview: true,
  },
  "v2-category-manager": {
    viewMode: "tree",
    maxDepth: 3,
    showActions: true,
  },
  "v2-pivot-grid": {
    fields: [],
  },
  "v2-location-swap-selector": {
    dataSource: { type: "static", tableName: "", valueField: "location_code", labelField: "location_name" },
    departureField: "departure",
    destinationField: "destination",
    departureLabel: "출발지",
    destinationLabel: "도착지",
    showSwapButton: true,
    variant: "card",
  },
  "v2-aggregation-widget": {
    dataSourceType: "table",
    items: [],
    filters: [],
    filterLogic: "AND",
    layout: "horizontal",
    showLabels: true,
    showIcons: true,
    gap: "16px",
    autoRefresh: false,
    refreshOnFormChange: true,
  },
  "v2-card-display": {
    cardsPerRow: 3,
    cardSpacing: 16,
    cardStyle: { showTitle: true, showSubtitle: true, showDescription: true, showImage: false, showActions: true },
    columnMapping: {},
    dataSource: "table",
    staticData: [],
  },
  "v2-table-search-widget": {
    title: "테이블 검색",
    autoSelectFirstTable: true,
    showTableSelector: true,
  },
  "v2-tabs-widget": {
    tabs: [
      { id: "tab-1", label: "탭 1", order: 0, disabled: false, components: [] },
      { id: "tab-2", label: "탭 2", order: 1, disabled: false, components: [] },
    ],
    defaultTab: "tab-1",
    orientation: "horizontal",
    variant: "default",
    allowCloseable: false,
    persistSelection: false,
  },
  "v2-repeater": {
    renderMode: "inline",
    dataSource: { tableName: "", foreignKey: "", referenceKey: "" },
    columns: [],
    modal: { size: "md" },
    button: { sourceType: "manual", manualButtons: [], layout: "horizontal", style: "outline" },
    features: {
      showAddButton: true,
      showDeleteButton: true,
      inlineEdit: false,
      dragSort: false,
      showRowNumber: false,
      selectable: false,
      multiSelect: false,
    },
  },

  // V2 컴포넌트
  "v2-input": {
    inputType: "text",
    format: "none",
    placeholder: "",
  },
  "v2-select": {
    mode: "dropdown",
    source: "static",
    options: [],
  },
  "v2-date": {
    dateType: "date",
    format: "YYYY-MM-DD",
  },
  "v2-list": {
    viewMode: "table",
    source: "static",
    columns: [],
    pagination: true,
    sortable: true,
  },
  "v2-layout": {
    layoutType: "grid",
    columns: 2,
    gap: "16",
    use12Column: true,
  },
  "v2-group": {
    groupType: "section",
    title: "",
    collapsible: false,
    defaultOpen: true,
  },
  "v2-media": {
    mediaType: "image",
    multiple: false,
    preview: true,
  },
  "v2-biz": {
    bizType: "flow",
  },
  "v2-hierarchy": {
    hierarchyType: "tree",
    viewMode: "tree",
    dataSource: "static",
  },
  "v2-repeater": {
    renderMode: "inline",
    useCustomTable: false,
    dataSource: {},
    columns: [],
    columnMappings: [],
    calculationRules: [],
    modal: {
      size: "lg",
      sourceDisplayColumns: [],
      searchFields: [],
    },
    features: {
      showAddButton: true,
      showDeleteButton: true,
      inlineEdit: true,
      dragSort: false,
      showRowNumber: false,
      selectable: false,
      multiSelect: true,
    },
  },
};

// ============================================
// 컴포넌트 기본값 조회
// ============================================
export function getComponentDefaults(componentType: string): Record<string, any> {
  const schema = componentOverridesSchemaRegistry[componentType];
  if (schema) {
    return schema.parse({});
  }

  return componentDefaultsRegistry[componentType] || {};
}

// ============================================
// URL에서 기본값 조회
// ============================================
export function getDefaultsByUrl(url: string): Record<string, any> {
  const componentType = getComponentTypeFromUrl(url);
  return getComponentDefaults(componentType);
}

// ============================================
// overrides 스키마 파싱 (유효성 검사)
// ============================================
export function parseOverridesByUrl(
  url: string,
  overrides: Record<string, any> | null | undefined,
  options?: { applyDefaults?: boolean },
): Record<string, any> {
  const componentType = getComponentTypeFromUrl(url);
  const schema = componentOverridesSchemaRegistry[componentType];
  const applyDefaults = options?.applyDefaults ?? false;

  if (!schema) {
    return overrides || {};
  }

  const parsed = schema.safeParse(overrides || {});
  if (!parsed.success) {
    console.warn("V2 overrides 스키마 검증 실패", {
      componentType,
      errors: parsed.error.issues,
    });
    return overrides || {};
  }

  return applyDefaults ? parsed.data : overrides || {};
}

// ============================================
// V2 컴포넌트 로드 (기본값 + overrides 병합)
// ============================================
export function loadComponentV2(component: ComponentV2): ComponentV2 & { config: Record<string, any> } {
  const defaults = getDefaultsByUrl(component.url);
  const overrides = parseOverridesByUrl(component.url, component.overrides);
  const config = mergeComponentConfig(defaults, overrides);

  return {
    ...component,
    config,
  };
}

// ============================================
// V2 컴포넌트 저장 (차이값 추출)
// ============================================
export function saveComponentV2(component: ComponentV2 & { config?: Record<string, any> }): ComponentV2 {
  const defaults = getDefaultsByUrl(component.url);
  const normalizedConfig = component.config
    ? parseOverridesByUrl(component.url, component.config, { applyDefaults: true })
    : undefined;
  const normalizedOverrides = normalizedConfig
    ? extractCustomConfig(normalizedConfig, defaults)
    : parseOverridesByUrl(component.url, component.overrides);

  return {
    id: component.id,
    url: component.url,
    position: component.position,
    size: component.size,
    displayOrder: component.displayOrder,
    overrides: normalizedOverrides,
  };
}

// ============================================
// V2 레이아웃 로드 (전체 컴포넌트 기본값 병합)
// ============================================
export function loadLayoutV2(layoutData: any): LayoutV2 & {
  components: Array<ComponentV2 & { config: Record<string, any> }>;
  layers: Array<Layer & { components: Array<ComponentV2 & { config: Record<string, any> }> }>;
} {
  const parsed = layoutV2Schema.parse(layoutData || { version: "2.1", components: [], layers: [] });

  // 마이그레이션: components만 있고 layers가 없는 경우 Default Layer 생성
  if ((!parsed.layers || parsed.layers.length === 0) && parsed.components && parsed.components.length > 0) {
    const defaultLayer: Layer = {
      id: "default-layer",
      name: "기본 레이어",
      type: "base",
      zIndex: 0,
      isVisible: true,
      isLocked: false,
      components: parsed.components,
    };
    parsed.layers = [defaultLayer];
  }

  // 모든 레이어의 컴포넌트 로드
  const loadedLayers = parsed.layers.map((layer) => ({
    ...layer,
    components: layer.components.map(loadComponentV2),
  }));

  // 하위 호환성을 위한 components 배열 (모든 레이어의 컴포넌트 합침)
  const allComponents = loadedLayers.flatMap((layer) => layer.components);

  return {
    ...parsed,
    layers: loadedLayers,
    components: allComponents,
  };
}

// ============================================
// V2 레이아웃 저장 (전체 컴포넌트 차이값 추출)
// ============================================
export function saveLayoutV2(
  components: Array<ComponentV2 & { config?: Record<string, any> }>,
  layers?: Array<Layer & { components: Array<ComponentV2 & { config?: Record<string, any> }> }>,
): LayoutV2 {
  // 레이어가 있는 경우 레이어 구조 저장
  if (layers && layers.length > 0) {
    const savedLayers = layers.map((layer) => ({
      ...layer,
      components: layer.components.map(saveComponentV2),
    }));

    return {
      version: "2.1",
      layers: savedLayers,
      components: savedLayers.flatMap((l) => l.components), // 하위 호환성
    };
  }

  // 레이어가 없는 경우 (기존 방식) - Default Layer로 감싸서 저장
  const savedComponents = components.map(saveComponentV2);
  const defaultLayer: Layer = {
    id: "default-layer",
    name: "기본 레이어",
    type: "base",
    zIndex: 0,
    isVisible: true,
    isLocked: false,
    components: savedComponents,
  };

  return {
    version: "2.1",
    layers: [defaultLayer],
    components: savedComponents,
  };
}
