/**
 * Integration tests for error handling in the Online Board.
 *
 * Verifies error UI renders correctly for API errors (404, 500, timeout)
 * and that retry triggers re-fetch.
 *
 * @vitest-environment jsdom
 */

import { describe, it, expect, vi, beforeEach } from "vitest";
import { render, screen, fireEvent } from "@testing-library/react";
import { OnlineBoardSearchPage } from "@/features/online-board/components/OnlineBoardSearchPage.js";
import { OnlineBoardDetailsPage } from "@/features/online-board/components/OnlineBoardDetailsPage.js";
import { ApiHttpError, ApiTimeoutError } from "@/shared/api/errors.js";
import type { IParsedFlightId } from "@/features/online-board/types.js";

// ---------------------------------------------------------------------------
// Mocks
// ---------------------------------------------------------------------------

vi.mock("@modern-js/runtime/router", () => ({
  useNavigate: () => vi.fn(),
  useParams: () => ({ lang: "ru-ru" }),
  useSearchParams: () => [new URLSearchParams()],
  Link: ({ children, ...props }: Record<string, unknown>) =>
    <a {...props}>{children as React.ReactNode}</a>,
}));

// Mock layout components to avoid transitive dependency issues
vi.mock("@/ui/layout/PageTabs.js", () => ({
  PageTabs: () => <div data-testid="page-tabs" />,
}));

vi.mock("@/features/online-board/components/OnlineBoardFilter.js", () => ({
  OnlineBoardFilter: () => <div data-testid="online-board-filter" />,
}));

vi.mock("@/features/flights-map/hooks/useFeatureFlag.js", () => ({
  useFeatureFlag: () => false,
}));

vi.mock("@/i18n/provider.js", () => ({
  useTranslation: () => ({
    t: (key: string) => key,
    i18n: { language: "ru" },
  }),
}));

const mockUseOnlineBoard = vi.fn();
vi.mock("@/features/online-board/hooks/useOnlineBoard.js", () => ({
  useOnlineBoard: (...args: unknown[]) => mockUseOnlineBoard(...args),
}));

vi.mock("@/features/online-board/hooks/useLiveBoardSearch.js", () => ({
  useLiveBoardSearch: (_params: unknown, initialFlights: unknown) => ({
    flights: initialFlights,
    connectionStatus: "idle" as const,
  }),
}));

vi.mock("@/features/online-board/hooks/useCalendarDays.js", () => ({
  useCalendarDays: () => ({
    days: [],
    loading: false,
  }),
}));

vi.mock("@/shared/dictionaries/index.js", () => ({
  useDictionaries: () => ({ dictionaries: null, loading: false, error: null }),
}));

const mockUseFlightDetails = vi.fn();
vi.mock("@/features/online-board/hooks/useFlightDetails.js", () => ({
  useFlightDetails: (...args: unknown[]) => mockUseFlightDetails(...args),
}));

vi.mock("@/features/online-board/hooks/useLiveFlightDetails.js", () => ({
  useLiveFlightDetails: () => ({
    flight: null,
    connectionStatus: "idle" as const,
  }),
}));

vi.mock("@/shared/hooks/useAppSettings.js", () => ({
  useAppSettings: () => ({
    onlineboardSearchFrom: 2,
    onlineboardSearchTo: 14,
    scheduleSearchFrom: 30,
    scheduleSearchTo: 30,
    loading: false,
    error: null,
  }),
}));

// OnlineBoardDetailsPage resolves station codes via useStationDisplayName,
// which internally hits useApiClient — no ApiClientProvider in this tree.
vi.mock("@/shared/hooks/useDictionaries.js", () => ({
  useCityName: (code: string) => code,
  useStationDisplayName: (code: string) => code,
}));

// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------

const DEPARTURE_PARAMS = {
  type: "departure" as const,
  station: "SVO",
  date: "20250115",
};

const FLIGHT_ID: IParsedFlightId = {
  carrier: "SU",
  flightNumber: "0100",
  date: "20250115",
};

