import { createStore } from "zustand";
import { create } from "zustand";
import { createHeroTemplateBlocks } from "@/app/editor/templates/heroTemplate";
import { createFeaturesTemplateBlocks } from "@/app/editor/templates/featuresTemplate";
import { createBlogTemplateBlocks } from "@/app/editor/templates/blogTemplate";
import { createTeamTemplateBlocks } from "@/app/editor/templates/teamTemplate";
import { createFooterTemplateBlocks } from "@/app/editor/templates/footerTemplate";
import { createCtaTemplateBlocks } from "@/app/editor/templates/ctaTemplate";
import { createFaqTemplateBlocks } from "@/app/editor/templates/faqTemplate";
import { createPricingTemplateBlocks } from "@/app/editor/templates/pricingTemplate";
import { createTestimonialsTemplateBlocks } from "@/app/editor/templates/testimonialsTemplate";

// 블록 타입 정의: 텍스트/버튼/이미지/비디오/섹션/구분선/리스트/폼 블록/폼 요소 블록
export type BlockType =
  | "text"
  | "button"
  | "image"
  | "video"
  | "section"
  | "divider"
  | "list"
  | "form"
  | "formInput"
  | "formSelect"
  | "formCheckbox"
  | "formRadio";

// 텍스트 블록 속성
export interface TextBlockProps {
  text: string;
  // 텍스트 정렬: 기본은 left
  align: "left" | "center" | "right";
  // 텍스트 크기: 기본은 base (기존 필드, 하위호환 유지)
  size: "sm" | "base" | "lg";

  // 폰트 크기 모드: 프리셋 스케일 또는 커스텀 값
  fontSizeMode?: "scale" | "custom";
  // 폰트 크기 스케일 (scale 모드일 때 사용)
  fontSizeScale?: "xs" | "sm" | "base" | "lg" | "xl" | "2xl" | "3xl";
  // 폰트 크기 커스텀 값 (예: "18px", "1.125rem")
  fontSizeCustom?: string;

  // 줄 간격 모드: 프리셋 스케일 또는 커스텀 값
  lineHeightMode?: "scale" | "custom";
  // 줄 간격 스케일
  lineHeightScale?: "tight" | "snug" | "normal" | "relaxed" | "loose";
  // 줄 간격 커스텀 값 (예: "1.4", "24px")
  lineHeightCustom?: string;

  // 폰트 굵기 모드: 프리셋 스케일 또는 커스텀 값
  fontWeightMode?: "scale" | "custom";
  // 폰트 굵기 스케일
  fontWeightScale?: "normal" | "medium" | "semibold" | "bold";
  // 폰트 굵기 커스텀 값 (예: "500", "650")
  fontWeightCustom?: string;

  // 글자 간격 커스텀 값 (em 단위, 예: "0.05em", "-0.02em")
  letterSpacingCustom?: string;

  // 텍스트 색상 모드: 팔레트 또는 커스텀 웹색상
  colorMode?: "palette" | "custom";
  // 텍스트 색상 팔레트 (디자인 토큰)
  colorPalette?:
    | "default"
    | "muted"
    | "strong"
    | "accent"
    | "danger"
    | "success"
    | "warning"
    | "info"
    | "neutral";
  // 커스텀 색상 값 (예: "#ff0000", "rgb(…)" 등)
  colorCustom?: string;

  // 블록 배경색 커스텀 값 (예: "#123456")
  backgroundColorCustom?: string;

  // 최대 너비 모드: 프리셋 또는 커스텀 값
  maxWidthMode?: "scale" | "custom";
  // 최대 너비 스케일
  maxWidthScale?: "none" | "prose" | "narrow";
  // 최대 너비 커스텀 값 (예: "600px", "40rem")
  maxWidthCustom?: string;

  // 텍스트 장식
  underline?: boolean;
  strike?: boolean;
  italic?: boolean;
}

// 버튼 블록 속성
export interface ButtonBlockProps {
  label: string;
  href: string;
  align?: "left" | "center" | "right";
  // 버튼 크기: 더 세분화된 스케일(xs~xl)
  size?: "xs" | "sm" | "md" | "lg" | "xl";
  variant?: "solid" | "outline" | "ghost";
  colorPalette?: "primary" | "muted" | "danger" | "success" | "neutral";
  fullWidth?: boolean;
  widthMode?: "auto" | "full" | "fixed";
  widthPx?: number;
  borderRadius?: "none" | "sm" | "md" | "lg" | "full";
  paddingX?: number;
  paddingY?: number;
  // 버튼 텍스트 크기 (예: "14px")
  fontSizeCustom?: string;
  // 버튼 텍스트 줄 간격 (예: "1.4")
  lineHeightCustom?: string;
  // 버튼 텍스트 글자 간격 (em 단위, 예: "0.05em")
  letterSpacingCustom?: string;
  // 채움(배경) 색상 커스텀 값
  fillColorCustom?: string;
  // 외곽선(보더) 색상 커스텀 값
  strokeColorCustom?: string;
  // 버튼 텍스트 색상 커스텀 값
  textColorCustom?: string;
  // 폼 컨트롤러에서 Submit 버튼 매핑 시 사용할 수 있는 전송 키 (name 역할)
  formFieldName?: string;
  imageSrc?: string;
  imageAlt?: string;
  imageSourceType?: "asset" | "externalUrl";
  imageAssetId?: string | null;
  imagePlacement?: "left" | "right" | "top" | "bottom";
}

// 이미지 블록 속성
export interface ImageBlockProps {
  src: string;
  alt: string;
  // 이미지 정렬: 기본은 center
  align?: "left" | "center" | "right";
  // 너비 모드: auto(콘텐츠에 맞춤) 또는 fixed(px 지정)
  widthMode?: "auto" | "fixed";
  widthPx?: number;
  // 모서리 둥글기
  borderRadius?: "none" | "sm" | "md" | "lg" | "full";
  // 모서리 둥글기 px 값 (이미지에 한해서는 연속적인 px 단위 조정을 지원한다)
  borderRadiusPx?: number;
  // 이미지 카드를 감싸는 배경색 커스텀 값 (예: "#123456")
  backgroundColorCustom?: string;
  // 이미지 소스 타입: 업로드된 에셋 또는 외부 URL
  sourceType?: "asset" | "externalUrl";
  // 업로드된 에셋인 경우 Asset.id 를 저장해둔다.
  assetId?: string | null;
}

export interface VideoBlockProps {
  sourceUrl: string;
  sourceType?: "asset" | "externalUrl";
  assetId?: string | null;
  posterImageSrc?: string;
  posterSourceType?: "asset" | "externalUrl";
  posterAssetId?: string | null;
  platform?: "auto" | "youtube" | "vimeo" | "html5";
  align?: "left" | "center" | "right";
  widthMode?: "auto" | "full" | "fixed";
  widthPx?: number;
  aspectRatio?: "16:9" | "4:3" | "1:1";
  autoplay?: boolean;
  loop?: boolean;
  muted?: boolean;
  controls?: boolean;
  titleText?: string;
  ariaLabel?: string;
  captionText?: string;
  // HTML5 비디오 재생 범위(초 단위)
  startTimeSec?: number;
  endTimeSec?: number;
  // 카드 스타일: 배경색/패딩/모서리 둥글기
  backgroundColorCustom?: string;
  cardPaddingPx?: number;
  borderRadiusPx?: number;
}

// 구분선 블록 속성
export interface DividerBlockProps {
  align: "left" | "center" | "right";
  thickness: "thin" | "medium";
  // 길이/너비
  widthMode?: "auto" | "full" | "fixed";
  widthPx?: number;
  // 색상
  colorHex?: string;
  // 상하 여백 (토큰, 호환용)
  marginY?: "sm" | "md" | "lg";
  // 상하 여백(px) 숫자 값
  marginYPx?: number;
}

