// @vitest-environment jsdom
import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
import { renderHook } from "@testing-library/react";
import { useGeoCityDefault } from "./useGeoCityDefault.js";
import type { IDictionaries } from "@/shared/dictionaries/index.js";

const mockDictionaries: IDictionaries = {
  cities: [
    {
      code: "MOW",
      name: "Москва",
      location: { lat: 55.7558, lon: 37.6173 },
      country_code: "RU",
      countryName: "Russia",
      has_afl_flights: true,
      airports: [],
    },
    {
      code: "LED",
      name: "Санкт-Петербург",
      location: { lat: 59.9311, lon: 30.3609 },
      country_code: "RU",
      countryName: "Russia",
      has_afl_flights: true,
      airports: [],
    },
  ],
  airports: [],
  regions: [],
  countries: [],
  cityByCode: new Map([
    [
      "MOW",
      {
        code: "MOW",
        name: "Москва",
        location: { lat: 55.7558, lon: 37.6173 },
        country_code: "RU",
        countryName: "Russia",
        has_afl_flights: true,
        airports: [],
      },
    ],
    [
      "LED",
      {
        code: "LED",
        name: "Санкт-Петербург",
        location: { lat: 59.9311, lon: 30.3609 },
        country_code: "RU",
        countryName: "Russia",
        has_afl_flights: true,
        airports: [],
      },
    ],
  ]),
  airportByCode: new Map(),
  ruCityCodes: new Set(["MOW", "LED"]),
  otherCityCodes: new Set(),
} as unknown as IDictionaries;

describe("useGeoCityDefault", () => {
  let getCurrentPositionMock: ReturnType<typeof vi.fn>;

  beforeEach(() => {
    getCurrentPositionMock = vi.fn();
    Object.defineProperty(navigator, "geolocation", {
      value: { getCurrentPosition: getCurrentPositionMock },
      configurable: true,
    });
  });

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

  it("calls onCity with nearest city code when geolocation succeeds and shouldApply returns true", () => {
    getCurrentPositionMock.mockImplementation((onSuccess: (pos: GeolocationPosition) => void) => {
      onSuccess({ coords: { latitude: 55.75, longitude: 37.61 } } as GeolocationPosition);
    });
    const onCity = vi.fn();
    renderHook(() =>
      useGeoCityDefault({ dictionaries: mockDictionaries, shouldApply: () => true, onCity }),
    );
    expect(onCity).toHaveBeenCalledWith("MOW");
  });

  it("does not call onCity when shouldApply returns false", () => {
    getCurrentPositionMock.mockImplementation((onSuccess: (pos: GeolocationPosition) => void) => {
      onSuccess({ coords: { latitude: 55.75, longitude: 37.61 } } as GeolocationPosition);
    });
    const onCity = vi.fn();
    renderHook(() =>
      useGeoCityDefault({ dictionaries: mockDictionaries, shouldApply: () => false, onCity }),
    );
    expect(onCity).not.toHaveBeenCalled();
  });

  it("is silent when geolocation permission is denied", () => {
    getCurrentPositionMock.mockImplementation((_s: unknown, onErr: (err: GeolocationPositionError) => void) => {
      onErr({ code: 1, message: "denied" } as GeolocationPositionError);
    });
    const onCity = vi.fn();
    renderHook(() =>
      useGeoCityDefault({ dictionaries: mockDictionaries, shouldApply: () => true, onCity }),
    );
    expect(onCity).not.toHaveBeenCalled();
  });

  it("is silent when navigator.geolocation is undefined", () => {
    Object.defineProperty(navigator, "geolocation", { value: undefined, configurable: true });
    const onCity = vi.fn();
    renderHook(() =>
      useGeoCityDefault({ dictionaries: mockDictionaries, shouldApply: () => true, onCity }),
    );
    expect(onCity).not.toHaveBeenCalled();
  });

  it("fires only once per mount even if dictionaries change", () => {
    getCurrentPositionMock.mockImplementation((onSuccess: (pos: GeolocationPosition) => void) => {
      onSuccess({ coords: { latitude: 55.75, longitude: 37.61 } } as GeolocationPosition);
    });
    const onCity = vi.fn();
    const { rerender } = renderHook(
      ({ dict }: { dict: IDictionaries }) =>
        useGeoCityDefault({ dictionaries: dict, shouldApply: () => true, onCity }),
      { initialProps: { dict: mockDictionaries } },
    );
    rerender({ dict: { ...mockDictionaries } as IDictionaries });
    expect(onCity).toHaveBeenCalledTimes(1);
  });

  it("waits for dictionaries to be non-null before computing city", () => {
    getCurrentPositionMock.mockImplementation((onSuccess: (pos: GeolocationPosition) => void) => {
      onSuccess({ coords: { latitude: 55.75, longitude: 37.61 } } as GeolocationPosition);
    });
    const onCity = vi.fn();
    renderHook(() =>
      useGeoCityDefault({ dictionaries: null, shouldApply: () => true, onCity }),
    );
    expect(onCity).not.toHaveBeenCalled();
  });
});
