import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import { expect, test } from "vitest";

import type { UploadAudioContentRequest } from "@/features/audio-contents/components/AudioContentForm";
import { AudioContentFormPage } from "@/features/audio-contents/pages/AudioContentFormPage";
import type { CapturedRequest } from "@/features/audio-contents/tests/audio-form-test-support";
import { createFormClient, fileWithSize, readJsonPart, requireFormData } from "@/features/audio-contents/tests/audio-form-test-support";

function createSuccessfulUpload(uploadedBodies: XMLHttpRequestBodyInit[]): UploadAudioContentRequest {
  return async (options) => {
    uploadedBodies.push(options.body);
    return options.responseSchema.parse({ contentId: 9301 });
  };
}

function renderCreateForm(uploadedBodies: XMLHttpRequestBodyInit[] = []) {
  const requests: CapturedRequest[] = [];
  window.history.pushState({}, "", "/ai-characters/101/audio-contents/new");
  render(
    <AudioContentFormPage
      apiClient={createFormClient(requests)}
      characterId="101"
      createCropSource={(file) => Promise.resolve({ file, height: 800, previewUrl: "blob:cover", width: 800 })}
      renderCrop={(request) => Promise.resolve(request.file)}
      uploadAudioContentRequest={createSuccessfulUpload(uploadedBodies)}
    />,
  );
}

async function fillRequiredCreateFields() {
  fireEvent.change(await screen.findByLabelText("제목"), { target: { value: "옵션 오디오" } });
  fireEvent.change(screen.getByLabelText("상세 설명"), { target: { value: "옵션 설명" } });
  fireEvent.change(screen.getByLabelText("태그"), { target: { value: "옵션" } });
  fireEvent.keyDown(screen.getByLabelText("태그"), { key: "Enter" });
  fireEvent.change(screen.getByLabelText("오디오 테마"), { target: { value: "7" } });
  fireEvent.change(screen.getByLabelText("오디오 파일"), { target: { files: [fileWithSize("voice.mp3", "audio/mpeg", 10)] } });
  fireEvent.change(screen.getByLabelText("커버 이미지"), { target: { files: [new File(["cover"], "cover.png", { type: "image/png" })] } });
  fireEvent.click(await screen.findByRole("button", { name: "적용" }));
  await waitFor(() => expect(screen.queryByRole("button", { name: "적용" })).not.toBeInTheDocument());
}

test("AudioContentFormPage commits tag chips with Enter and comma without submitting, then removes one with a native button", async () => {
  // Given
  const uploadedBodies: XMLHttpRequestBodyInit[] = [];
  renderCreateForm(uploadedBodies);
  const tagInput = await screen.findByLabelText("태그");

  // When
  fireEvent.change(tagInput, { target: { value: "상담" } });
  fireEvent.keyDown(tagInput, { key: "Enter" });
  fireEvent.change(tagInput, { target: { value: "힐링" } });
  fireEvent.keyDown(tagInput, { key: "," });

  // Then
  expect(tagInput).toHaveValue("");
  expect(uploadedBodies).toHaveLength(0);
  const removeConsulting = screen.getByRole("button", { name: "태그 상담 삭제" });
  expect(removeConsulting.tagName).toBe("BUTTON");
  expect(screen.getByRole("button", { name: "태그 힐링 삭제" })).toBeInTheDocument();

  // When
  fireEvent.click(removeConsulting);

  // Then
  expect(screen.queryByRole("button", { name: "태그 상담 삭제" })).not.toBeInTheDocument();
  expect(screen.getByRole("button", { name: "태그 힐링 삭제" })).toBeInTheDocument();
});

test.each([
  "-1",
  "1.5",
  "100000",
])("AudioContentFormPage preserves the raw price input %s", async (value) => {
  // Given
  renderCreateForm();
  const priceInput = await screen.findByLabelText("가격");
  expect(priceInput).toHaveValue(0);
  expect(priceInput).toHaveAttribute("type", "number");
  expect(priceInput).toHaveAttribute("min", "0");
  expect(priceInput).toHaveAttribute("step", "1");

  // When
  fireEvent.change(priceInput, { target: { value } });

  // Then
  expect(priceInput).toHaveValue(Number(value));
});