// 리스트 아이템 트리(중첩 리스트) 노드 정의
export interface ListItemNode {
  id: string;
  text: string;
  children?: ListItemNode[];
}

export interface ListLine {
  depth: number;
  text: string;
}

// 리스트 아이템 트리 조작 유틸 (pure helper)

type ListTreeOpResult = {
  tree: ListItemNode[];
  changed: boolean;
};

const cloneNode = (node: ListItemNode): ListItemNode => ({
  id: node.id,
  text: node.text,
  children: node.children ? node.children.map(cloneNode) : [],
});

export const buildItemsTreeFromItems = (blockId: string, items: string[]): ListItemNode[] =>
  items.map((text, index) => ({
    id: `${blockId}_item_${index + 1}`,
    text,
    children: [],
  }));

export const flattenItemsTreeToItems = (tree: ListItemNode[]): string[] => {
  const result: string[] = [];

  const walk = (nodes: ListItemNode[]) => {
    for (const node of nodes) {
      result.push(node.text);
      if (node.children && node.children.length > 0) {
        walk(node.children);
      }
    }
  };

  walk(tree);
  return result;
};

export const linesToItemsTree = (blockId: string, lines: ListLine[]): ListItemNode[] => {
  const roots: ListItemNode[] = [];
  const stack: ListItemNode[] = [];

  lines.forEach((line, index) => {
    const depth = Number.isFinite(line.depth) && line.depth > 0 ? Math.floor(line.depth) : 0;

    const node: ListItemNode = {
      id: `${blockId}_item_${index + 1}`,
      text: line.text,
      children: [],
    };

    if (depth === 0 || stack.length === 0) {
      roots.push(node);
      stack.length = 1;
      stack[0] = node;
      return;
    }

    // depth 가 현재 스택 깊이보다 크면, 스택 마지막을 부모로 사용한다.
    const parentDepth = Math.min(depth - 1, stack.length - 1);
    const parent = stack[parentDepth];

    if (!parent.children) {
      parent.children = [];
    }
    parent.children.push(node);

    // 현재 노드를 해당 depth 위치에 두고, 그 이후 스택은 잘라낸다.
    stack.length = parentDepth + 2;
    stack[parentDepth + 1] = node;
  });

  return roots;
};

export const itemsTreeToLines = (tree: ListItemNode[]): ListLine[] => {
  const lines: ListLine[] = [];

  const walk = (nodes: ListItemNode[], depth: number) => {
    for (const node of nodes) {
      lines.push({ depth, text: node.text });
      if (node.children && node.children.length > 0) {
        walk(node.children, depth + 1);
      }
    }
  };

  walk(tree, 0);
  return lines;
};

export const parseTextareaToLines = (text: string): ListLine[] => {
  const rawLines = text.split(/\r?\n/);

  return rawLines.map((raw) => {
    let depth = 0;
    let i = 0;

    while (i < raw.length) {
      const ch = raw[i];
      if (ch === "\t") {
        depth += 1;
        i += 1;
      } else if (ch === " ") {
        let spaceCount = 0;
        while (i < raw.length && raw[i] === " ") {
          spaceCount += 1;
          i += 1;
        }
        depth += Math.floor(spaceCount / 2);
      } else {
        break;
      }
    }

    const textWithoutIndent = raw.slice(i);
    return {
      depth,
      text: textWithoutIndent,
    };
  });
};

export const stringifyLinesToTextarea = (lines: ListLine[]): string => {
  return lines
    .map((line) => {
      const indent = "  ".repeat(Math.max(0, Math.floor(line.depth)));
      return `${indent}${line.text}`;
    })
    .join("\n");
};

// 내부 재귀 유틸: siblings 배열과 targetId 를 받아 조작을 수행한다.
const withSiblings = (
  siblings: ListItemNode[],
  targetId: string,
  op: (args: {
    siblings: ListItemNode[];
    index: number;
    parentPath: ListItemNode[];
  }) => ListTreeOpResult,
  parentPath: ListItemNode[] = [],
): ListTreeOpResult => {
  for (let i = 0; i < siblings.length; i += 1) {
    const node = siblings[i];
    if (node.id === targetId) {
      return op({ siblings, index: i, parentPath });
    }
    if (node.children && node.children.length > 0) {
      const childResult = withSiblings(node.children, targetId, op, [...parentPath, node]);
      if (childResult.changed) {
        const cloned = siblings.map(cloneNode);
        const idx = cloned.findIndex((n) => n.id === node.id);
        if (idx >= 0) {
          cloned[idx].children = childResult.tree;
        }
        return { tree: cloned, changed: true };
      }
    }
  }
  return { tree: siblings, changed: false };
};

export const indentListItem = (tree: ListItemNode[], targetId: string): ListItemNode[] => {
  const { tree: next } = withSiblings(
    tree,
    targetId,
    ({ siblings, index }) => {
      // 첫 번째 아이템은 들여쓰기 불가
      if (index === 0) {
        return { tree: siblings, changed: false };
      }

      const prev = siblings[index - 1];
      const target = siblings[index];

      const newPrev: ListItemNode = cloneNode(prev);
      const newTarget: ListItemNode = cloneNode(target);
      const newSiblings = siblings.map(cloneNode);

      // siblings 에서 target 제거
      newSiblings.splice(index, 1);

      // prev 위치의 노드를 newPrev 로 교체
      const prevIndex = index - 1;
      const prevNode = newSiblings[prevIndex];
      const prevChildren = prevNode.children ? [...prevNode.children] : [];
      prevChildren.push(newTarget);
      newSiblings[prevIndex] = { ...newPrev, children: prevChildren };

      return { tree: newSiblings, changed: true };
    },
  );
  return next;
};

export const outdentListItem = (tree: ListItemNode[], targetId: string): ListItemNode[] => {
  // 부모의 children 에서 targetId 를 찾아 제거하고,
  // 부모가 속한 siblings 배열에서 부모 바로 다음 위치에 target 을 삽입한다.

  const liftFromChildren = (nodes: ListItemNode[]): ListTreeOpResult => {
    const clonedSiblings = nodes.map(cloneNode);

    for (let i = 0; i < clonedSiblings.length; i += 1) {
      const node = clonedSiblings[i];
      const children = node.children ?? [];

      // 1단계: 현재 node 의 children 에 target 이 있는지 확인한다.
      const childIndex = children.findIndex((c) => c.id === targetId);
      if (childIndex >= 0) {
        const child = children[childIndex];
        const newChildren = [...children];
        newChildren.splice(childIndex, 1);

        // 부모의 children 에서 target 제거
        clonedSiblings[i] = {
          ...node,
          children: newChildren,
        };

        // 부모 바로 뒤에 target 을 siblings 레벨로 삽입
        clonedSiblings.splice(i + 1, 0, cloneNode(child));

        return { tree: clonedSiblings, changed: true };
      }

      // 2단계: 더 깊은 레벨에서 검색한다.
      if (children.length > 0) {
        const childResult = liftFromChildren(children);
        if (childResult.changed) {
          clonedSiblings[i] = {
            ...node,
            children: childResult.tree,
          };
          return { tree: clonedSiblings, changed: true };
        }
      }
    }

    return { tree: nodes, changed: false };
  };

  const { tree: next } = liftFromChildren(tree);
  return next;
};

export const moveListItemUp = (tree: ListItemNode[], targetId: string): ListItemNode[] => {
  const { tree: next } = withSiblings(
    tree,
    targetId,
    ({ siblings, index }) => {
      if (index === 0) {
        return { tree: siblings, changed: false };
      }
      const cloned = siblings.map(cloneNode);
      const tmp = cloned[index - 1];
      cloned[index - 1] = cloned[index];
      cloned[index] = tmp;
      return { tree: cloned, changed: true };
    },
  );
  return next;
};

