import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import {
  fireEvent,
  render,
  screen,
  waitFor,
} from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import LegalContentModal from "@/components/auth/LegalContentModal";
import type { LegalPageData } from "@/components/auth/LegalContentModal";
import type { ApiEnvelope } from "@/lib/api/types";

vi.mock("@/lib/api", async (importOriginal) => {
  const actual = await importOriginal<typeof import("@/lib/api")>();
  return {
    ...actual,
    apiFetch: vi.fn(),
  };
});

import { apiFetch } from "@/lib/api";

function getBackdrop(): HTMLElement {
  return screen.getByRole("button", { name: "إغلاق النافذة" });
}

function getCloseButton(): HTMLElement {
  const icon = document.querySelector(".ri-close-line");
  if (!icon?.parentElement) {
    throw new Error("Close button not found");
  }
  return icon.parentElement as HTMLElement;
}

function mockLegalSuccess(data: LegalPageData) {
  vi.mocked(apiFetch).mockImplementation(async (_url, options) => {
    const envelope: ApiEnvelope<LegalPageData> = {
      key: "success",
      data,
      msg: "",
    };
    options?.successCase?.(envelope);
    return envelope;
  });
}

describe("LegalContentModal", () => {
  beforeEach(() => {
    vi.clearAllMocks();
    vi.mocked(apiFetch).mockImplementation(
      () => new Promise(() => undefined),
    );
  });

  afterEach(() => {
    vi.unstubAllGlobals();
  });

  it("renders nothing when the modal is closed", () => {
    const { container } = render(
      <LegalContentModal
        isOpen={false}
        onClose={vi.fn()}
        title="الشروط والأحكام"
        endpoint="/terms"
      />,
    );

    expect(container).toBeEmptyDOMElement();
  });

  it("closes on Escape key press on the backdrop", () => {
    const onClose = vi.fn();

    render(
      <LegalContentModal
        isOpen
        onClose={onClose}
        title="الشروط والأحكام"
        endpoint="/terms"
      />,
    );

    fireEvent.keyDown(getBackdrop(), { key: "Escape" });

    expect(onClose).toHaveBeenCalledTimes(1);
  });

  it("closes when the backdrop is clicked", () => {
    const onClose = vi.fn();

    render(
      <LegalContentModal
        isOpen
        onClose={onClose}
        title="سياسة الخصوصية"
        endpoint="/privacy"
      />,
    );

    fireEvent.click(getBackdrop());

    expect(onClose).toHaveBeenCalledTimes(1);
  });

  it("closes when the header close button is clicked", async () => {
    const user = userEvent.setup();
    const onClose = vi.fn();

    render(
      <LegalContentModal
        isOpen
        onClose={onClose}
        title="الشروط والأحكام"
        endpoint="/terms"
      />,
    );

    await user.click(getCloseButton());

    expect(onClose).toHaveBeenCalledTimes(1);
  });

  it("shows a loading state while fetching legal content", () => {
    render(
      <LegalContentModal
        isOpen
        onClose={vi.fn()}
        title="الشروط والأحكام"
        endpoint="/terms"
      />,
    );

    expect(document.querySelector(".ri-loader-4-line")).toBeInTheDocument();
  });

  it("loads and displays terms content with preserved line breaks", async () => {
    mockLegalSuccess({
      content: "الفقرة الأولى\n\nالفقرة الثانية",
      image: "https://example.com/terms.png",
    });

    render(
      <LegalContentModal
        isOpen
        onClose={vi.fn()}
        title="الشروط والأحكام"
        endpoint="/terms"
      />,
    );

    expect(await screen.findByText(/الفقرة الأولى/)).toBeInTheDocument();
    expect(screen.getByText(/الفقرة الثانية/)).toBeInTheDocument();
    expect(screen.getByText(/الفقرة الأولى/).closest("p")).toBeInTheDocument();
    expect(screen.getByText(/الفقرة الثانية/).closest("p")).toBeInTheDocument();
    const image = document.querySelector('img[src="https://example.com/terms.png"]');
    expect(image).toBeInTheDocument();

    expect(apiFetch).toHaveBeenCalledWith("/terms", {
      method: "GET",
      successCase: expect.any(Function),
      errorCase: expect.any(Function),
    });
  });

  it("converts a single newline to a line break inside the same paragraph", async () => {
    mockLegalSuccess({
      content: "السطر الأول\nالسطر الثاني",
    });

    render(
      <LegalContentModal
        isOpen
        onClose={vi.fn()}
        title="الشروط والأحكام"
        endpoint="/terms"
      />,
    );

    expect(await screen.findByText(/السطر الأول/)).toBeInTheDocument();
    const paragraph = screen.getByText(/السطر الأول/).closest("p");
    expect(paragraph).toHaveTextContent(/السطر الثاني/);
    expect(document.querySelectorAll("p")).toHaveLength(1);
  });

  it("renders HTML tags from the API as real elements", async () => {
    mockLegalSuccess({
      content: "<p>شروط <strong>مهمة</strong></p><ul><li>بند واحد</li></ul>",
    });

    render(
      <LegalContentModal
        isOpen
        onClose={vi.fn()}
        title="الشروط والأحكام"
        endpoint="/terms"
      />,
    );

    const emphasis = await screen.findByText("مهمة");
    expect(emphasis.tagName).toBe("STRONG");
    expect(screen.getByText("بند واحد").closest("li")).toBeInTheDocument();
  });

  it("converts double newlines into separate paragraphs in HTML content", async () => {
    mockLegalSuccess({
      content: "<p>الفقرة الأولى</p>\n\n<p>الفقرة الثانية</p>",
    });

    render(
      <LegalContentModal
        isOpen
        onClose={vi.fn()}
        title="الشروط والأحكام"
        endpoint="/terms"
      />,
    );

    expect(await screen.findByText("الفقرة الأولى")).toBeInTheDocument();
    expect(screen.getByText("الفقرة الثانية")).toBeInTheDocument();
    expect(document.querySelectorAll("p")).toHaveLength(2);
  });

  it("fetches privacy content from the privacy endpoint", async () => {
    mockLegalSuccess({
      content: "نص سياسة الخصوصية",
    });

    render(
      <LegalContentModal
        isOpen
        onClose={vi.fn()}
        title="سياسة الخصوصية"
        endpoint="/privacy"
      />,
    );

    expect(await screen.findByText("نص سياسة الخصوصية")).toBeInTheDocument();
    expect(apiFetch).toHaveBeenCalledWith("/privacy", {
      method: "GET",
      successCase: expect.any(Function),
      errorCase: expect.any(Function),
    });
  });

  it("shows a fetch error when legal content fails to load", async () => {
    vi.mocked(apiFetch).mockImplementation(async (_url, options) => {
      const envelope: ApiEnvelope = { key: "fail", msg: "تعذر الجلب" };
      options?.errorCase?.(envelope);
      return envelope;
    });

    render(
      <LegalContentModal
        isOpen
        onClose={vi.fn()}
        title="الشروط والأحكام"
        endpoint="/terms"
      />,
    );

    expect(await screen.findByText("تعذر الجلب")).toBeInTheDocument();
  });

  it("falls back to a default fetch error message", async () => {
    vi.mocked(apiFetch).mockImplementation(async (_url, options) => {
      const envelope: ApiEnvelope = { key: "fail", msg: "" };
      options?.errorCase?.(envelope);
      return envelope;
    });

    render(
      <LegalContentModal
        isOpen
        onClose={vi.fn()}
        title="الشروط والأحكام"
        endpoint="/terms"
      />,
    );

    expect(await screen.findByText("تعذر تحميل المحتوى")).toBeInTheDocument();
  });

  it("ignores stale fetch callbacks after the modal unmounts", async () => {
    let resolveFetch!: (value: ApiEnvelope<LegalPageData>) => void;

    vi.mocked(apiFetch).mockImplementation((_url, options) => {
      return new Promise<ApiEnvelope<LegalPageData>>((resolve) => {
        resolveFetch = (envelope) => {
          if (envelope.key === "fail") {
            options?.errorCase?.(envelope);
          } else {
            options?.successCase?.(envelope);
          }
          resolve(envelope);
        };
      });
    });

    const { unmount } = render(
      <LegalContentModal
        isOpen
        onClose={vi.fn()}
        title="الشروط والأحكام"
        endpoint="/terms"
      />,
    );

    await waitFor(() => expect(apiFetch).toHaveBeenCalled());
    unmount();
    resolveFetch({ key: "fail", msg: "متأخر" });

    expect(screen.queryByText("متأخر")).not.toBeInTheDocument();
  });
});