test("AudioContentFormPage shows paid options only for a positive price", async () => {
  // Given
  renderCreateForm();
  const priceInput = await screen.findByLabelText("가격");

  // When
  fireEvent.change(priceInput, { target: { value: "0" } });

  // Then
  expect(screen.queryByRole("group", { name: "구매 옵션" })).not.toBeInTheDocument();
  expect(screen.queryByLabelText("미리듣기 생성")).not.toBeInTheDocument();
  expect(screen.queryByLabelText("포인트 사용")).not.toBeInTheDocument();
  expect(screen.queryByLabelText("미리듣기 시작")).not.toBeInTheDocument();
  expect(screen.queryByLabelText("미리듣기 종료")).not.toBeInTheDocument();

  // When
  fireEvent.change(priceInput, { target: { value: "1" } });

  // Then
  expect(screen.getByRole("group", { name: "구매 옵션" })).toBeInTheDocument();
  expect(screen.getByLabelText("미리듣기 생성")).toBeInTheDocument();
  expect(screen.getByLabelText("포인트 사용")).toBeInTheDocument();
});

test("AudioContentFormPage resets paid settings and retains removed-control defaults when price is numeric zero", async () => {
  // Given
  const uploadedBodies: XMLHttpRequestBodyInit[] = [];
  renderCreateForm(uploadedBodies);
  await fillRequiredCreateFields();
  const priceInput = screen.getByLabelText("가격");
  fireEvent.change(priceInput, { target: { value: "1" } });
  fireEvent.click(screen.getByRole("radio", { name: "대여 전용" }));
  fireEvent.click(screen.getByLabelText("미리듣기 생성"));
  fireEvent.click(screen.getByLabelText("포인트 사용"));
  fireEvent.change(screen.getByLabelText("미리듣기 시작"), { target: { value: "00:00:30" } });
  fireEvent.change(screen.getByLabelText("미리듣기 종료"), { target: { value: "01:00:05" } });

  // When
  fireEvent.change(priceInput, { target: { value: "00" } });

  // Then
  expect(screen.queryByRole("group", { name: "구매 옵션" })).not.toBeInTheDocument();
  expect(screen.queryByLabelText("미리듣기 생성")).not.toBeInTheDocument();
  expect(screen.queryByLabelText("포인트 사용")).not.toBeInTheDocument();
  expect(screen.queryByLabelText("미리듣기 시작")).not.toBeInTheDocument();
  expect(screen.queryByLabelText("미리듣기 종료")).not.toBeInTheDocument();
  expect(screen.queryByLabelText("기간제")).not.toBeInTheDocument();
  expect(screen.queryByRole("checkbox", { name: "대여 전용" })).not.toBeInTheDocument();
  expect(screen.queryByLabelText("언어 코드")).not.toBeInTheDocument();

  // When
  fireEvent.click(screen.getByRole("button", { name: "생성" }));

  // Then
  await waitFor(() => expect(uploadedBodies).toHaveLength(1));
  expect(await readJsonPart(requireFormData(uploadedBodies.at(-1)).get("request"))).toMatchObject({
    price: 0,
    purchaseOption: "BOTH",
    limited: null,
    isGeneratePreview: false,
    isOnlyRental: false,
    isPointAvailable: false,
    previewStartTime: null,
    previewEndTime: null,
    languageCode: null,
  });
});

test("AudioContentFormPage reveals preview duration offset inputs only when preview generation is enabled and submits full HH:MM:SS values", async () => {
  // Given
  const uploadedBodies: XMLHttpRequestBodyInit[] = [];
  renderCreateForm(uploadedBodies);
  await fillRequiredCreateFields();
  fireEvent.change(screen.getByLabelText("가격"), { target: { value: "1" } });
  expect(screen.queryByLabelText("미리듣기 시작")).not.toBeInTheDocument();
  expect(screen.queryByLabelText("미리듣기 종료")).not.toBeInTheDocument();

  // When
  fireEvent.click(screen.getByLabelText("미리듣기 생성"));

  // Then
  const previewStart = screen.getByLabelText("미리듣기 시작");
  const previewEnd = screen.getByLabelText("미리듣기 종료");
  expect(previewStart).toHaveAttribute("type", "text");
  expect(previewStart).toHaveAttribute("placeholder", "예: 00:00:30");
  expect(previewStart).toHaveAttribute("pattern", "[0-9]{2}:[0-9]{2}:[0-9]{2}");
  expect(previewStart).not.toHaveAttribute("step");
  expect(previewEnd).toHaveAttribute("type", "text");
  expect(previewEnd).toHaveAttribute("placeholder", "예: 01:00:05");
  expect(previewEnd).toHaveAttribute("pattern", "[0-9]{2}:[0-9]{2}:[0-9]{2}");
  expect(previewEnd).not.toHaveAttribute("step");

  // When
  fireEvent.change(previewStart, { target: { value: "00:00:30" } });
  fireEvent.change(previewEnd, { target: { value: "01:00:05" } });
  fireEvent.click(screen.getByRole("button", { name: "생성" }));

  // Then
  await waitFor(() => expect(uploadedBodies).toHaveLength(1));
  expect(await readJsonPart(requireFormData(uploadedBodies.at(-1)).get("request"))).toMatchObject({
    previewStartTime: "00:00:30",
    previewEndTime: "01:00:05",
  });
});

