import {
  cleanup,
  fireEvent,
  render,
  screen,
  waitFor,
} from "@testing-library/react";
import { afterEach, describe, expect, it, vi } from "vitest";
import { QuestionSnapList } from "./question-snap-list";
import {
  computeQuestionLift,
  updateQuestionKeyboardHeight,
} from "./question-viewport-coordinator";

vi.mock("@/translations/provider", () => ({
  useI18n: () => ({ dictionary: {} }),
}));

describe("QuestionSnapList keyboard interaction", () => {
  afterEach(() => {
    cleanup();
    document.body.classList.remove("question-keyboard-open");
    document.documentElement.style.removeProperty("--question-lift-y");
  });

  it("computes a stable occlusion lift without overshoot", () => {
    expect(
      computeQuestionLift({
        baseTop: 300,
        baseBottom: 600,
        visibleTop: 100,
        visibleBottom: 500,
      }),
    ).toBe(150);
    expect(
      computeQuestionLift({
        baseTop: 300,
        baseBottom: 600,
        visibleTop: 100,
        visibleBottom: 800,
      }),
    ).toBe(0);
  });

  it("does not treat pointer contact as input focus", () => {
    render(
      <QuestionSnapList>
        <input aria-label="Question one" type="text" />
      </QuestionSnapList>,
    );

    fireEvent.pointerDown(
      screen.getByRole("textbox", { name: "Question one" }),
    );

    expect(document.body).not.toHaveClass("question-keyboard-open");
  });

  it("activates only on real focus and resets before changing questions", async () => {
    render(
      <QuestionSnapList>
        <input aria-label="Question one" type="text" />
        <input aria-label="Question two" type="text" />
      </QuestionSnapList>,
    );

    const firstInput = screen.getByRole("textbox", { name: "Question one" });
    const secondInput = document.querySelector<HTMLInputElement>(
      'input[aria-label="Question two"]',
    );
    const content = firstInput.closest<HTMLElement>(".question-snap-content");
    vi.spyOn(content as HTMLElement, "getBoundingClientRect").mockReturnValue({
      top: 300,
      bottom: 600,
      left: 0,
      right: 300,
      width: 300,
      height: 300,
      x: 0,
      y: 300,
      toJSON: () => ({}),
    });

    firstInput.focus();
    updateQuestionKeyboardHeight(300);
    await waitFor(() => {
      expect(document.body).toHaveClass("question-keyboard-open");
    });

    fireEvent.wheel(screen.getByRole("region", { name: "Questions" }), {
      deltaY: 100,
    });

    await waitFor(() => {
      expect(document.body).not.toHaveClass("question-keyboard-open");
      expect(firstInput).not.toHaveFocus();
      expect(secondInput).not.toHaveFocus();
      expect(
        document.documentElement.style.getPropertyValue("--question-lift-y"),
      ).toBe("0px");
    });
  });

  it("does not keep question keyboard state for a dialog input", async () => {
    render(
      <>
        <QuestionSnapList>
          <input aria-label="Question input" type="text" />
        </QuestionSnapList>
        <div role="dialog">
          <input aria-label="Dialog search" type="text" />
        </div>
      </>,
    );

    const questionInput = screen.getByRole("textbox", {
      name: "Question input",
    });
    const content = questionInput.closest<HTMLElement>(
      ".question-snap-content",
    );
    vi.spyOn(content as HTMLElement, "getBoundingClientRect").mockReturnValue({
      top: 300,
      bottom: 600,
      left: 0,
      right: 300,
      width: 300,
      height: 300,
      x: 0,
      y: 300,
      toJSON: () => ({}),
    });

    questionInput.focus();
    updateQuestionKeyboardHeight(300);
    await waitFor(() => {
      expect(document.body).toHaveClass("question-keyboard-open");
    });

    screen.getByRole("textbox", { name: "Dialog search" }).focus();

    await waitFor(() => {
      expect(document.body).not.toHaveClass("question-keyboard-open");
    });
  });

  it("raises again when Flutter reopens the keyboard without another focusin", async () => {
    render(
      <QuestionSnapList>
        <input aria-label="Persistent input" type="text" />
      </QuestionSnapList>,
    );

    const input = screen.getByRole("textbox", { name: "Persistent input" });
    const content = input.closest<HTMLElement>(".question-snap-content");
    vi.spyOn(content as HTMLElement, "getBoundingClientRect").mockReturnValue({
      top: 300,
      bottom: 600,
      left: 0,
      right: 300,
      width: 300,
      height: 300,
      x: 0,
      y: 300,
      toJSON: () => ({}),
    });

    input.focus();
    updateQuestionKeyboardHeight(300);
    await waitFor(() =>
      expect(document.body).toHaveClass("question-keyboard-open"),
    );

    updateQuestionKeyboardHeight(0);
    await waitFor(() => {
      expect(document.body).not.toHaveClass("question-keyboard-open");
      expect(input).toHaveFocus();
    });

    updateQuestionKeyboardHeight(300);
    await waitFor(() => {
      expect(document.body).toHaveClass("question-keyboard-open");
      expect(input).toHaveFocus();
    });
  });
});
