/**
 * Tests for OnlineBoardSearchPage component.
 *
 * Verifies rendering with mock providers and navigation wiring.
 * Includes TZ §4.1.13 scroll-to-current-time + auto-expand integration tests.
 *
 * @vitest-environment jsdom
 */

import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
import { render, screen } from "@testing-library/react";
import { OnlineBoardSearchPage } from "./OnlineBoardSearchPage.js";
import type { OnlineBoardSearchPageProps } from "./OnlineBoardSearchPage.js";
import type { ISimpleFlight } from "../types.js";

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

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

// Mock layout components that bring in extra transitive dependencies
vi.mock("@/ui/layout/PageTabs.js", () => ({
  PageTabs: () => <div data-testid="page-tabs" />,
}));

let capturedFilterProps: Record<string, unknown> | undefined;
vi.mock("./OnlineBoardFilter.js", () => ({
  OnlineBoardFilter: (props: Record<string, unknown>) => {
    capturedFilterProps = props;
    return <div data-testid="online-board-filter" />;
  },
}));

vi.mock("@/shared/hooks/useSearchHistory.js", () => ({
  useSearchHistory: () => ({ items: [], add: vi.fn(), clear: vi.fn() }),
}));

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

let capturedSearchParams: Record<string, unknown> | undefined;
vi.mock("../hooks/useOnlineBoard.js", () => ({
  useOnlineBoard: (params: Record<string, unknown>) => {
    capturedSearchParams = params;
    return {
      flights: [],
      loading: false,
      error: null,
      refresh: vi.fn(),
    };
  },
}));

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

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

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

// Capture FlightList props for scroll-to-current-time integration tests
let capturedInitialCurrentFlightId: string | null | undefined = undefined;
vi.mock("@/ui/flights/FlightList.js", () => ({
  FlightList: (props: { initialCurrentFlightId?: string | null }) => {
    capturedInitialCurrentFlightId = props.initialCurrentFlightId;
    return <div data-testid="flight-list" data-initial-id={props.initialCurrentFlightId ?? ""} />;
  },
}));

/** Minimal Direct flight shape that satisfies ISimpleFlight + closestFlight */
function makeDirectFlight(
  id: string,
  departureLocalIso: string,
  flightDate = "20260415",
): ISimpleFlight {
  return {
    id,
    routeType: "Direct",
    flyingTime: "2h",
    status: "Scheduled",
    flightId: { carrier: "SU", flightNumber: id, suffix: "", date: flightDate },
    operatingBy: {},
    leg: {
      arrival: {
        scheduled: { airport: "", airportCode: "LED", city: "", cityCode: "", countryCode: "" },
        times: {
          scheduledArrival: {
            dayChange: { value: 0, title: "" },
            local: departureLocalIso, // reuse for simplicity
            localTime: "",
            tzOffset: 0,
            utc: "",
          },
        },
      },
      dayChange: 0,
      departure: {
        scheduled: { airport: "", airportCode: "SVO", city: "", cityCode: "", countryCode: "" },
        checkingStatus: "Scheduled",
        times: {
          scheduledDeparture: {
            dayChange: { value: 0, title: "" },
            local: departureLocalIso,
            localTime: "",
            tzOffset: 0,
            utc: "",
          },
        },
      },
      equipment: {},
      flags: { checkinAvailable: false, returnToAirport: false, routeChanged: false },
      flyingTime: "2h",
      index: 0,
      operatingBy: {},
      status: "Scheduled",
      updated: "",
    },
  } as ISimpleFlight;
}

