import { describe, it, expect, vi, afterEach } from "vitest";
import { render, screen, fireEvent, cleanup } from "@testing-library/react";
import { ImagePropertiesPanel } from "@/app/editor/panels/ImagePropertiesPanel";
import type { ImageBlockProps } from "@/features/editor/state/editorStore";

// ImagePropertiesPanel 컨트롤 TDD
// - 카드 배경색(backgroundColorCustom) HEX 인풋 변경 시 updateBlock 이 올바르게 호출되는지

describe("ImagePropertiesPanel", () => {
  const baseProps: ImageBlockProps = {
    src: "/images/example.png",
    alt: "예시 이미지",
    align: "center",
    widthMode: "auto",
    borderRadius: "md",
  };

  afterEach(() => {
    cleanup();
  });

  it("카드 배경색 HEX 인풋 변경 시 updateBlock 이 backgroundColorCustom 으로 호출되어야 한다", () => {
    const updateBlock = vi.fn();

    render(
      <ImagePropertiesPanel
        imageProps={{ ...baseProps, backgroundColorCustom: "#111111" }}
        selectedBlockId="image-1"
        updateBlock={updateBlock}
      />,
    );

    const hexInput = screen.getByLabelText("Image card background color HEX");
    fireEvent.change(hexInput, { target: { value: "#112233" } });

    expect(updateBlock).toHaveBeenCalledWith(
      "image-1",
      expect.objectContaining({ backgroundColorCustom: "#112233" }),
    );
  });

  it("이미지 소스 셀렉트에서 url 선택 시 sourceType 이 externalUrl 이 되고 assetId 가 null 로 설정되어야 한다", () => {
    const updateBlock = vi.fn();

    render(
      <ImagePropertiesPanel
        imageProps={{
          ...baseProps,
          src: "/api/image/asset-1",
          sourceType: "asset",
          assetId: "asset-1",
        } as any}
        selectedBlockId="image-source-1"
        updateBlock={updateBlock}
      />,
    );

    const select = screen.getByLabelText("Image source");
    fireEvent.change(select, { target: { value: "url" } });

    expect(updateBlock).toHaveBeenCalledWith(
      "image-source-1",
      expect.objectContaining({ sourceType: "externalUrl", assetId: null }),
    );
  });

  it("이미지 URL 인풋 변경 시 src / sourceType / assetId 가 externalUrl/null 로 업데이트되어야 한다", () => {
    const updateBlock = vi.fn();

    render(
      <ImagePropertiesPanel
        imageProps={{
          ...baseProps,
          src: "https://example.com/before.png",
          sourceType: "externalUrl",
          assetId: "old-asset",
        } as any}
        selectedBlockId="image-url-1"
        updateBlock={updateBlock}
      />,
    );

    const urlInput = screen.getByLabelText("Image URL");
    fireEvent.change(urlInput, { target: { value: "https://example.com/after.png" } });

    expect(updateBlock).toHaveBeenCalledWith(
      "image-url-1",
      expect.objectContaining({
        src: "https://example.com/after.png",
        sourceType: "externalUrl",
        assetId: null,
      }),
    );
  });

  it("대체 텍스트 인풋 변경 시 updateBlock 이 alt 로 호출되어야 한다", () => {
    const updateBlock = vi.fn();

    render(
      <ImagePropertiesPanel
        imageProps={baseProps}
        selectedBlockId="image-alt-1"
        updateBlock={updateBlock}
      />,
    );

    const altInput = screen.getByLabelText("Alt text");
    fireEvent.change(altInput, { target: { value: "새 대체 텍스트" } });

    expect(updateBlock).toHaveBeenCalledWith(
      "image-alt-1",
      expect.objectContaining({ alt: "새 대체 텍스트" }),
    );
  });

  it("이미지 정렬 셀렉트 변경 시 updateBlock 이 align 으로 호출되어야 한다", () => {
    const updateBlock = vi.fn();

    render(
      <ImagePropertiesPanel
        imageProps={{ ...baseProps, align: "center" }}
        selectedBlockId="image-align-1"
        updateBlock={updateBlock}
      />,
    );

    const select = screen.getByLabelText("Image alignment");
    fireEvent.change(select, { target: { value: "left" } });

    expect(updateBlock).toHaveBeenCalledWith(
      "image-align-1",
      expect.objectContaining({ align: "left" }),
    );
  });

  it("너비 모드 셀렉트 변경 시 updateBlock 이 widthMode 로 호출되어야 한다", () => {
    const updateBlock = vi.fn();

    render(
      <ImagePropertiesPanel
        imageProps={{ ...baseProps, widthMode: "auto" }}
        selectedBlockId="image-width-mode-1"
        updateBlock={updateBlock}
      />,
    );

    const select = screen.getByLabelText("Image width mode");
    fireEvent.change(select, { target: { value: "fixed" } });

    expect(updateBlock).toHaveBeenCalledWith(
      "image-width-mode-1",
      expect.objectContaining({ widthMode: "fixed" }),
    );
  });

  it("너비 모드가 fixed 일 때 고정 너비 슬라이더 변경 시 updateBlock 이 widthPx 로 호출되어야 한다", () => {
    const updateBlock = vi.fn();

    render(
      <ImagePropertiesPanel
        imageProps={{ ...baseProps, widthMode: "fixed", widthPx: 320 }}
        selectedBlockId="image-width-fixed-1"
        updateBlock={updateBlock}
      />,
    );

    const slider = screen.getByLabelText("Fixed width 슬라이더");
    fireEvent.change(slider, { target: { value: "480" } });

    expect(updateBlock).toHaveBeenCalledWith(
      "image-width-fixed-1",
      expect.objectContaining({ widthPx: 480 }),
    );
  });

  it("모서리 둥글기 슬라이더 변경 시 updateBlock 이 borderRadius 와 borderRadiusPx 를 함께 업데이트해야 한다", () => {
    const updateBlock = vi.fn();

    render(
      <ImagePropertiesPanel
        imageProps={{ ...baseProps, borderRadius: "md", borderRadiusPx: 60 }}
        selectedBlockId="image-radius-1"
        updateBlock={updateBlock}
      />,
    );

    const slider = screen.getByLabelText("Border radius 슬라이더");
    fireEvent.change(slider, { target: { value: "24" } });

    expect(updateBlock).toHaveBeenCalledWith(
      "image-radius-1",
      expect.objectContaining({ borderRadius: "sm", borderRadiusPx: 24 }),
    );
  });

  it("ImagePropertiesPanel 주요 인풋/셀렉트 컨트롤은 라이트/다크 듀얼 테마 크롬을 사용해야 한다", () => {
    const updateBlock = vi.fn();

    render(
      <ImagePropertiesPanel
        imageProps={baseProps as any}
        selectedBlockId="image-theme-1"
        updateBlock={updateBlock}
      />,
    );

    const sourceSelect = screen.getByLabelText("Image source") as HTMLSelectElement;
    const sourceClass = sourceSelect.getAttribute("class") ?? "";
    expect(sourceClass).toContain("bg-white");
    expect(sourceClass).toContain("text-slate-900");
    expect(sourceClass).toContain("border-slate-300");
    expect(sourceClass).toContain("dark:bg-slate-900");
    expect(sourceClass).toContain("dark:text-slate-100");
    expect(sourceClass).toContain("dark:border-slate-700");

    const urlInput = screen.getByLabelText("Image URL") as HTMLInputElement;
    const urlClass = urlInput.getAttribute("class") ?? "";
    expect(urlClass).toContain("bg-white");
    expect(urlClass).toContain("text-slate-900");
    expect(urlClass).toContain("border-slate-300");
    expect(urlClass).toContain("dark:bg-slate-900");
    expect(urlClass).toContain("dark:text-slate-100");
    expect(urlClass).toContain("dark:border-slate-700");

    const alignSelect = screen.getByLabelText("Image alignment") as HTMLSelectElement;
    const alignClass = alignSelect.getAttribute("class") ?? "";
    expect(alignClass).toContain("bg-white");
    expect(alignClass).toContain("text-slate-900");
    expect(alignClass).toContain("border-slate-300");
    expect(alignClass).toContain("dark:bg-slate-900");
    expect(alignClass).toContain("dark:text-slate-100");
    expect(alignClass).toContain("dark:border-slate-700");
  });
});
