/**
 * @vitest-environment jsdom
 */

import { describe, it, expect, vi } from "vitest";
import { render, screen, fireEvent } from "@testing-library/react";
import { CityPickerPopup } from "./CityPickerPopup.js";
import { transformDictionaries } from "@/shared/dictionaries/index.js";
import type { IRawDictionaries } from "@/shared/dictionaries/index.js";

const raw: IRawDictionaries = {
  regions: [
    { world_region_id: 500374, title: { ru: "Россия и страны СНГ" } },
    { world_region_id: 1, title: { ru: "Азия" } },
  ],
  countries: [
    { code: "RU", title: { ru: "Россия" }, world_region_id: 500374 },
    { code: "CN", title: { ru: "Китай" }, world_region_id: 1 },
  ],
  cities: [
    { code: "MOW", title: { ru: "Москва" }, country_code: "RU", has_afl_flights: true, location: { lat: 55, lon: 37 } },
    { code: "LED", title: { ru: "Санкт-Петербург" }, country_code: "RU", has_afl_flights: true, location: { lat: 60, lon: 30 } },
    { code: "BJS", title: { ru: "Пекин" }, country_code: "CN", has_afl_flights: true, location: { lat: 39, lon: 116 } },
  ],
  airports: [
    { code: "SVO", city_code: "MOW", title: { ru: "Шереметьево" }, has_afl_flights: true, location: { lat: 55, lon: 37 } },
    { code: "DME", city_code: "MOW", title: { ru: "Домодедово" }, has_afl_flights: true, location: { lat: 55, lon: 37 } },
    { code: "LED", city_code: "LED", title: { ru: "Пулково" }, has_afl_flights: true, location: { lat: 60, lon: 30 } },
    { code: "PEK", city_code: "BJS", title: { ru: "Пекин аэропорт" }, has_afl_flights: true, location: { lat: 39, lon: 116 } },
  ],
};
const dictionaries = transformDictionaries(raw, "ru");