export const moveListItemDown = (tree: ListItemNode[], targetId: string): ListItemNode[] => {
  const { tree: next } = withSiblings(
    tree,
    targetId,
    ({ siblings, index }) => {
      if (index >= siblings.length - 1) {
        return { tree: siblings, changed: false };
      }
      const cloned = siblings.map(cloneNode);
      const tmp = cloned[index + 1];
      cloned[index + 1] = cloned[index];
      cloned[index] = tmp;
      return { tree: cloned, changed: true };
    },
  );
  return next;
};

// 리스트 블록 속성
export interface ListBlockProps {
  items: string[]; // 1단계 하위호환용 단순 배열
  itemsTree?: ListItemNode[]; // 중첩 리스트용 트리 구조
  ordered: boolean;
  align: "left" | "center" | "right";
  // 타이포/색상
  fontSizeCustom?: string;
  lineHeightCustom?: string;
  textColorCustom?: string;
  // 리스트 전체를 감싸는 배경색 커스텀 값
  backgroundColorCustom?: string;
  // 불릿/간격
  bulletStyle?:
    | "disc"
    | "circle"
    | "square"
    | "decimal"
    | "none"
    | "lower-alpha"
    | "upper-alpha"
    | "lower-roman"
    | "upper-roman";
  // 토큰 기반 간격(호환용)
  gapY?: "sm" | "md" | "lg";
  // 아이템 간 여백(px) 숫자 값
  gapYPx?: number;
}

// 섹션 블록 속성
export interface SectionBlockProps {
  background: "default" | "muted" | "primary";
  paddingY: "sm" | "md" | "lg";
  // px 단위 세로 패딩(있으면 토큰 대신 우선 적용)
  paddingYPx?: number;
  // 레이아웃 컬럼 정의 (12 그리드 기준 span)
  columns: Array<{
    id: string;
    span: number;
  }>;
  // 섹션 레이아웃 스타일
  maxWidthMode?: "narrow" | "normal" | "wide";
  // px 단위 최대 폭(있으면 토큰 기반 maxWidth 대신 우선 적용)
  maxWidthPx?: number;
  gapX?: "sm" | "md" | "lg";
  // px 단위 컬럼 간 간격(있으면 토큰 기반 gap 대신 우선 적용)
  gapXPx?: number;
  alignItems?: "top" | "center" | "bottom";
  backgroundColorCustom?: string;
  // 섹션 배경 이미지
  backgroundImageSrc?: string;
  backgroundImageSourceType?: "asset" | "externalUrl";
  backgroundImageAssetId?: string | null;
  backgroundImageSize?: "auto" | "cover" | "contain";
  backgroundImagePosition?: "center" | "top" | "bottom" | "left" | "right";
  backgroundImageRepeat?: "no-repeat" | "repeat" | "repeat-x" | "repeat-y";
  backgroundImagePositionMode?: "preset" | "custom";
  backgroundImagePositionXPercent?: number;
  backgroundImagePositionYPercent?: number;
  // 섹션 배경 비디오
  backgroundVideoSrc?: string;
  backgroundVideoSourceType?: "asset" | "externalUrl";
  backgroundVideoAssetId?: string | null;
}

// 폼 입력/셀렉트/체크박스/라디오 공통 스타일 속성
export interface FormFieldStyleProps {
  align?: "left" | "center" | "right";
  size?: "xs" | "sm" | "md" | "lg" | "xl";
  fullWidth?: boolean;
  // 필드 너비 및 레이아웃
  widthMode?: "auto" | "full" | "fixed";
  widthPx?: number;
  paddingX?: number;
  paddingY?: number;
  optionGapPx?: number;
  labelGapPx?: number;
  labelLayout?: "stacked" | "inline";
  borderRadius?: "none" | "sm" | "md" | "lg" | "full";
  // 필드 텍스트 색상/타이포그라피
  fontSizeCustom?: string;
  lineHeightCustom?: string;
  letterSpacingCustom?: string;
  textColorCustom?: string;
  // 필드 배경/테두리 색상
  fillColorCustom?: string;
  strokeColorCustom?: string;
}

// 폼 입력 블록 속성
export type FormLabelMode = "text" | "image";

export interface FormInputBlockProps extends FormFieldStyleProps {
  label: string;
  formFieldName: string;
  required?: boolean;
  inputType?: "text" | "email" | "textarea";
  placeholder?: string;
  labelMode?: FormLabelMode;
  labelImageUrl?: string;
  labelImageAlt?: string;
}

// 폼 셀렉트 옵션/블록 속성
export interface FormSelectOption {
  label: string;
  value: string;
}

export interface FormSelectBlockProps extends FormFieldStyleProps {
  label: string;
  formFieldName: string;
  options: FormSelectOption[];
  required?: boolean;
  labelMode?: FormLabelMode;
  labelImageUrl?: string;
  labelImageAlt?: string;
}

// 폼 체크박스 옵션/블록 속성 (여러 체크박스를 한 그룹으로 표현)
export interface FormCheckboxOption {
  label: string;
  value: string;
  // 각 체크박스 옵션도 개별 이미지 라벨을 가질 수 있도록 확장한다.
  labelImageUrl?: string;
}

export interface FormCheckboxBlockProps extends FormFieldStyleProps {
  groupLabel: string;
  formFieldName: string;
  options: FormCheckboxOption[];
  required?: boolean;
  // 그룹 타이틀 자체도 텍스트/이미지 모드를 가질 수 있도록 분리한다.
  groupLabelMode?: FormLabelMode;
  groupLabelImageUrl?: string;
  groupLabelImageSource?: "url" | "upload";
  optionImageSource?: "url" | "upload";
}

// 폼 라디오 그룹 옵션/블록 속성
export interface FormRadioOption {
  label: string;
  value: string;
  // 각 라디오 옵션도 개별 이미지 라벨을 가질 수 있도록 확장한다.
  labelImageUrl?: string;
}

export interface FormRadioBlockProps extends FormFieldStyleProps {
  groupLabel: string;
  formFieldName: string;
  options: FormRadioOption[];
  required?: boolean;
  // 라디오 그룹 타이틀의 텍스트/이미지 모드
  groupLabelMode?: FormLabelMode;
  groupLabelImageUrl?: string;
  groupLabelImageSource?: "url" | "upload";
  optionImageSource?: "url" | "upload";
}

// 폼 필드 설정
export interface FormFieldConfig {
  id: string;
  name: string; // 실제 전송 키 (예: "email")
  label: string; // UI 라벨 (예: "이메일")
  type: "text" | "email" | "textarea";
  required?: boolean;
}

// 폼 블록 속성
export type FormSubmitTarget = "internal" | "webhook" | "both";
export type FormPayloadFormat = "form" | "json";

export interface FormBlockProps {
  kind: "contact";
  // 전송 대상: internal 또는 webhook(외부 URL, Google Sheets 포함)
  submitTarget: FormSubmitTarget;
  // 성공/실패 메시지
  successMessage?: string;
  errorMessage?: string;
  // webhook 설정 (Google Sheets 포함)
  destinationUrl?: string; // POST 보낼 URL
  method?: "POST" | "GET"; // 기본 POST
  // webhook 전송 포맷: 기본은 x-www-form-urlencoded(form).
  payloadFormat?: FormPayloadFormat;
  headers?: Record<string, string>; // Authorization 등
  extraParams?: Record<string, string>; // 항상 함께 보내는 추가 파라미터(formId 등)
  // 폼 필드 목록
  fields?: FormFieldConfig[];
  // v2 컨트롤러: 개별 폼 요소 블록과 제출 버튼을 연결하기 위한 ID 목록
  fieldIds?: string[];
  submitButtonId?: string | null;
  // 폼 레이아웃
  formWidthMode?: "auto" | "full" | "fixed";
  formWidthPx?: number;
  marginYPx?: number;
  // 폼 전체를 감싸는 배경색 커스텀 값
  backgroundColorCustom?: string;
}

