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

// VideoPropertiesPanel 컨트롤 TDD
// - 비디오 URL 인풋/정렬/너비 모드/화면 비율 컨트롤이 updateBlock 을 올바르게 호출하는지 검증한다.

describe("VideoPropertiesPanel", () => {
  const baseProps: VideoBlockProps = {
    sourceUrl: "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    align: "center",
    widthMode: "auto",
    aspectRatio: "16:9",
  };

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

  it("비디오 URL 인풋 변경 시 updateBlock 이 sourceUrl / sourceType / assetId 로 호출되어야 한다", () => {
    const updateBlock = vi.fn();

    render(
      <VideoPropertiesPanel
        videoProps={{ ...baseProps, sourceType: "externalUrl" }}
        selectedBlockId="video-1"
        updateBlock={updateBlock}
      />,
    );

    const urlInput = screen.getByLabelText("비디오 URL");
    fireEvent.change(urlInput, { target: { value: "https://example.com/video.mp4" } });

    expect(updateBlock).toHaveBeenCalledWith(
      "video-1",
      expect.objectContaining({
        sourceUrl: "https://example.com/video.mp4",
        sourceType: "externalUrl",
        assetId: null,
      }),
    );
  });

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

    render(
      <VideoPropertiesPanel
        videoProps={{ ...baseProps, align: "center" }}
        selectedBlockId="video-2"
        updateBlock={updateBlock}
      />,
    );

    const select = screen.getByLabelText("비디오 정렬");
    fireEvent.change(select, { target: { value: "left" } });

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

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

    render(
      <VideoPropertiesPanel
        videoProps={{ ...baseProps, widthMode: "auto" }}
        selectedBlockId="video-3"
        updateBlock={updateBlock}
      />,
    );

    const select = screen.getByLabelText("비디오 너비 모드");
    fireEvent.change(select, { target: { value: "fixed" } });

    expect(updateBlock).toHaveBeenCalledWith(
      "video-3",
      expect.objectContaining({ widthMode: "fixed" }),
    );
  });

  it("화면 비율 셀렉트 변경 시 updateBlock 이 aspectRatio 로 호출되어야 한다", () => {
    const updateBlock = vi.fn();

    render(
      <VideoPropertiesPanel
        videoProps={{ ...baseProps, aspectRatio: "16:9" }}
        selectedBlockId="video-4"
        updateBlock={updateBlock}
      />,
    );

    const select = screen.getByLabelText("화면 비율");
    fireEvent.change(select, { target: { value: "4:3" } });

    expect(updateBlock).toHaveBeenCalledWith(
      "video-4",
      expect.objectContaining({ aspectRatio: "4:3" }),
    );
  });

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

    render(
      <VideoPropertiesPanel
        videoProps={{ ...baseProps, sourceType: "externalUrl" }}
        selectedBlockId="video-card-1"
        updateBlock={updateBlock}
      />, 
    );

    const hexInput = screen.getByLabelText("비디오 카드 배경색 HEX");
    fireEvent.change(hexInput, { target: { value: "#123456" } });

    expect(updateBlock).toHaveBeenCalledWith(
      "video-card-1",
      expect.objectContaining({ backgroundColorCustom: "#123456" }),
    );
  });

  it("카드 패딩 슬라이더 변경 시 updateBlock 이 cardPaddingPx 로 호출되어야 한다", () => {
    const updateBlock = vi.fn();

    render(
      <VideoPropertiesPanel
        videoProps={{ ...baseProps, cardPaddingPx: 16 }}
        selectedBlockId="video-card-2"
        updateBlock={updateBlock}
      />, 
    );

    const slider = screen.getByLabelText("카드 패딩 슬라이더");
    fireEvent.change(slider, { target: { value: "24" } });

    expect(updateBlock).toHaveBeenCalledWith(
      "video-card-2",
      expect.objectContaining({ cardPaddingPx: 24 }),
    );
  });

  it("카드 모서리 둥글기 슬라이더 변경 시 updateBlock 이 borderRadiusPx 로 호출되어야 한다", () => {
    const updateBlock = vi.fn();

    render(
      <VideoPropertiesPanel
        videoProps={{ ...baseProps, borderRadiusPx: 8 }}
        selectedBlockId="video-card-3"
        updateBlock={updateBlock}
      />, 
    );

    const slider = screen.getByLabelText("카드 모서리 둥글기 슬라이더");
    fireEvent.change(slider, { target: { value: "20" } });

    expect(updateBlock).toHaveBeenCalledWith(
      "video-card-3",
      expect.objectContaining({ borderRadiusPx: 20 }),
    );
  });

  it("포스터 이미지 URL 인풋 변경 시 updateBlock 이 posterImageSrc / posterSourceType / posterAssetId 로 호출되어야 한다", () => {
    const updateBlock = vi.fn();

    const posterProps: VideoBlockProps = {
      ...baseProps,
      sourceType: "externalUrl",
      posterImageSrc: "https://example.com/poster-before.png",
      posterSourceType: "externalUrl",
      posterAssetId: null,
    };

    render(
      <VideoPropertiesPanel
        videoProps={posterProps}
        selectedBlockId="video-poster-1"
        updateBlock={updateBlock}
      />,
    );

    const input = screen.getByLabelText("포스터 이미지 URL");
    fireEvent.change(input, { target: { value: "https://example.com/poster-after.png" } });

    expect(updateBlock).toHaveBeenCalledWith(
      "video-poster-1",
      expect.objectContaining({
        posterImageSrc: "https://example.com/poster-after.png",
        posterSourceType: "externalUrl",
        posterAssetId: null,
      }),
    );
  });

  it("시작 시점 슬라이더 변경 시 updateBlock 이 startTimeSec 로 호출되어야 한다", () => {
    const updateBlock = vi.fn();

    render(
      <VideoPropertiesPanel
        videoProps={{ ...baseProps, startTimeSec: 0 }}
        selectedBlockId="video-range-1"
        updateBlock={updateBlock}
      />,
    );

    const slider = screen.getByLabelText("시작 시점 (초) 슬라이더");
    fireEvent.change(slider, { target: { value: "5" } });

    expect(updateBlock).toHaveBeenCalledWith(
      "video-range-1",
      expect.objectContaining({ startTimeSec: 5 }),
    );
  });

  it("종료 시점 슬라이더 변경 시 updateBlock 이 endTimeSec 로 호출되어야 한다", () => {
    const updateBlock = vi.fn();

    render(
      <VideoPropertiesPanel
        videoProps={{ ...baseProps, endTimeSec: 10 }}
        selectedBlockId="video-range-2"
        updateBlock={updateBlock}
      />,
    );

    const slider = screen.getByLabelText("종료 시점 (초) 슬라이더");
    fireEvent.change(slider, { target: { value: "15" } });

    expect(updateBlock).toHaveBeenCalledWith(
      "video-range-2",
      expect.objectContaining({ endTimeSec: 15 }),
    );
  });

  it("비디오 제목 인풋 변경 시 updateBlock 이 titleText 로 호출되어야 한다", () => {
    const updateBlock = vi.fn();

    render(
      <VideoPropertiesPanel
        videoProps={{ ...baseProps, titleText: "기존 제목" } as any}
        selectedBlockId="video-a11y-1"
        updateBlock={updateBlock}
      />,
    );

    const input = screen.getByLabelText("비디오 제목");
    fireEvent.change(input, { target: { value: "새 비디오 제목" } });

    expect(updateBlock).toHaveBeenCalledWith(
      "video-a11y-1",
      expect.objectContaining({ titleText: "새 비디오 제목" }),
    );
  });

  it("비디오 aria-label 인풋 변경 시 updateBlock 이 ariaLabel 로 호출되어야 한다", () => {
    const updateBlock = vi.fn();

    render(
      <VideoPropertiesPanel
        videoProps={{ ...baseProps, ariaLabel: "기존 aria" } as any}
        selectedBlockId="video-a11y-2"
        updateBlock={updateBlock}
      />,
    );

    const input = screen.getByLabelText("비디오 aria-label");
    fireEvent.change(input, { target: { value: "새 aria-label" } });

    expect(updateBlock).toHaveBeenCalledWith(
      "video-a11y-2",
      expect.objectContaining({ ariaLabel: "새 aria-label" }),
    );
  });

  it("비디오 캡션 텍스트 인풋 변경 시 updateBlock 이 captionText 로 호출되어야 한다", () => {
    const updateBlock = vi.fn();

    render(
      <VideoPropertiesPanel
        videoProps={{ ...baseProps, captionText: "기존 캡션" } as any}
        selectedBlockId="video-a11y-3"
        updateBlock={updateBlock}
      />,
    );

    const input = screen.getByLabelText("비디오 캡션 텍스트");
    fireEvent.change(input, { target: { value: "새 캡션 텍스트" } });

    expect(updateBlock).toHaveBeenCalledWith(
      "video-a11y-3",
      expect.objectContaining({ captionText: "새 캡션 텍스트" }),
    );
  });
});