describe("CityPickerPopup", () => {
  it("renders a tab button per region", () => {
    render(<CityPickerPopup dictionaries={dictionaries} onSelect={vi.fn()} />);
    expect(screen.getByText("Россия и страны СНГ")).toBeTruthy();
    expect(screen.getByText("Азия")).toBeTruthy();
  });

  it("shows the first region's rows by default", () => {
    render(<CityPickerPopup dictionaries={dictionaries} onSelect={vi.fn()} />);
    expect(screen.getByText("Москва")).toBeTruthy();
  });

  it("switches to another region when tab clicked", () => {
    render(<CityPickerPopup dictionaries={dictionaries} onSelect={vi.fn()} />);
    fireEvent.click(screen.getByText("Азия"));
    expect(screen.getByText("Пекин")).toBeTruthy();
  });

  it("calls onSelect with city code when city cell clicked", () => {
    const onSelect = vi.fn();
    render(<CityPickerPopup dictionaries={dictionaries} onSelect={onSelect} />);
    fireEvent.click(screen.getByText("Санкт-Петербург"));
    expect(onSelect).toHaveBeenCalledWith("LED");
  });

  it("calls onSelect with airport code when airport cell clicked (multi-airport city)", () => {
    const onSelect = vi.fn();
    render(<CityPickerPopup dictionaries={dictionaries} onSelect={onSelect} />);
    fireEvent.click(screen.getByText("Шереметьево"));
    expect(onSelect).toHaveBeenCalledWith("SVO");
  });

  it("highlights the selectedCode cell", () => {
    const { container } = render(
      <CityPickerPopup dictionaries={dictionaries} selectedCode="LED" onSelect={vi.fn()} />,
    );
    const activeCells = container.querySelectorAll(".city-active");
    expect(activeCells.length).toBeGreaterThan(0);
  });

  it("hides GPS button when onLocate is not provided", () => {
    render(<CityPickerPopup dictionaries={dictionaries} onSelect={vi.fn()} />);
    expect(screen.queryByTestId("city-picker-locate")).toBeNull();
  });

  it("shows GPS button when onLocate is provided and invokes it on click", () => {
    const onLocate = vi.fn();
    render(
      <CityPickerPopup dictionaries={dictionaries} onSelect={vi.fn()} onLocate={onLocate} />,
    );
    fireEvent.click(screen.getByTestId("city-picker-locate"));
    expect(onLocate).toHaveBeenCalled();
  });

  // ── §4.1.9.2 assertion tests ──────────────────────────────────────────────

  it("4.1.9.2-R1: selected item has city-active CSS class (visual highlight)", () => {
    const { container } = render(
      <CityPickerPopup dictionaries={dictionaries} selectedCode="LED" onSelect={vi.fn()} />,
    );
    const activeCells = container.querySelectorAll(".city-active");
    expect(activeCells.length).toBeGreaterThan(0);
  });

  it("4.1.9.2-R2: item has aria-selected=true when it matches selectedCode", () => {
    render(
      <CityPickerPopup dictionaries={dictionaries} selectedCode="LED" onSelect={vi.fn()} />,
    );
    // LED maps to Санкт-Петербург city — find the role=option element
    const options = screen.getAllByRole("option");
    const ledOption = options.find((o) => o.getAttribute("aria-selected") === "true");
    expect(ledOption).toBeTruthy();
  });

  it("4.1.9.2-R3: scroll panel has max-height so the list is scrollable", () => {
    const { container } = render(
      <CityPickerPopup dictionaries={dictionaries} onSelect={vi.fn()} />,
    );
    const panel = container.querySelector(".content--scroll-panel");
    expect(panel).toBeTruthy();
  });

  it("4.1.9.2-R4: items grouped by region tabs (one tab per region)", () => {
    render(<CityPickerPopup dictionaries={dictionaries} onSelect={vi.fn()} />);
    const tabs = screen.getAllByRole("tab");
    expect(tabs.length).toBe(2);
  });

  it("4.1.9.2-R5: country headers appear in the content panel (grouped by country)", () => {
    render(<CityPickerPopup dictionaries={dictionaries} onSelect={vi.fn()} />);
    // "Россия" should appear as a country-start-row header
    expect(screen.getByText("Россия")).toBeTruthy();
  });

  it("4.1.9.2-R6: ArrowDown moves keyboard highlight to the first item", () => {
    const { container } = render(
      <CityPickerPopup dictionaries={dictionaries} onSelect={vi.fn()} />,
    );
    const popup = container.querySelector("[data-testid='city-picker-popup']")!;
    fireEvent.keyDown(popup, { key: "ArrowDown" });
    // After one ArrowDown the scroll-panel's aria-activedescendant is set
    const panel = container.querySelector(".content--scroll-panel");
    expect(panel?.getAttribute("aria-activedescendant")).toBeTruthy();
  });

  it("4.1.9.2-R7: ArrowDown then ArrowDown moves highlight to the second item", () => {
    const { container } = render(
      <CityPickerPopup dictionaries={dictionaries} onSelect={vi.fn()} />,
    );
    const popup = container.querySelector("[data-testid='city-picker-popup']")!;
    fireEvent.keyDown(popup, { key: "ArrowDown" });
    const firstId = container
      .querySelector(".content--scroll-panel")
      ?.getAttribute("aria-activedescendant");

    fireEvent.keyDown(popup, { key: "ArrowDown" });
    const secondId = container
      .querySelector(".content--scroll-panel")
      ?.getAttribute("aria-activedescendant");

    expect(firstId).toBeTruthy();
    expect(secondId).toBeTruthy();
    expect(secondId).not.toBe(firstId);
  });

  it("4.1.9.2-R8: ArrowUp does not go below index 0 (clamps at first item)", () => {
    const { container } = render(
      <CityPickerPopup dictionaries={dictionaries} onSelect={vi.fn()} />,
    );
    const popup = container.querySelector("[data-testid='city-picker-popup']")!;
    // Move down first so Up has something to come back to
    fireEvent.keyDown(popup, { key: "ArrowDown" });
    const firstId = container
      .querySelector(".content--scroll-panel")
      ?.getAttribute("aria-activedescendant");

    // Press Up — should stay at first item
    fireEvent.keyDown(popup, { key: "ArrowUp" });
    const afterUpId = container
      .querySelector(".content--scroll-panel")
      ?.getAttribute("aria-activedescendant");

    expect(afterUpId).toBe(firstId);
  });

  it("4.1.9.2-R9: highlighted item gets city-highlighted CSS class", () => {
    const { container } = render(
      <CityPickerPopup dictionaries={dictionaries} onSelect={vi.fn()} />,
    );
    const popup = container.querySelector("[data-testid='city-picker-popup']")!;
    // No highlight yet
    expect(container.querySelector(".city-highlighted")).toBeNull();
    fireEvent.keyDown(popup, { key: "ArrowDown" });
    expect(container.querySelector(".city-highlighted")).toBeTruthy();
  });

  it("4.1.9.2-R10: Enter commits the highlighted item", () => {
    const onSelect = vi.fn();
    const { container } = render(
      <CityPickerPopup dictionaries={dictionaries} onSelect={onSelect} />,
    );
    const popup = container.querySelector("[data-testid='city-picker-popup']")!;
    fireEvent.keyDown(popup, { key: "ArrowDown" });
    fireEvent.keyDown(popup, { key: "Enter" });
    expect(onSelect).toHaveBeenCalledTimes(1);
  });

  it("4.1.9.2-R11: Enter without highlight does not call onSelect", () => {
    const onSelect = vi.fn();
    const { container } = render(
      <CityPickerPopup dictionaries={dictionaries} onSelect={onSelect} />,
    );
    const popup = container.querySelector("[data-testid='city-picker-popup']")!;
    fireEvent.keyDown(popup, { key: "Enter" });
    expect(onSelect).not.toHaveBeenCalled();
  });

  it("4.1.9.2-R12: Escape calls onClose without calling onSelect", () => {
    const onSelect = vi.fn();
    const onClose = vi.fn();
    const { container } = render(
      <CityPickerPopup
        dictionaries={dictionaries}
        onSelect={onSelect}
        onClose={onClose}
      />,
    );
    const popup = container.querySelector("[data-testid='city-picker-popup']")!;
    fireEvent.keyDown(popup, { key: "Escape" });
    expect(onClose).toHaveBeenCalledTimes(1);
    expect(onSelect).not.toHaveBeenCalled();
  });

  it("4.1.9.2-R13: switching tabs resets the keyboard highlight", () => {
    const { container } = render(
      <CityPickerPopup dictionaries={dictionaries} onSelect={vi.fn()} />,
    );
    const popup = container.querySelector("[data-testid='city-picker-popup']")!;
    fireEvent.keyDown(popup, { key: "ArrowDown" });
    expect(container.querySelector(".city-highlighted")).toBeTruthy();
    // Switch to the second region tab
    fireEvent.click(screen.getByText("Азия"));
    expect(container.querySelector(".city-highlighted")).toBeNull();
  });

  it("4.1.9.2-R14: popup root has role=dialog for a11y", () => {
    render(<CityPickerPopup dictionaries={dictionaries} onSelect={vi.fn()} />);
    expect(screen.getByRole("dialog")).toBeTruthy();
  });
});