export type CanvasPreset = "mobile" | "tablet" | "desktop" | "full" | "custom";

export interface ProjectConfig {
  title: string;
  slug: string;
  canvasPreset: CanvasPreset;
  canvasWidthPx?: number;
  canvasBgColorHex?: string;
  bodyBgColorHex?: string;
  headHtml?: string;
  trackingScript?: string;
  // SEO / 메타 설정 (15.1)
  seoTitle?: string;
  seoDescription?: string;
  seoOgImageUrl?: string;
  seoCanonicalUrl?: string;
  seoNoIndex?: boolean;
}

// 공통 블록 모델
export interface Block {
  id: string;
  type: BlockType;
  props:
    | TextBlockProps
    | ButtonBlockProps
    | ImageBlockProps
    | VideoBlockProps
    | SectionBlockProps
    | DividerBlockProps
    | ListBlockProps
    | FormBlockProps
    | FormInputBlockProps
    | FormSelectBlockProps
    | FormCheckboxBlockProps
    | FormRadioBlockProps;
  // 레이아웃 트리 상 위치 정보 (루트 텍스트/버튼/이미지 블록은 null)
  sectionId?: string | null;
  columnId?: string | null;
}

// 에디터 상태 인터페이스
export interface EditorState {
  blocks: Block[];
  selectedBlockId: string | null;
  selectedListItemId?: string | null;
  history: Block[][];
  future: Block[][];
  projectConfig: ProjectConfig;
  updateProjectConfig: (partial: Partial<ProjectConfig>) => void;
  addTextBlock: () => void;
  addButtonBlock: () => void;
  addImageBlock: () => void;
  addVideoBlock: () => void;
  addDividerBlock: () => void;
  addListBlock: () => void;
  addSectionBlock: () => void;
  addFormBlock: () => void;
  addFormInputBlock: () => void;
  addFormSelectBlock: () => void;
  addFormCheckboxBlock: () => void;
  addFormRadioBlock: () => void;
  addHeroTemplateSection: () => void;
  addFeaturesTemplateSection: () => void;
  addCtaTemplateSection: () => void;
  addFaqTemplateSection: () => void;
  addPricingTemplateSection: () => void;
  addTestimonialsTemplateSection: () => void;
  addBlogTemplateSection: () => void;
  addTeamTemplateSection: () => void;
  addFooterTemplateSection: () => void;
  selectListItem: (itemId: string | null) => void;
  indentSelectedListItem: (blockId: string) => void;
  outdentSelectedListItem: (blockId: string) => void;
  moveSelectedListItemUp: (blockId: string) => void;
  moveSelectedListItemDown: (blockId: string) => void;
  updateBlock: (
    id: string,
    partial:
      | Partial<TextBlockProps>
      | Partial<ButtonBlockProps>
      | Partial<FormBlockProps>
      | Partial<FormInputBlockProps>
      | Partial<FormSelectBlockProps>
      | Partial<FormCheckboxBlockProps>
      | Partial<FormRadioBlockProps>,
  ) => void;
  selectBlock: (id: string | null) => void;
  replaceBlocks: (blocks: Block[]) => void;
  reorderBlocks: (activeId: string, overId: string) => void;
  moveBlock: (id: string, sectionId: string | null, columnId: string | null) => void;
  undo: () => void;
  redo: () => void;
  removeBlock: (id: string) => void;
  duplicateBlock: (id: string) => void;
  resetHistory: () => void;
}

// 간단한 ID 생성기 (추후 uuid 라이브러리로 교체 가능)
let idCounter = 0;
const createId = () => `blk_${Date.now()}_${idCounter++}`;

// undo/redo 를 위한 히스토리 유틸리티: 상태 변경 전에 현재 blocks 스냅샷을 history 에 쌓고 future 는 비운다.
const pushHistoryImpl = (set: any, get: any) => {
  const { blocks } = get() as EditorState;
  set((state: EditorState) => ({
    history: [...state.history, blocks],
    future: [],
  }));
};

const getNextFormFieldName = (blocks: Block[], prefix: string): string => {
  const regex = new RegExp(`^${prefix}-(\\d+)$`);
  let max = 0;

  for (const b of blocks) {
    const anyProps: any = b.props;
    const name = anyProps?.formFieldName;
    if (typeof name !== "string") continue;
    const match = name.match(regex);
    if (!match) continue;
    const n = Number(match[1] ?? 0);
    if (!Number.isNaN(n) && n > max) {
      max = n;
    }
  }

  return `${prefix}-${max + 1}`;
};

