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

let mockState: { projectConfig: ProjectConfig; updateProjectConfig: ReturnType<typeof vi.fn> };

beforeEach(() => {
  const baseConfig: ProjectConfig = {
    title: "Project",
    slug: "project",
    canvasPreset: "full",
    canvasWidthPx: 1024,
    canvasBgColorHex: "#0f172a",
    bodyBgColorHex: "#020617",
  } as ProjectConfig;

  mockState = {
    projectConfig: baseConfig,
    updateProjectConfig: vi.fn(),
  };
});

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

vi.mock("@/features/editor/state/editorStore", () => {
  const useEditorStore = (selector: (state: any) => any) => selector(mockState);
  return {
    __esModule: true,
    useEditorStore,
  };
});

describe("ProjectPropertiesPanel", () => {
  it("캔버스 너비 프리셋을 변경하면 canvasPreset/canvasWidthPx 가 업데이트되어야 한다", () => {
    render(<ProjectPropertiesPanel />);

    const select = screen.getByLabelText("Canvas width 프리셋") as HTMLSelectElement;

    fireEvent.change(select, { target: { value: "mobile" } });

    expect(mockState.updateProjectConfig).toHaveBeenCalledWith({ canvasPreset: "mobile", canvasWidthPx: 390 });
  });

  it("태블릿 프리셋을 선택하면 canvasPreset=tablet 과 canvasWidthPx=768 로 업데이트되어야 한다", () => {
    render(<ProjectPropertiesPanel />);

    const select = screen.getByLabelText("Canvas width 프리셋") as HTMLSelectElement;

    fireEvent.change(select, { target: { value: "tablet" } });

    expect(mockState.updateProjectConfig).toHaveBeenCalledWith({ canvasPreset: "tablet", canvasWidthPx: 768 });
  });

  it("데스크톱 프리셋을 선택하면 canvasPreset=desktop 과 canvasWidthPx=1200 으로 업데이트되어야 한다", () => {
    render(<ProjectPropertiesPanel />);

    const select = screen.getByLabelText("Canvas width 프리셋") as HTMLSelectElement;

    fireEvent.change(select, { target: { value: "desktop" } });

    expect(mockState.updateProjectConfig).toHaveBeenCalledWith({ canvasPreset: "desktop", canvasWidthPx: 1200 });
  });

  it("캔버스 너비를 390 으로 입력하면 mobile 프리셋으로 동기화되어야 한다", () => {
    render(<ProjectPropertiesPanel />);

    const input = screen.getByLabelText("Canvas width 커스텀 (px)") as HTMLInputElement;

    fireEvent.change(input, { target: { value: "390" } });

    expect(mockState.updateProjectConfig).toHaveBeenCalledWith({ canvasPreset: "mobile", canvasWidthPx: 390 });
  });

  it("캔버스 너비를 768 로 입력하면 tablet 프리셋으로 동기화되어야 한다", () => {
    render(<ProjectPropertiesPanel />);

    const input = screen.getByLabelText("Canvas width 커스텀 (px)") as HTMLInputElement;

    fireEvent.change(input, { target: { value: "768" } });

    expect(mockState.updateProjectConfig).toHaveBeenCalledWith({ canvasPreset: "tablet", canvasWidthPx: 768 });
  });

  it("캔버스 너비를 1200 으로 입력하면 desktop 프리셋으로 동기화되어야 한다", () => {
    render(<ProjectPropertiesPanel />);

    const input = screen.getByLabelText("Canvas width 커스텀 (px)") as HTMLInputElement;

    fireEvent.change(input, { target: { value: "1200" } });

    expect(mockState.updateProjectConfig).toHaveBeenCalledWith({ canvasPreset: "desktop", canvasWidthPx: 1200 });
  });

  it("캔버스 너비를 커스텀 값으로 입력하면 canvasPreset=custom 과 함께 canvasWidthPx 가 설정되어야 한다", () => {
    render(<ProjectPropertiesPanel />);

    const input = screen.getByLabelText("Canvas width 커스텀 (px)") as HTMLInputElement;

    fireEvent.change(input, { target: { value: "1000" } });

    expect(mockState.updateProjectConfig).toHaveBeenCalledWith({ canvasPreset: "custom", canvasWidthPx: 1000 });
  });

  it("캔버스 배경색 HEX 입력을 변경하면 canvasBgColorHex 가 업데이트되어야 한다", () => {
    render(<ProjectPropertiesPanel />);

    const input = screen.getByLabelText("Canvas background HEX") as HTMLInputElement;

    fireEvent.change(input, { target: { value: "#123456" } });

    expect(mockState.updateProjectConfig).toHaveBeenCalledWith({ canvasBgColorHex: "#123456" });
  });

  it("페이지 배경색 HEX 입력을 변경하면 bodyBgColorHex 가 업데이트되어야 한다", () => {
    render(<ProjectPropertiesPanel />);

    const input = screen.getByLabelText("Page background HEX") as HTMLInputElement;

    fireEvent.change(input, { target: { value: "#654321" } });

    expect(mockState.updateProjectConfig).toHaveBeenCalledWith({ bodyBgColorHex: "#654321" });
  });

	it("캔버스 배경색 팔레트에서 \"None\" 을 선택하면 canvasBgColorHex 가 빈 문자열로 업데이트되어야 한다", () => {
		render(<ProjectPropertiesPanel />);

		// 첫 번째 ColorPickerField(캔버스 배경색)의 팔레트 버튼을 연다.
		const paletteButtons = screen.getAllByText("Color palette");
		fireEvent.click(paletteButtons[0].closest("button") as HTMLButtonElement);

		// 팔레트에서 "None" 항목을 선택한다.
		const noneButton = screen.getByText("None");
		fireEvent.click(noneButton);

		expect(mockState.updateProjectConfig).toHaveBeenCalledWith({ canvasBgColorHex: "" });
	});

	it("페이지 배경색 팔레트에서 \"None\" 을 선택하면 bodyBgColorHex 가 빈 문자열로 업데이트되어야 한다", () => {
		render(<ProjectPropertiesPanel />);
	
		// 두 번째 ColorPickerField(페이지 배경색)의 팔레트 버튼은 기본 선택 팔레트 라벨("Dark text")를 기준으로 찾는다.
		const darkLabel = screen.getByText("Dark text");
		fireEvent.click(darkLabel.closest("button") as HTMLButtonElement);

		const noneButton = screen.getByText("None");
		fireEvent.click(noneButton);

		expect(mockState.updateProjectConfig).toHaveBeenCalledWith({ bodyBgColorHex: "" });
	});

  it("프로젝트 제목 입력은 라이트/다크 테마에 맞는 배경/텍스트 클래스를 사용해야 한다", () => {
    render(<ProjectPropertiesPanel />);

    const input = screen.getByLabelText("Project title") as HTMLInputElement;
    const className = input.getAttribute("class") ?? "";

    // 라이트 모드: 흰 배경 + 어두운 텍스트
    expect(className).toContain("bg-white");
    expect(className).toContain("text-slate-900");
    // 다크 모드: 다크 배경 + 밝은 텍스트
    expect(className).toContain("dark:bg-slate-900");
    expect(className).toContain("dark:text-slate-100");
  });
});
