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

describe("ListPropertiesPanel", () => {
  const baseProps: ListBlockProps = {
    items: ["아이템 1", "아이템 2"],
    ordered: false,
    align: "left",
  };

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

  it("리스트 아이템 textarea 변경 시 items 와 itemsTree 로 updateBlock 이 호출되어야 한다", () => {
    const updateBlock = vi.fn();

    render(
      <ListPropertiesPanel
        listProps={baseProps}
        selectedBlockId="list-1"
        selectedListItemId={null}
        updateBlock={updateBlock}
      />,
    );

    const textarea = screen.getByLabelText("리스트 아이템들");
    fireEvent.change(textarea, { target: { value: "첫째\n둘째" } });

    expect(updateBlock).toHaveBeenCalledWith(
      "list-1",
      expect.objectContaining({
        items: ["첫째", "둘째"],
      }),
    );
  });

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

    render(
      <ListPropertiesPanel
        listProps={{ ...baseProps, align: "left" }}
        selectedBlockId="list-align"
        selectedListItemId={null}
        updateBlock={updateBlock}
      />,
    );

    const select = screen.getByLabelText("리스트 정렬");
    fireEvent.change(select, { target: { value: "center" } });

    expect(updateBlock).toHaveBeenCalledWith(
      "list-align",
      expect.objectContaining({ align: "center" }),
    );
  });

  it("글자 크기 슬라이더 변경 시 updateBlock 이 fontSizeCustom 을 px 문자열로 호출해야 한다", () => {
    const updateBlock = vi.fn();

    render(
      <ListPropertiesPanel
        listProps={{ ...baseProps, fontSizeCustom: "14px" }}
        selectedBlockId="list-font-size"
        selectedListItemId={null}
        updateBlock={updateBlock}
      />,
    );

    const slider = screen.getByLabelText("글자 크기 (px) 슬라이더");
    fireEvent.change(slider, { target: { value: "20" } });

    expect(updateBlock).toHaveBeenCalledWith(
      "list-font-size",
      expect.objectContaining({ fontSizeCustom: "20px" }),
    );
  });

  it("줄 간격 슬라이더 변경 시 updateBlock 이 lineHeightCustom 으로 호출되어야 한다", () => {
    const updateBlock = vi.fn();

    render(
      <ListPropertiesPanel
        listProps={{ ...baseProps, lineHeightCustom: "1.5" }}
        selectedBlockId="list-line-height"
        selectedListItemId={null}
        updateBlock={updateBlock}
      />,
    );

    const slider = screen.getByLabelText("줄 간격 슬라이더");
    fireEvent.change(slider, { target: { value: "1.8" } });

    expect(updateBlock).toHaveBeenCalledWith(
      "list-line-height",
      expect.objectContaining({ lineHeightCustom: "1.8" }),
    );
  });

  it("텍스트 색상 HEX 인풋 변경 시 updateBlock 이 textColorCustom 으로 호출되어야 한다", () => {
    const updateBlock = vi.fn();

    render(
      <ListPropertiesPanel
        listProps={{ ...baseProps, textColorCustom: "#ffffff" }}
        selectedBlockId="list-text-color"
        selectedListItemId={null}
        updateBlock={updateBlock}
      />,
    );

    const hexInput = screen.getByLabelText("리스트 텍스트 색상 HEX");
    fireEvent.change(hexInput, { target: { value: "#112233" } });

    expect(updateBlock).toHaveBeenCalledWith(
      "list-text-color",
      expect.objectContaining({ textColorCustom: "#112233" }),
    );
  });

	it("텍스트 색상 팔레트에서 \"없음\" 을 선택하면 textColorCustom 이 빈 문자열로 업데이트되어야 한다", () => {
		const updateBlock = vi.fn();

		render(
			<ListPropertiesPanel
				listProps={{ ...baseProps, textColorCustom: "#ff0000" }}
				selectedBlockId="list-text-none"
				selectedListItemId={null}
				updateBlock={updateBlock}
			/>,
		);

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

		// 팔레트에서 "없음" 항목을 버튼 role 기준으로 선택한다 (불릿 스타일 select 의 option "없음" 과 구분).
		const noneButtons = screen.getAllByRole("button", { name: "없음" });
		fireEvent.click(noneButtons[0]);

		expect(updateBlock).toHaveBeenCalledWith(
			"list-text-none",
			expect.objectContaining({ textColorCustom: "" }),
		);
	});

	it("textColorCustom 이 비어 있으면 텍스트 색상 HEX 인풋 값은 빈 문자열이어야 한다", () => {
		const updateBlock = vi.fn();

		render(
			<ListPropertiesPanel
				listProps={{ ...baseProps, textColorCustom: "" }}
				selectedBlockId="list-text-empty"
				selectedListItemId={null}
				updateBlock={updateBlock}
			/>,
		);

		const hexInput = screen.getByLabelText("리스트 텍스트 색상 HEX") as HTMLInputElement;
		expect(hexInput.value).toBe("");
	});

  it("불릿 스타일 셀렉트 변경 시 bulletStyle 과 ordered 가 함께 업데이트되어야 한다 (decimal)", () => {
    const updateBlock = vi.fn();

    render(
      <ListPropertiesPanel
        listProps={{ ...baseProps, bulletStyle: "disc", ordered: false }}
        selectedBlockId="list-bullet-decimal"
        selectedListItemId={null}
        updateBlock={updateBlock}
      />,
    );

    const select = screen.getByLabelText("리스트 불릿 스타일");
    fireEvent.change(select, { target: { value: "decimal" } });

    expect(updateBlock).toHaveBeenCalledWith(
      "list-bullet-decimal",
      expect.objectContaining({ bulletStyle: "decimal", ordered: true }),
    );
  });

  it("불릿 스타일 셀렉트에서 none 선택 시 ordered 가 false 로 설정되어야 한다", () => {
    const updateBlock = vi.fn();

    render(
      <ListPropertiesPanel
        listProps={{ ...baseProps, bulletStyle: "decimal", ordered: true }}
        selectedBlockId="list-bullet-none"
        selectedListItemId={null}
        updateBlock={updateBlock}
      />,
    );

    const select = screen.getByLabelText("리스트 불릿 스타일");
    fireEvent.change(select, { target: { value: "none" } });

    expect(updateBlock).toHaveBeenCalledWith(
      "list-bullet-none",
      expect.objectContaining({ bulletStyle: "none", ordered: false }),
    );
  });

  it("아이템 간 여백 슬라이더 변경 시 updateBlock 이 gapYPx 로 호출되어야 한다", () => {
    const updateBlock = vi.fn();

    render(
      <ListPropertiesPanel
        listProps={{ ...baseProps, gapYPx: 8 }}
        selectedBlockId="list-gapY"
        selectedListItemId={null}
        updateBlock={updateBlock}
      />,
    );

    const slider = screen.getByLabelText("아이템 간 여백 (px) 슬라이더");
    fireEvent.change(slider, { target: { value: "16" } });

    expect(updateBlock).toHaveBeenCalledWith(
      "list-gapY",
      expect.objectContaining({ gapYPx: 16 }),
    );
  });

  it("리스트 정렬 셀렉트는 라이트/다크 듀얼 테마 크롬을 사용해야 한다", () => {
    const updateBlock = vi.fn();

    render(
      <ListPropertiesPanel
        listProps={baseProps}
        selectedBlockId="list-align-theme"
        selectedListItemId={null}
        updateBlock={updateBlock}
      />,
    );

    const select = screen.getByLabelText("리스트 정렬") as HTMLSelectElement;
    const className = select.getAttribute("class") ?? "";

    expect(className).toContain("bg-white");
    expect(className).toContain("text-slate-900");
    expect(className).toContain("border-slate-300");
    expect(className).toContain("dark:bg-slate-900");
    expect(className).toContain("dark:text-slate-100");
    expect(className).toContain("dark:border-slate-700");
  });

  it("리스트 불릿 스타일 셀렉트는 라이트/다크 듀얼 테마 크롬을 사용해야 한다", () => {
    const updateBlock = vi.fn();

    render(
      <ListPropertiesPanel
        listProps={baseProps}
        selectedBlockId="list-bullet-theme"
        selectedListItemId={null}
        updateBlock={updateBlock}
      />,
    );

    const select = screen.getByLabelText("리스트 불릿 스타일") as HTMLSelectElement;
    const className = select.getAttribute("class") ?? "";

    expect(className).toContain("bg-white");
    expect(className).toContain("text-slate-900");
    expect(className).toContain("border-slate-300");
    expect(className).toContain("dark:bg-slate-900");
    expect(className).toContain("dark:text-slate-100");
    expect(className).toContain("dark:border-slate-700");
  });

  it("리스트 아이템 textarea 는 라이트/다크 테마에 맞는 배경/텍스트 클래스를 사용해야 한다", () => {
    const updateBlock = vi.fn();

    render(
      <ListPropertiesPanel
        listProps={baseProps}
        selectedBlockId="list-theme"
        selectedListItemId={null}
        updateBlock={updateBlock}
      />,
    );

    const textarea = screen.getByLabelText("리스트 아이템들") as HTMLTextAreaElement;
    const className = textarea.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");
  });
});