// 에디터 스토어 생성 함수 (테스트 및 앱에서 공유 사용)
// set/get 은 zustand 내부 구현에 의해 주입되므로, 여기서는 any 로 완화해 사용한다.
const createEditorState = (set: any, get: any): EditorState => {
  const pushHistory = () => {
    const { blocks } = get() as EditorState;
    set((state: EditorState) => ({
      history: [...state.history, blocks],
      future: [],
    }));
  };

  return {
  blocks: [],
  selectedBlockId: null,
  selectedListItemId: null,
  history: [],
  future: [],
  projectConfig: {
    title: "새 페이지",
    slug: "my-landing",
    canvasPreset: "full",
    canvasBgColorHex: "#020617",
    bodyBgColorHex: "#020617",
    headHtml: "",
    trackingScript: "",
    // SEO 기본값: 필요할 때만 값을 채우고, 없으면 title 등을 사용한다.
    seoTitle: "",
    seoDescription: "",
    seoOgImageUrl: "",
    seoCanonicalUrl: "",
    seoNoIndex: false,
  },

  updateProjectConfig: (partial: Partial<ProjectConfig>) => {
    set((state: EditorState) => ({
      projectConfig: {
        ...state.projectConfig,
        ...partial,
      },
    }));
  },

  // 텍스트 블록 추가: 기본 텍스트와 함께 생성 후 선택 상태로 만든다
  addTextBlock: () => {
    const id = createId();

    const { selectedBlockId, blocks } = get();
    let sectionId: string | null = null;
    let columnId: string | null = null;

    if (selectedBlockId) {
      const target = blocks.find((b: Block) => b.id === selectedBlockId);
      if (target) {
        if (target.type === "section") {
          const sProps = target.props as SectionBlockProps;
          sectionId = target.id;
          columnId = sProps.columns?.[0]?.id ?? null;
        } else {
          sectionId = (target as any).sectionId ?? null;
          columnId = (target as any).columnId ?? null;
        }
      }
    }

    const newBlock: Block = {
      id,
      type: "text",
      props: {
        text: "새 텍스트",
        align: "left",
        size: "base",
      },
      sectionId,
      columnId,
    };

    pushHistoryImpl(set, get);
    set((state: EditorState) => ({
      blocks: [...state.blocks, newBlock],
      selectedBlockId: id,
    }));
  },

    // Hero 템플릿 섹션 추가: 섹션 1개와 기본 텍스트/버튼 블록들을 첫 컬럼에 배치한다
    addHeroTemplateSection: () => {
      const { selectedBlockId, blocks } = get();
      let sectionId = createId();
      let replacingSectionId: string | null = null;

      if (selectedBlockId) {
        const target = blocks.find((b: Block) => b.id === selectedBlockId && b.type === "section");
        if (target) {
          sectionId = target.id;
          replacingSectionId = target.id;
        }
      }

      const { blocks: templateBlocks, lastSelectedId } = createHeroTemplateBlocks({
        sectionId,
        createId,
      });

      pushHistoryImpl(set, get);
      set((state: EditorState) => {
        let baseBlocks = state.blocks as Block[];
        if (replacingSectionId) {
          baseBlocks = baseBlocks.filter(
            (b) => b.id !== replacingSectionId && (b as any).sectionId !== replacingSectionId,
          );
        }

        const newBlocks = [...baseBlocks, ...templateBlocks];

        return {
          blocks: newBlocks,
          selectedBlockId: lastSelectedId,
        };
      });
    },

    // 리스트 아이템 선택 상태를 업데이트한다.
    selectListItem: (itemId) => {
      set({ selectedListItemId: itemId ?? null });
    },

    // Features 템플릿 섹션 추가: 3컬럼(4/4/4) 섹션과 각 컬럼의 제목/설명 텍스트를 생성한다
    addFeaturesTemplateSection: () => {
      const { selectedBlockId, blocks } = get();
      let sectionId = createId();
      let replacingSectionId: string | null = null;

      if (selectedBlockId) {
        const target = blocks.find((b: Block) => b.id === selectedBlockId && b.type === "section");
        if (target) {
          sectionId = target.id;
          replacingSectionId = target.id;
        }
      }

      const { blocks: templateBlocks, lastSelectedId } = createFeaturesTemplateBlocks({
        sectionId,
        createId,
      });

      pushHistoryImpl(set, get);
      set((state: EditorState) => {
        let baseBlocks = state.blocks as Block[];
        if (replacingSectionId) {
          baseBlocks = baseBlocks.filter(
            (b) => b.id !== replacingSectionId && (b as any).sectionId !== replacingSectionId,
          );
        }

        const newBlocks = [...baseBlocks, ...templateBlocks];

        return {
          blocks: newBlocks,
          selectedBlockId: lastSelectedId,
        };
      });
    },

    // Blog 템플릿 섹션 추가: 3컬럼 포스트 카드(제목/요약)를 생성한다
    addBlogTemplateSection: () => {
      const { selectedBlockId, blocks } = get();
      let sectionId = createId();
      let replacingSectionId: string | null = null;

      if (selectedBlockId) {
        const target = blocks.find((b: Block) => b.id === selectedBlockId && b.type === "section");
        if (target) {
          sectionId = target.id;
          replacingSectionId = target.id;
        }
      }

      const { blocks: templateBlocks, lastSelectedId } = createBlogTemplateBlocks({
        sectionId,
        createId,
      });

      pushHistoryImpl(set, get);
      set((state: EditorState) => {
        let baseBlocks = state.blocks as Block[];
        if (replacingSectionId) {
          baseBlocks = baseBlocks.filter(
            (b) => b.id !== replacingSectionId && (b as any).sectionId !== replacingSectionId,
          );
        }

        const newBlocks = [...baseBlocks, ...templateBlocks];

        return {
          blocks: newBlocks,
          selectedBlockId: lastSelectedId,
        };
      });
    },

    // Team 템플릿 섹션 추가: 3컬럼 팀 카드(이름/역할/소개)를 생성한다
    addTeamTemplateSection: () => {
      const { selectedBlockId, blocks } = get();
      let sectionId = createId();
      let replacingSectionId: string | null = null;

      if (selectedBlockId) {
        const target = blocks.find((b: Block) => b.id === selectedBlockId && b.type === "section");
        if (target) {
          sectionId = target.id;
          replacingSectionId = target.id;
        }
      }

      const { blocks: templateBlocks, lastSelectedId } = createTeamTemplateBlocks({
        sectionId,
        createId,
      });

      pushHistoryImpl(set, get);
      set((state: EditorState) => {
        let baseBlocks = state.blocks as Block[];
        if (replacingSectionId) {
          baseBlocks = baseBlocks.filter(
            (b) => b.id !== replacingSectionId && (b as any).sectionId !== replacingSectionId,
          );
        }

        const newBlocks = [...baseBlocks, ...templateBlocks];

        return {
          blocks: newBlocks,
          selectedBlockId: lastSelectedId,
        };
      });
    },

    // Footer 템플릿 섹션 추가: 링크/카피라이트 텍스트가 포함된 1컬럼 섹션을 생성한다
    addFooterTemplateSection: () => {
      const { selectedBlockId, blocks } = get();
      let sectionId = createId();
      let replacingSectionId: string | null = null;

      if (selectedBlockId) {
        const target = blocks.find((b: Block) => b.id === selectedBlockId && b.type === "section");
        if (target) {
          sectionId = target.id;
          replacingSectionId = target.id;
        }
      }

      const { blocks: templateBlocks, lastSelectedId } = createFooterTemplateBlocks({
        sectionId,
        createId,
      });

      pushHistoryImpl(set, get);
      set((state: EditorState) => {
        let baseBlocks = state.blocks as Block[];
        if (replacingSectionId) {
          baseBlocks = baseBlocks.filter(
            (b) => b.id !== replacingSectionId && (b as any).sectionId !== replacingSectionId,
          );
        }

        const newBlocks = [...baseBlocks, ...templateBlocks];

        return {
          blocks: newBlocks,
          selectedBlockId: lastSelectedId,
        };
      });
    },

    // CTA 템플릿 섹션 추가: 텍스트와 버튼이 포함된 1컬럼 섹션을 생성한다
    addCtaTemplateSection: () => {
      const { selectedBlockId, blocks } = get();
      let sectionId = createId();
      let replacingSectionId: string | null = null;

      if (selectedBlockId) {
        const target = blocks.find((b: Block) => b.id === selectedBlockId && b.type === "section");
        if (target) {
          sectionId = target.id;
          replacingSectionId = target.id;
        }
      }

      const { blocks: templateBlocks, lastSelectedId } = createCtaTemplateBlocks({
        sectionId,
        createId,
      });

      pushHistoryImpl(set, get);
      set((state: EditorState) => {
        let baseBlocks = state.blocks as Block[];
        if (replacingSectionId) {
          baseBlocks = baseBlocks.filter(
            (b) => b.id !== replacingSectionId && (b as any).sectionId !== replacingSectionId,
          );
        }

        const newBlocks = [...baseBlocks, ...templateBlocks];

        return {
          blocks: newBlocks,
          selectedBlockId: lastSelectedId,
        };
      });
    },

    // FAQ 템플릿 섹션 추가: 질문/답변 쌍이 수직으로 나열된 섹션을 생성한다
    addFaqTemplateSection: () => {
      const { selectedBlockId, blocks } = get();
      let sectionId = createId();
      let replacingSectionId: string | null = null;

      if (selectedBlockId) {
        const target = blocks.find((b: Block) => b.id === selectedBlockId && b.type === "section");
        if (target) {
          sectionId = target.id;
          replacingSectionId = target.id;
        }
      }

      const { blocks: templateBlocks, lastSelectedId } = createFaqTemplateBlocks({
        sectionId,
        createId,
      });

      pushHistoryImpl(set, get);
      set((state: EditorState) => {
        let baseBlocks = state.blocks as Block[];
        if (replacingSectionId) {
          baseBlocks = baseBlocks.filter(
            (b) => b.id !== replacingSectionId && (b as any).sectionId !== replacingSectionId,
          );
        }

        const newBlocks = [...baseBlocks, ...templateBlocks];

        return {
          blocks: newBlocks,
          selectedBlockId: lastSelectedId,
        };
      });
    },

    // Pricing 템플릿 섹션 추가: 3컬럼 요금제 카드(플랜 이름/가격/설명)를 생성한다
    addPricingTemplateSection: () => {
      const { selectedBlockId, blocks } = get();
      let sectionId = createId();
      let replacingSectionId: string | null = null;

      if (selectedBlockId) {
        const target = blocks.find((b: Block) => b.id === selectedBlockId && b.type === "section");
        if (target) {
          sectionId = target.id;
          replacingSectionId = target.id;
        }
      }

      const { blocks: templateBlocks, lastSelectedId } = createPricingTemplateBlocks({
        sectionId,
        createId,
      });

      pushHistoryImpl(set, get);
      set((state: EditorState) => {
        let baseBlocks = state.blocks as Block[];
        if (replacingSectionId) {
          baseBlocks = baseBlocks.filter(
            (b) => b.id !== replacingSectionId && (b as any).sectionId !== replacingSectionId,
          );
        }

        const newBlocks = [...baseBlocks, ...templateBlocks];

        return {
          blocks: newBlocks,
          selectedBlockId: lastSelectedId,
        };
      });
    },

    // Testimonials 템플릿 섹션 추가: 3컬럼 후기 카드(본문/작성자)를 생성한다
    addTestimonialsTemplateSection: () => {
      const { selectedBlockId, blocks } = get();
      let sectionId = createId();
      let replacingSectionId: string | null = null;

      if (selectedBlockId) {
        const target = blocks.find((b: Block) => b.id === selectedBlockId && b.type === "section");
        if (target) {
          sectionId = target.id;
          replacingSectionId = target.id;
        }
      }

      const { blocks: templateBlocks, lastSelectedId } = createTestimonialsTemplateBlocks({
        sectionId,
        createId,
      });

      pushHistoryImpl(set, get);
      set((state: EditorState) => {
        let baseBlocks = state.blocks as Block[];
        if (replacingSectionId) {
          baseBlocks = baseBlocks.filter(
            (b) => b.id !== replacingSectionId && (b as any).sectionId !== replacingSectionId,
          );
        }

        const newBlocks = [...baseBlocks, ...templateBlocks];

        return {
          blocks: newBlocks,
          selectedBlockId: lastSelectedId,
        };
      });
    },

    // 이미지 블록 추가: 기본 플레이스홀더와 스타일 기본값과 함께 생성 후 선택 상태로 만든다
    addImageBlock: () => {
      const id = createId();

      const { selectedBlockId, blocks } = get();
      let sectionId: string | null = null;
      let columnId: string | null = null;

      if (selectedBlockId) {
        const target = blocks.find((b: Block) => b.id === selectedBlockId);
        if (target) {
          if (target.type === "section") {
            const sProps = target.props as SectionBlockProps;
            sectionId = target.id;
            columnId = sProps.columns?.[0]?.id ?? null;
          } else {
            sectionId = (target as any).sectionId ?? null;
            columnId = (target as any).columnId ?? null;
          }
        }
      }

      const newBlock: Block = {
        id,
        type: "image",
        props: {
          src: "",
          alt: "이미지 설명",
          align: "center",
          widthMode: "auto",
          borderRadius: "md",
        },
        sectionId,
        columnId,
      };

      pushHistoryImpl(set, get);
      set((state: EditorState) => ({
        blocks: [...state.blocks, newBlock],
        selectedBlockId: id,
      }));
    },

    // 비디오 블록 추가: 기본 소스/정렬/비율 설정과 함께 생성 후 선택 상태로 만든다
    addVideoBlock: () => {
      const id = createId();

      const { selectedBlockId, blocks } = get();
      let sectionId: string | null = null;
      let columnId: string | null = null;

      if (selectedBlockId) {
        const target = blocks.find((b: Block) => b.id === selectedBlockId);
        if (target) {
          if (target.type === "section") {
            const sProps = target.props as SectionBlockProps;
            sectionId = target.id;
            columnId = sProps.columns?.[0]?.id ?? null;
          } else {
            sectionId = (target as any).sectionId ?? null;
            columnId = (target as any).columnId ?? null;
          }
        }
      }

      const newBlock: Block = {
        id,
        type: "video",
        props: {
          sourceUrl: "",
          platform: "auto",
          align: "center",
          widthMode: "auto",
          aspectRatio: "16:9",
          controls: true,
          autoplay: false,
          loop: false,
          muted: false,
        },
        sectionId,
        columnId,
      };

      pushHistoryImpl(set, get);
      set((state: EditorState) => ({
        blocks: [...state.blocks, newBlock],
        selectedBlockId: id,
      }));
    },

    // 폼 입력 블록 추가: 루트에 기본 label/formFieldName 과 함께 생성 후 선택 상태로 만든다
    addFormInputBlock: () => {
      const id = createId();
      const { blocks } = get();

      const label = "입력 필드";
      const formFieldName = getNextFormFieldName(blocks, "text");

      const newBlock: Block = {
        id,
        type: "formInput",
        props: {
          label,
          formFieldName,
          required: false,
          inputType: "text",
          placeholder: "",
          labelMode: "text",
          borderRadius: "md",
          fillColorCustom: "",
          strokeColorCustom: "",
          textColorCustom: "",
          widthMode: "full",
          paddingX: 12,
          paddingY: 10,
          optionGapPx: 4,
          labelLayout: "stacked",
          labelGapPx: 8,
        },
        sectionId: null,
        columnId: null,
      };

      pushHistoryImpl(set, get);
      set((state: EditorState) => ({
        blocks: [...state.blocks, newBlock],
        selectedBlockId: id,
      }));
    },

    // 폼 셀렉트 블록 추가: 루트에 기본 label/formFieldName/options 와 함께 생성 후 선택 상태로 만든다
    addFormSelectBlock: () => {
      const id = createId();
      const { blocks } = get();

      const label = "선택 필드";
      const formFieldName = getNextFormFieldName(blocks, "select");
      const options: FormSelectOption[] = [
        { label: "옵션 1", value: "option_1" },
        { label: "옵션 2", value: "option_2" },
      ];

      const newBlock: Block = {
        id,
        type: "formSelect",
        props: {
          label,
          formFieldName,
          options,
          required: false,
          labelMode: "text",
          borderRadius: "md",
          fillColorCustom: "",
          strokeColorCustom: "",
          textColorCustom: "",
          widthMode: "full",
          paddingX: 12,
          paddingY: 10,
          labelLayout: "stacked",
          labelGapPx: 8,
        },
        sectionId: null,
        columnId: null,
      };

      pushHistoryImpl(set, get);
      set((state: EditorState) => ({
        blocks: [...state.blocks, newBlock],
        selectedBlockId: id,
      }));
    },

    // 폼 체크박스 블록 추가: 루트에 기본 groupLabel/formFieldName/options 와 함께 생성 후 선택 상태로 만든다
    addFormCheckboxBlock: () => {
      const id = createId();
      const { blocks } = get();

      const groupLabel = "체크박스";
      const formFieldName = getNextFormFieldName(blocks, "checkbox");
      const options: FormCheckboxOption[] = [
        { label: "체크 1", value: "check_1" },
        { label: "체크 2", value: "check_2" },
      ];

      const newBlock: Block = {
        id,
        type: "formCheckbox",
        props: {
          groupLabel,
          formFieldName,
          options,
          required: false,
          groupLabelMode: "text",
          borderRadius: "md",
          fillColorCustom: "",
          strokeColorCustom: "",
          textColorCustom: "",
          widthMode: "full",
          paddingX: 12,
          paddingY: 10,
          optionGapPx: 4,
          labelLayout: "stacked",
          labelGapPx: 8,
        },
        sectionId: null,
        columnId: null,
      };

      pushHistoryImpl(set, get);
      set((state: EditorState) => ({
        blocks: [...state.blocks, newBlock],
        selectedBlockId: id,
      }));
    },

    // 폼 라디오 그룹 블록 추가: 루트에 기본 groupLabel/formFieldName/options 와 함께 생성 후 선택 상태로 만든다
    addFormRadioBlock: () => {
      const id = createId();
      const { blocks } = get();

      const groupLabel = "라디오 그룹";
      const formFieldName = getNextFormFieldName(blocks, "radio");
      const options: FormRadioOption[] = [
        { label: "옵션 A", value: "option_a" },
        { label: "옵션 B", value: "option_b" },
      ];

      const newBlock: Block = {
        id,
        type: "formRadio",
        props: {
          groupLabel,
          formFieldName,
          options,
          required: false,
          groupLabelMode: "text",
          borderRadius: "md",
          fillColorCustom: "",
          strokeColorCustom: "",
          textColorCustom: "",
          widthMode: "full",
          paddingX: 12,
          paddingY: 10,
          optionGapPx: 4,
          labelLayout: "stacked",
          labelGapPx: 8,
        },
        sectionId: null,
        columnId: null,
      };

      pushHistoryImpl(set, get);
      set((state: EditorState) => ({
        blocks: [...state.blocks, newBlock],
        selectedBlockId: id,
      }));
    },

    // 구분선 블록 추가: 기본 정렬/두께와 함께 생성 후 선택 상태로 만든다
    addDividerBlock: () => {
      const id = createId();

      const { selectedBlockId, blocks } = get();
      let sectionId: string | null = null;
      let columnId: string | null = null;

      if (selectedBlockId) {
        const target = blocks.find((b: Block) => b.id === selectedBlockId);
        if (target) {
          if (target.type === "section") {
            const sProps = target.props as SectionBlockProps;
            sectionId = target.id;
            columnId = sProps.columns?.[0]?.id ?? null;
          } else {
            sectionId = (target as any).sectionId ?? null;
            columnId = (target as any).columnId ?? null;
          }
        }
      }

      const newBlock: Block = {
        id,
        type: "divider",
        props: {
          align: "center",
          thickness: "thin",
          colorHex: "#475569",
        },
        sectionId,
        columnId,
      };

      pushHistoryImpl(set, get);
      set((state: EditorState) => ({
        blocks: [...state.blocks, newBlock],
        selectedBlockId: id,
      }));
    },

    // 리스트 블록 추가: 기본 항목/정렬과 함께 생성 후 선택 상태로 만든다
    addListBlock: () => {
      const id = createId();

      const { selectedBlockId, blocks } = get();
      let sectionId: string | null = null;
      let columnId: string | null = null;

      if (selectedBlockId) {
        const target = blocks.find((b: Block) => b.id === selectedBlockId);
        if (target) {
          if (target.type === "section") {
            const sProps = target.props as SectionBlockProps;
            sectionId = target.id;
            columnId = sProps.columns?.[0]?.id ?? null;
          } else {
            sectionId = (target as any).sectionId ?? null;
            columnId = (target as any).columnId ?? null;
          }
        }
      }

      const newBlock: Block = {
        id,
        type: "list",
        props: {
          items: ["리스트 아이템 1"],
          itemsTree: [
            {
              id: `${id}_item_1`,
              text: "리스트 아이템 1",
              children: [],
            },
          ],
          ordered: false,
          align: "left",
        },
        sectionId,
        columnId,
      };

      pushHistoryImpl(set, get);
      set((state: EditorState) => ({
        blocks: [...state.blocks, newBlock],
        selectedBlockId: id,
      }));
    },

    // 섹션 블록 추가: 배경/패딩 기본값과 함께 생성 후 선택 상태로 만든다
    addSectionBlock: () => {
      const id = createId();
      const newBlock: Block = {
        id,
        type: "section",
        props: {
          background: "default",
          paddingY: "md",
          columns: [
            {
              id: `${id}_col_1`,
              span: 12,
            },
          ],
        },
        sectionId: null,
        columnId: null,
      };

      pushHistoryImpl(set, get);
      set((state: EditorState) => ({
        blocks: [...state.blocks, newBlock],
        selectedBlockId: id,
      }));
    },

    // 폼 블록 추가: 기본 contact 폼 설정과 함께 생성 후 선택 상태로 만든다
    addFormBlock: () => {
      const id = createId();

      const fields: FormFieldConfig[] = [
        {
          id: `${id}_field_name`,
          name: "name",
          label: "이름",
          type: "text",
          required: true,
        },
        {
          id: `${id}_field_email`,
          name: "email",
          label: "이메일",
          type: "email",
          required: true,
        },
        {
          id: `${id}_field_message`,
          name: "message",
          label: "메시지",
          type: "textarea",
          required: true,
        },
      ];

      const newBlock: Block = {
        id,
        type: "form",
        props: {
          kind: "contact",
          submitTarget: "internal",
          successMessage: "성공적으로 전송되었습니다.",
          errorMessage: "전송 중 오류가 발생했습니다.",
          fields,
          fieldIds: [],
          submitButtonId: null,
        },
        sectionId: null,
        columnId: null,
      };

      pushHistoryImpl(set, get);
      set((state: EditorState) => ({
        blocks: [...state.blocks, newBlock],
        selectedBlockId: id,
      }));
    },

    // 버튼 블록 추가: 기본 라벨/링크와 함께 생성 후 선택 상태로 만든다
    addButtonBlock: () => {
      const id = createId();

      const { selectedBlockId, blocks } = get();
      let sectionId: string | null = null;
      let columnId: string | null = null;

      if (selectedBlockId) {
        const target = blocks.find((b: Block) => b.id === selectedBlockId);
        if (target) {
          if (target.type === "section") {
            const sProps = target.props as SectionBlockProps;
            sectionId = target.id;
            columnId = sProps.columns?.[0]?.id ?? null;
          } else {
            sectionId = (target as any).sectionId ?? null;
            columnId = (target as any).columnId ?? null;
          }
        }
      }
      const newBlock: Block = {
        id,
        type: "button",
        props: {
          label: "버튼",
          href: "#",
          align: "left",
          size: "md",
          variant: "solid",
          colorPalette: "primary",
          fullWidth: false,
          widthMode: "auto",
          widthPx: 240,
          borderRadius: "md",
          paddingX: 16,
          paddingY: 10,
          fontSizeCustom: "14px",
          // 기본 primary/solid 버튼 색상과 동일한 커스텀 색상 값을 사용해
          // 에디터/프리뷰/속성 패널이 모두 같은 색상을 공유하도록 한다.
          textColorCustom: "#0b1120",
          fillColorCustom: "#0ea5e9",
          strokeColorCustom: "#0284c7",
        },
        sectionId,
        columnId,
      };

      pushHistoryImpl(set, get);
      set((state: EditorState) => ({
        blocks: [...state.blocks, newBlock],
        selectedBlockId: id,
      }));
    },

    // 특정 블록의 속성을 부분 업데이트 (텍스트/버튼 공통 사용)
    updateBlock: (id, partial) => {
      pushHistoryImpl(set, get);
      set((state: EditorState) => ({
        blocks: state.blocks.map((block: Block) =>
          block.id === id
            ? {
                ...block,
                props: { ...(block.props as any), ...(partial as any) },
              }
            : block,
        ),
      }));
    },

    // 선택된 리스트 아이템을 들여쓰기한다.
    indentSelectedListItem: (blockId) => {
      const { blocks, selectedListItemId } = get();
      if (!selectedListItemId) return;

      const target = blocks.find((b: Block) => b.id === blockId && b.type === "list");
      if (!target) return;

      const listProps = target.props as ListBlockProps & { itemsTree?: ListItemNode[] };
      const currentTree: ListItemNode[] =
        (listProps.itemsTree as ListItemNode[] | undefined) && listProps.itemsTree!.length > 0
          ? (listProps.itemsTree as ListItemNode[])
          : [];

      const nextTree = indentListItem(currentTree, selectedListItemId);

      set((state: EditorState) => ({
        blocks: state.blocks.map((block: Block) =>
          block.id === blockId
            ? {
                ...block,
                props: {
                  ...(block.props as any),
                  itemsTree: nextTree,
                },
              }
            : block,
        ),
      }));
    },

    // 선택된 리스트 아이템을 부모의 다음 형제로 내어쓰기한다.
    outdentSelectedListItem: (blockId) => {
      const { blocks, selectedListItemId } = get();
      if (!selectedListItemId) return;

      const target = blocks.find((b: Block) => b.id === blockId && b.type === "list");
      if (!target) return;

      const listProps = target.props as ListBlockProps & { itemsTree?: ListItemNode[] };
      const currentTree: ListItemNode[] =
        (listProps.itemsTree as ListItemNode[] | undefined) && listProps.itemsTree!.length > 0
          ? (listProps.itemsTree as ListItemNode[])
          : [];

      const nextTree = outdentListItem(currentTree, selectedListItemId);

      set((state: EditorState) => ({
        blocks: state.blocks.map((block: Block) =>
          block.id === blockId
            ? {
                ...block,
                props: {
                  ...(block.props as any),
                  itemsTree: nextTree,
                },
              }
            : block,
        ),
      }));
    },

    // 선택된 리스트 아이템을 같은 레벨에서 한 칸 위로 이동시킨다.
    moveSelectedListItemUp: (blockId) => {
      const { blocks, selectedListItemId } = get();
      if (!selectedListItemId) return;

      const target = blocks.find((b: Block) => b.id === blockId && b.type === "list");
      if (!target) return;

      const listProps = target.props as ListBlockProps & { itemsTree?: ListItemNode[] };
      const currentTree: ListItemNode[] =
        (listProps.itemsTree as ListItemNode[] | undefined) && listProps.itemsTree!.length > 0
          ? (listProps.itemsTree as ListItemNode[])
          : [];

      const nextTree = moveListItemUp(currentTree, selectedListItemId);

      set((state: EditorState) => ({
        blocks: state.blocks.map((block: Block) =>
          block.id === blockId
            ? {
                ...block,
                props: {
                  ...(block.props as any),
                  itemsTree: nextTree,
                },
              }
            : block,
        ),
      }));
    },

    // 선택된 리스트 아이템을 같은 레벨에서 한 칸 아래로 이동시킨다.
    moveSelectedListItemDown: (blockId) => {
      const { blocks, selectedListItemId } = get();
      if (!selectedListItemId) return;

      const target = blocks.find((b: Block) => b.id === blockId && b.type === "list");
      if (!target) return;

      const listProps = target.props as ListBlockProps & { itemsTree?: ListItemNode[] };
      const currentTree: ListItemNode[] =
        (listProps.itemsTree as ListItemNode[] | undefined) && listProps.itemsTree!.length > 0
          ? (listProps.itemsTree as ListItemNode[])
          : [];

      const nextTree = moveListItemDown(currentTree, selectedListItemId);

      set((state: EditorState) => ({
        blocks: state.blocks.map((block: Block) =>
          block.id === blockId
            ? {
                ...block,
                props: {
                  ...(block.props as any),
                  itemsTree: nextTree,
                },
              }
            : block,
        ),
      }));
    },

    // 선택된 블록 ID를 변경
    selectBlock: (id) => {
      const { blocks } = get();
      // 존재하지 않는 ID를 선택하려고 할 경우, 그냥 null 로 초기화
      if (id && !blocks.some((b: Block) => b.id === id)) {
        set({ selectedBlockId: null });
        return;
      }
      set({ selectedBlockId: id });
    },

    replaceBlocks: (blocks) => {
      pushHistoryImpl(set, get);
      set({
        blocks,
        selectedBlockId: blocks.length > 0 ? blocks[0].id : null,
      });
    },

    reorderBlocks: (activeId, overId) => {
      pushHistoryImpl(set, get);
      set((state: EditorState) => {
        const current = state.blocks;
        const oldIndex = current.findIndex((b: Block) => b.id === activeId);
        const newIndex = current.findIndex((b: Block) => b.id === overId);

        if (oldIndex === -1 || newIndex === -1 || oldIndex === newIndex) {
          return { blocks: current };
        }

        const updated = [...current];
        const [moved] = updated.splice(oldIndex, 1);
        updated.splice(newIndex, 0, moved);

        return { blocks: updated };
      });
    },
    moveBlock: (id, sectionId, columnId) => {
      pushHistoryImpl(set, get);
      set((state: EditorState) => ({
        blocks: state.blocks.map((block: Block) =>
          block.id === id
            ? {
                ...block,
                sectionId,
                columnId,
              }
            : block,
        ),
      }));
    },
    removeBlock: (id) => {
      pushHistoryImpl(set, get);
      set((state: EditorState) => {
        const current = state.blocks;
        const index = current.findIndex((b) => b.id === id);
        if (index === -1) {
          return state;
        }
        const target = current[index];

        let nextBlocks: Block[];
        if (target.type === "section") {
          // 섹션 블록이 삭제될 때는 해당 섹션 안에 속한 자식 블록들도 함께 제거한다.
          nextBlocks = current.filter((b) => b.id !== id && b.sectionId !== id);
        } else {
          nextBlocks = current.filter((b) => b.id !== id);
        }

        // 선택 상태는 삭제된 블록의 이전 블록 또는 다음 블록으로 이동한다.
        let nextSelected: string | null = null;
        if (nextBlocks.length > 0) {
          const candidateIndex = Math.min(Math.max(index - 1, 0), nextBlocks.length - 1);
          nextSelected = nextBlocks[candidateIndex].id;
        }

        return {
          ...state,
          blocks: nextBlocks,
          selectedBlockId: nextSelected,
        };
      });
    },
    duplicateBlock: (id) => {
      pushHistoryImpl(set, get);
      set((state: EditorState) => {
        const current = state.blocks;
        const index = current.findIndex((b) => b.id === id);
        if (index === -1) {
          return state;
        }

        const original = current[index];
        const newId = createId();

        const cloned: Block = {
          ...original,
          id: newId,
          // props 는 얕은 복사로 충분 (현재 props 는 모두 평면 구조)
          props: { ...(original.props as any) },
        };

        const nextBlocks = [...current];
        nextBlocks.splice(index + 1, 0, cloned);

        return {
          ...state,
          blocks: nextBlocks,
          selectedBlockId: newId,
        };
      });
    },
    undo: () => {
      const { history, blocks } = get();
      if (history.length === 0) return;

      const previous = history[history.length - 1];
      const nextHistory = history.slice(0, -1);

      set((state: EditorState) => ({
        blocks: previous,
        selectedBlockId: previous.length > 0 ? previous[previous.length - 1].id : null,
        history: nextHistory,
        future: [...state.future, blocks],
      }));
    },
    redo: () => {
      const { future, blocks } = get();
      if (future.length === 0) return;

      const next = future[future.length - 1];
      const nextFuture = future.slice(0, -1);

      set((state: EditorState) => ({
        blocks: next,
        selectedBlockId: next.length > 0 ? next[next.length - 1].id : null,
        history: [...state.history, blocks],
        future: nextFuture,
      }));
    },
    resetHistory: () => {
      set({ history: [], future: [] });
    },
  };
};

// React 컴포넌트에서 사용하는 전역 훅 스토어
export const useEditorStore = create<EditorState>()(createEditorState);

// 테스트 등에서 독립적인 스토어 인스턴스를 만들 때 사용하는 팩토리
export const createEditorStore = () => createStore<EditorState>(createEditorState);