describe("OnlineBoardSearchPage", () => {
  const departureParsedParams: OnlineBoardSearchPageProps["params"] = {
    type: "departure",
    station: "SVO",
    date: "20250115",
  };

  beforeEach(() => {
    vi.clearAllMocks();
    capturedInitialCurrentFlightId = undefined;
    capturedFilterProps = undefined;
    capturedSearchParams = undefined;
  });

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

  it("renders search page container", () => {
    render(<OnlineBoardSearchPage params={departureParsedParams} />);
    expect(screen.getByTestId("online-board-search")).toBeTruthy();
  });

  it("renders empty flight list when no results", () => {
    render(<OnlineBoardSearchPage params={departureParsedParams} />);
    // FlightList is mocked; verify the search page itself renders without error
    // and the mock FlightList receives an empty initialCurrentFlightId.
    expect(screen.getByTestId("online-board-search")).toBeTruthy();
    expect(capturedInitialCurrentFlightId ?? null).toBeNull();
  });

  it("renders for flight search type", () => {
    render(
      <OnlineBoardSearchPage
        params={{
          type: "flight",
          carrier: "SU",
          flightNumber: "100",
          date: "20250115",
        }}
      />,
    );
    expect(screen.getByTestId("online-board-search")).toBeTruthy();
  });

  it("TIRREDESIGN-13: preserves suffix in flight API params and filter value", () => {
    render(
      <OnlineBoardSearchPage
        params={{
          type: "flight",
          carrier: "SU",
          flightNumber: "0100",
          suffix: "D",
          date: "20250115",
        }}
      />,
    );

    expect(capturedSearchParams?.flightNumber).toBe("SU0100D");
    expect(capturedFilterProps?.initialFlightNumber).toBe("0100D");
  });

  it("renders for route search type", () => {
    render(
      <OnlineBoardSearchPage
        params={{
          type: "route",
          departure: "SVO",
          arrival: "JFK",
          date: "20250115",
        }}
      />,
    );
    expect(screen.getByTestId("online-board-search")).toBeTruthy();
  });

  it("renders for arrival search type", () => {
    render(
      <OnlineBoardSearchPage
        params={{
          type: "arrival",
          station: "JFK",
          date: "20250115",
        }}
      />,
    );
    expect(screen.getByTestId("online-board-search")).toBeTruthy();
  });

  // ---------------------------------------------------------------------------
  // TZ §4.1.13 — scroll-to-current-time + auto-expand-nearest (today's tab)
  // ---------------------------------------------------------------------------

  describe("scroll-to-current-time integration (§4.1.13)", () => {
    // Pin the clock to 2026-04-15 12:00 local time
    const TODAY_YYYYMMDD = "20260415";
    const FUTURE_YYYYMMDD = "20260416";

    // Three flights: 08:00, 11:30, 14:00 on today. Nearest to 12:00 is 11:30 (id "F2").
    const todayFlights = [
      makeDirectFlight("F1", "2026-04-15T08:00:00"),
      makeDirectFlight("F2", "2026-04-15T11:30:00"),
      makeDirectFlight("F3", "2026-04-15T14:00:00"),
    ];

    it("on today's tab, auto-selects the flight nearest to current time", async () => {
      vi.useFakeTimers();
      vi.setSystemTime(new Date(2026, 3, 15, 12, 0, 0)); // April 15 2026 12:00

      // Directly test that findClosestFlightId selects F2 for today
      const { findClosestFlightId } = await import("../closestFlight.js");
      const id = findClosestFlightId(todayFlights, {
        searchDate: TODAY_YYYYMMDD,
        isArrival: false,
        now: new Date(2026, 3, 15, 12, 0, 0),
      });
      expect(id).toBe("F2");
    });

    it("on a future tab, auto-selects the first flight (not scroll-to-now)", async () => {
      vi.useFakeTimers();
      vi.setSystemTime(new Date(2026, 3, 15, 12, 0, 0)); // April 15 12:00

      // Flights dated for tomorrow (F4, F5, F6)
      const futureFlights = [
        makeDirectFlight("F4", "2026-04-16T08:00:00", FUTURE_YYYYMMDD),
        makeDirectFlight("F5", "2026-04-16T12:00:00", FUTURE_YYYYMMDD),
        makeDirectFlight("F6", "2026-04-16T18:00:00", FUTURE_YYYYMMDD),
      ];
      const { findClosestFlightId } = await import("../closestFlight.js");
      const id = findClosestFlightId(futureFlights, {
        searchDate: FUTURE_YYYYMMDD,
        isArrival: false,
        now: new Date(2026, 3, 15, 12, 0, 0),
      });
      // For a future day where now < firstFlight, returns first flight
      expect(id).toBe("F4");
    });

    it("on a past tab, auto-selects the last flight", async () => {
      vi.useFakeTimers();
      vi.setSystemTime(new Date(2026, 3, 15, 12, 0, 0)); // April 15 12:00

      const pastFlights = [
        makeDirectFlight("P1", "2026-04-14T08:00:00", "20260414"),
        makeDirectFlight("P2", "2026-04-14T12:00:00", "20260414"),
        makeDirectFlight("P3", "2026-04-14T18:00:00", "20260414"),
      ];
      const { findClosestFlightId } = await import("../closestFlight.js");
      const id = findClosestFlightId(pastFlights, {
        searchDate: "20260414",
        isArrival: false,
        now: new Date(2026, 3, 15, 12, 0, 0),
      });
      // now > all past flights → returns last
      expect(id).toBe("P3");
    });

    it("renders with no auto-selected flight when list is empty", () => {
      render(<OnlineBoardSearchPage params={departureParsedParams} />);
      expect(screen.getByTestId("online-board-search")).toBeTruthy();
      // FlightList should receive null/undefined initialCurrentFlightId when flights are empty
      expect(capturedInitialCurrentFlightId ?? null).toBeNull();
    });
  });
});