test("AudioContentFormPage rejects malformed preview offsets through the submit button", async () => {
  // Given
  const uploadedBodies: XMLHttpRequestBodyInit[] = [];
  renderCreateForm(uploadedBodies);
  await fillRequiredCreateFields();
  fireEvent.change(screen.getByLabelText("가격"), { target: { value: "1" } });
  fireEvent.click(screen.getByLabelText("미리듣기 생성"));
  const previewStart = screen.getByLabelText("미리듣기 시작");
  const previewEnd = screen.getByLabelText("미리듣기 종료");

  // When
  fireEvent.change(previewStart, { target: { value: "30" } });
  fireEvent.change(previewEnd, { target: { value: "01:00" } });
  fireEvent.click(screen.getByRole("button", { name: "생성" }));

  // Then
  expect(await screen.findAllByText("미리듣기 시간은 HH:MM:SS 형식으로 입력하세요.")).toHaveLength(2);
  expect(previewStart).toHaveAttribute("aria-invalid", "true");
  expect(previewEnd).toHaveAttribute("aria-invalid", "true");
  await waitFor(() => expect(previewStart).toHaveFocus());
  expect(uploadedBodies).toHaveLength(0);
});

test("AudioContentFormPage conditionally renders and clears the native scheduled release datetime", async () => {
  // Given
  renderCreateForm();
  await screen.findByLabelText("즉시 공개");
  expect(screen.queryByLabelText("예약 공개일")).not.toBeInTheDocument();

  // When
  fireEvent.click(screen.getByLabelText("예약 공개"));

  // Then
  const scheduledInput = screen.getByLabelText("예약 공개일");
  expect(scheduledInput).toHaveAttribute("type", "datetime-local");

  // When
  fireEvent.change(scheduledInput, { target: { value: "2026-08-05T12:00" } });
  fireEvent.click(screen.getByLabelText("즉시 공개"));

  // Then
  expect(screen.queryByLabelText("예약 공개일")).not.toBeInTheDocument();

  // When
  fireEvent.click(screen.getByLabelText("예약 공개"));

  // Then
  expect(screen.getByLabelText("예약 공개일")).toHaveValue("");
});

test("AudioContentFormPage turns edit tags into removable chips and serializes the remaining chips as a comma string", async () => {
  // Given
  const requests: CapturedRequest[] = [];
  window.history.pushState({}, "", "/ai-characters/101/audio-contents/9001/edit");
  render(<AudioContentFormPage apiClient={createFormClient(requests)} characterId="101" contentId="9001" />);

  // Then
  await waitFor(() => expect(screen.queryByRole("button", { name: "태그 상담 삭제" })).toBeInTheDocument());
  expect(screen.getByRole("button", { name: "태그 힐링 삭제" })).toBeInTheDocument();

  // When
  fireEvent.click(screen.getByRole("button", { name: "태그 상담 삭제" }));
  fireEvent.change(screen.getByLabelText("태그"), { target: { value: "집중" } });
  fireEvent.keyDown(screen.getByLabelText("태그"), { key: "Enter" });
  fireEvent.click(screen.getByRole("button", { name: "저장" }));

  // Then
  await waitFor(() => expect(window.location.pathname).toBe("/ai-characters/101/audio-contents/9001"));
  expect(await readJsonPart(requireFormData(requests.at(-1)?.body).get("request"))).toMatchObject({ tags: "힐링,집중" });
});