// ---------------------------------------------------------------------------
// Tests — Search page errors
// ---------------------------------------------------------------------------

describe("Search page error handling", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it("renders error UI for HTTP 500", () => {
    const refreshSpy = vi.fn();
    mockUseOnlineBoard.mockReturnValue({
      flights: [],
      loading: false,
      error: new ApiHttpError("HTTP 500 from /board", 500),
      refresh: refreshSpy,
    });

    render(<OnlineBoardSearchPage params={DEPARTURE_PARAMS} />);
    expect(screen.getByTestId("search-error")).toBeTruthy();
    expect(screen.getByText("BOARD.LOAD-FAILED-TITLE")).toBeTruthy();
  });

  it("renders error UI for HTTP 404", () => {
    mockUseOnlineBoard.mockReturnValue({
      flights: [],
      loading: false,
      error: new ApiHttpError("HTTP 404", 404),
      refresh: vi.fn(),
    });

    render(<OnlineBoardSearchPage params={DEPARTURE_PARAMS} />);
    expect(screen.getByTestId("search-error")).toBeTruthy();
  });

  it("renders error UI for timeout", () => {
    mockUseOnlineBoard.mockReturnValue({
      flights: [],
      loading: false,
      error: new ApiTimeoutError(5000),
      refresh: vi.fn(),
    });

    render(<OnlineBoardSearchPage params={DEPARTURE_PARAMS} />);
    expect(screen.getByTestId("search-error")).toBeTruthy();
  });

  it("renders retry button in error state", () => {
    mockUseOnlineBoard.mockReturnValue({
      flights: [],
      loading: false,
      error: new ApiHttpError("HTTP 500", 500),
      refresh: vi.fn(),
    });

    render(<OnlineBoardSearchPage params={DEPARTURE_PARAMS} />);
    expect(screen.getByText("SHARED.RETRY")).toBeTruthy();
  });

  it("calls refresh when retry button is clicked", () => {
    const refreshSpy = vi.fn();
    mockUseOnlineBoard.mockReturnValue({
      flights: [],
      loading: false,
      error: new ApiHttpError("HTTP 500", 500),
      refresh: refreshSpy,
    });

    render(<OnlineBoardSearchPage params={DEPARTURE_PARAMS} />);
    fireEvent.click(screen.getByText("SHARED.RETRY"));
    expect(refreshSpy).toHaveBeenCalledTimes(1);
  });
});

// ---------------------------------------------------------------------------
// Tests — Details page errors
// ---------------------------------------------------------------------------

describe("Details page error handling", () => {
  beforeEach(() => {
    vi.clearAllMocks();
    // OnlineBoardDetailsPage also calls useOnlineBoard for the sibling mini-
    // list sidebar — return an empty list so the sidebar just renders nothing.
    mockUseOnlineBoard.mockReturnValue({
      flights: [],
      loading: false,
      error: null,
      refresh: vi.fn(),
    });
  });

  it("renders error state for details API failure", () => {
    mockUseFlightDetails.mockReturnValue({
      flight: null,
      allFlights: [],
      daysOfFlight: [],
      loading: false,
      error: new ApiHttpError("HTTP 500", 500),
    });

    render(
      <OnlineBoardDetailsPage
        flightId={FLIGHT_ID}
        locale="ru"
        canonicalOrigin="https://www.aeroflot.ru"
      />,
    );
    expect(screen.getByTestId("flight-details-error")).toBeTruthy();
  });

  it("renders not-found when details returns null", () => {
    mockUseFlightDetails.mockReturnValue({
      flight: null,
      allFlights: [],
      daysOfFlight: [],
      loading: false,
      error: null,
    });

    render(
      <OnlineBoardDetailsPage
        flightId={FLIGHT_ID}
        locale="ru"
        canonicalOrigin="https://www.aeroflot.ru"
      />,
    );
    expect(screen.getByTestId("flight-details-not-found")).toBeTruthy();
  });
});
