/**
 * Tests for ScheduleDetailsPage breadcrumb wiring.
 *
 * Verifies TZ §4.1.4 Table 7 rows 11-13: the leaf breadcrumb is built
 * from the `?request=` query param when area === "schedule", and falls
 * back to a single-item trail when no request param is present (share-link
 * scenario).
 *
 * @vitest-environment jsdom
 */

import { forwardRef } from "react";
import { describe, it, expect, vi, beforeEach } from "vitest";
import { render, screen } from "@testing-library/react";
import { ScheduleDetailsPage } from "./ScheduleDetailsPage.js";
import type { IScheduleFlightId } from "../types.js";

// ---------------------------------------------------------------------------
// Controlled useSearchParams — overridden per test
// ---------------------------------------------------------------------------
let mockSearchParamsGet: (key: string) => string | null = () => null;

vi.mock("@modern-js/runtime/router", () => ({
  useSearchParams: () => [{ get: (k: string) => mockSearchParamsGet(k) }],
  useNavigate: () => vi.fn(),
  useParams: () => ({ lang: "ru-ru" }),
  Link: forwardRef<
    HTMLAnchorElement,
    React.AnchorHTMLAttributes<HTMLAnchorElement> & { to: string }
  >(({ children, to, ...props }, ref) => (
    <a ref={ref} href={to} {...props}>
      {children}
    </a>
  )),
}));

vi.mock("@/shared/hooks/useDictionaries.js", () => ({
  useCityName: (code: string) => code,
  useStationDisplayName: (code: string) => code,
}));

vi.mock("@/i18n/provider.js", () => ({
  useTranslation: () => ({
    t: (key: string, vars?: Record<string, unknown>) => {
      if (vars) {
        return Object.entries(vars).reduce(
          (s: string, [k, v]) => s.replace(`{${k}}`, String(v)),
          key,
        );
      }
      return key;
    },
  }),
}));

vi.mock("@/i18n/resolver.js", () => ({
  localeToLanguage: () => "ru",
  normalizeLocaleParam: (l: string) => l,
  DEFAULT_LANGUAGE: "ru",
}));

// Mutable for individual test overrides
let mockScheduleDetailsResult: { flights: unknown[]; loading: boolean; error: unknown } = {
  flights: [],
  loading: true,
  error: null,
};

vi.mock("../hooks/useScheduleDetails.js", () => ({
  useScheduleDetails: () => mockScheduleDetailsResult,
}));

// `useScheduleSearch` is invoked from ScheduleDetailsPage to fetch the
// sibling-flights list for the left mini-panel; mock it so the test
// doesn't need ApiClientProvider plumbing.
vi.mock("../hooks/useScheduleSearch.js", () => ({
  useScheduleSearch: () => ({
    flights: [],
    loading: false,
    error: null,
    refresh: vi.fn(),
    cancel: vi.fn(),
  }),
}));

vi.mock("../seo.js", () => ({
  buildScheduleDetailsSeo: () => ({}),
}));

vi.mock("@/ui/layout/PageLayout.js", () => ({
  PageLayout: ({
    children,
    breadcrumbs,
    headerLeft,
    contentLeft,
    stickyContent,
  }: {
    children?: React.ReactNode;
    breadcrumbs?: { label: string; url?: string }[];
    headerLeft?: React.ReactNode;
    contentLeft?: React.ReactNode;
    stickyContent?: React.ReactNode;
  }) => (
    <div>
      <div data-testid="page-header-left">{headerLeft}</div>
      <nav data-testid="breadcrumbs">
        {breadcrumbs?.map((b, i) => (
          <span key={i} data-testid={`crumb-${i}`} data-url={b.url}>
            {b.label}
          </span>
        ))}
      </nav>
      <div data-testid="page-content-left">{contentLeft}</div>
      <div data-testid="page-sticky">{stickyContent}</div>
      {children}
    </div>
  ),
}));

vi.mock("@/ui/layout/PageTabs.js", () => ({
  PageTabs: () => <div />,
}));

vi.mock("@/ui/flights/FlightListSkeleton.js", () => ({
  FlightListSkeleton: () => <div data-testid="skeleton" />,
}));

vi.mock("@/ui/seo/SeoHead.js", () => ({
  SeoHead: () => null,
}));

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

vi.mock("@/features/online-board/components/FlightsMiniList/index.js", () => ({
  FlightsMiniList: () => <div data-testid="flights-mini-list" />,
}));

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

vi.mock("@/ui/flights/FlightCard.js", () => ({
  FlightCard: () => <div data-testid="flight-card" />,
}));

vi.mock("./ScheduleFlightBody.js", () => ({
  ScheduleFlightBody: () => <div data-testid="schedule-flight-body" />,
}));

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

vi.mock("@/features/online-board/components/FullRouteTimeline/index.js", () => ({
  FullRouteTimeline: () => <div data-testid="full-route-timeline" />,
}));

vi.mock("@/ui/flights/IFlyWarning.js", () => ({
  IFlyWarning: () => null,
}));

vi.mock("@/shared/seo/json-ld.js", () => ({
  JsonLdRenderer: () => null,
}));

vi.mock("../json-ld.js", () => ({
  buildScheduleFlightJsonLd: () => ({}),
}));

// ---------------------------------------------------------------------------

const flightId: IScheduleFlightId = {
  carrier: "SU",
  flightNumber: "1234",
  date: "20260515",
};

describe("ScheduleDetailsPage breadcrumbs", () => {
  beforeEach(() => {
    mockSearchParamsGet = () => null;
    mockScheduleDetailsResult = { flights: [], loading: true, error: null };
  });

  it("shows 1-item trail when no ?request= param (share-link)", () => {
    render(
      <ScheduleDetailsPage
        flights={[flightId]}
        locale="ru-ru"
        canonicalOrigin="https://example.com"
      />,
    );
    expect(screen.queryByTestId("crumb-0")).toBeTruthy();
    expect(screen.queryByTestId("crumb-1")).toBeNull();
  });

  it("appends a route leaf crumb when ?request= carries schedule area (Angular parity)", () => {
    mockSearchParamsGet = (k) =>
      k === "request"
        ? "schedule-route-NBC-KHV-20220307-20220313"
        : null;

    render(
      <ScheduleDetailsPage
        flights={[flightId]}
        locale="ru-ru"
        canonicalOrigin="https://example.com"
      />,
    );

    const crumb0 = screen.getByTestId("crumb-0");
    const crumb1 = screen.getByTestId("crumb-1");
    expect(crumb0.textContent).toContain("SCHEDULE.TITLE");
    // Leaf label key is BREADCRUMBS.SCHEDULE-ROUTE — Angular shows
    // "{depCity} - {arrCity}" (no "Маршрут:" prefix on schedule details).
    expect(crumb1.textContent).toContain("BREADCRUMBS.SCHEDULE-ROUTE");
    const backUrl = crumb1.getAttribute("data-url") ?? "";
    expect(backUrl).toContain("/schedule/");
    expect(backUrl).toContain("NBC");
    expect(backUrl).toContain("KHV");
  });

  it("ignores ?request= that carries onlineboard area", () => {
    mockSearchParamsGet = (k) =>
      k === "request"
        ? "onlineboard-route-MOW-LED-20260515"
        : null;

    render(
      <ScheduleDetailsPage
        flights={[flightId]}
        locale="ru-ru"
        canonicalOrigin="https://example.com"
      />,
    );

    // Should stay at 1-item trail — no leaf
    expect(screen.getByTestId("crumb-0")).toBeTruthy();
    expect(screen.queryByTestId("crumb-1")).toBeNull();
  });
});

// ── TZ §4.1.16.1 structure tests ──────────────────────────────────────────────
describe("ScheduleDetailsPage structure (§4.1.16.1 + §4.1.16.2 + §4.1.16.3)", () => {
  beforeEach(() => {
    mockSearchParamsGet = () => null;
    // Reset to loading state (breadcrumb tests rely on this)
    mockScheduleDetailsResult = { flights: [], loading: true, error: null };
    // The new ScheduleFlightsMiniList scrolls the highlighted row into
    // view on mount; JSDOM doesn't ship `scrollIntoView`, so stub it.
    Element.prototype.scrollIntoView = vi.fn();
  });

  it("4.1.16.1-R2: renders FlightsMiniList in the left column when flights data available", () => {
    // Set non-loading state so the full page renders with contentLeft
    mockScheduleDetailsResult = {
      flights: [
        {
          id: "SU1234-20260515",
          routeType: "Direct",
          status: "Scheduled",
          flyingTime: "2:00",
          flightId: { carrier: "SU", flightNumber: "1234", suffix: "", date: "20260515" },
          operatingBy: {},
          leg: {
            index: 0, status: "Scheduled", flyingTime: "2:00", dayChange: 0, updated: "",
            operatingBy: {}, equipment: {}, flags: { checkinAvailable: false, returnToAirport: false, routeChanged: false },
            departure: {
              scheduled: { airport: "SVO", airportCode: "SVO", city: "Moscow", cityCode: "MOW", countryCode: "RU" },
              latest: { airport: "SVO", airportCode: "SVO", city: "Moscow", cityCode: "MOW", countryCode: "RU" },
              dispatch: "", gate: "", terminal: "", checkingStatus: "", parkingStand: "",
              times: { scheduledDeparture: { dayChange: { value: 0, title: "" }, local: "10:00", localTime: "10:00", tzOffset: 3, utc: "07:00" } },
            },
            arrival: {
              scheduled: { airport: "LED", airportCode: "LED", city: "St Petersburg", cityCode: "LED", countryCode: "RU" },
              latest: { airport: "LED", airportCode: "LED", city: "St Petersburg", cityCode: "LED", countryCode: "RU" },
              dispatch: "", gate: "", terminal: "",
              times: { scheduledArrival: { dayChange: { value: 0, title: "" }, local: "12:00", localTime: "12:00", tzOffset: 3, utc: "09:00" } },
            },
          },
        },
      ],
      loading: false,
      error: null,
    };
    render(
      <ScheduleDetailsPage
        flights={[flightId]}
        locale="ru-ru"
        canonicalOrigin="https://example.com"
      />,
    );
    // FlightsMiniList is rendered in contentLeft — mock PageLayout renders it
    expect(screen.getByTestId("flights-mini-list")).toBeTruthy();
    // DayTabs is rendered in stickyContent
    expect(screen.getByTestId("day-tabs")).toBeTruthy();
  });

  it("4.1.16.3-R22: DayTabs rendered in stickyContent on flight-found state", () => {
    // Reuse the flights fixture from R2 test above
    mockScheduleDetailsResult = {
      flights: [
        {
          id: "SU1234-20260515",
          routeType: "Direct",
          status: "Scheduled",
          flyingTime: "2:00",
          flightId: { carrier: "SU", flightNumber: "1234", suffix: "", date: "20260515" },
          operatingBy: {},
          leg: {
            index: 0, status: "Scheduled", flyingTime: "2:00", dayChange: 0, updated: "",
            operatingBy: {}, equipment: {}, flags: { checkinAvailable: false, returnToAirport: false, routeChanged: false },
            departure: {
              scheduled: { airport: "SVO", airportCode: "SVO", city: "Moscow", cityCode: "MOW", countryCode: "RU" },
              latest: { airport: "SVO", airportCode: "SVO", city: "Moscow", cityCode: "MOW", countryCode: "RU" },
              dispatch: "", gate: "", terminal: "", checkingStatus: "", parkingStand: "",
              times: { scheduledDeparture: { dayChange: { value: 0, title: "" }, local: "10:00", localTime: "10:00", tzOffset: 3, utc: "07:00" } },
            },
            arrival: {
              scheduled: { airport: "LED", airportCode: "LED", city: "St Petersburg", cityCode: "LED", countryCode: "RU" },
              latest: { airport: "LED", airportCode: "LED", city: "St Petersburg", cityCode: "LED", countryCode: "RU" },
              dispatch: "", gate: "", terminal: "",
              times: { scheduledArrival: { dayChange: { value: 0, title: "" }, local: "12:00", localTime: "12:00", tzOffset: 3, utc: "09:00" } },
            },
          },
        },
      ],
      loading: false,
      error: null,
    };
    render(
      <ScheduleDetailsPage
        flights={[flightId]}
        locale="ru-ru"
        canonicalOrigin="https://example.com"
      />,
    );
    // Success state renders DayTabs in stickyContent (Schedule window = +330 days)
    expect(screen.getByTestId("day-tabs")).toBeTruthy();
  });

  it("4.1.16.1-R4: back link navigates to scheduleHref (success state)", () => {
    // Back link is only in the success state headerLeft (not loading/error/not-found).
    // Reuse the same flight fixture.
    mockScheduleDetailsResult = {
      flights: [
        {
          id: "SU1234-20260515",
          routeType: "Direct",
          status: "Scheduled",
          flyingTime: "2:00",
          flightId: { carrier: "SU", flightNumber: "1234", suffix: "", date: "20260515" },
          operatingBy: {},
          leg: {
            index: 0, status: "Scheduled", flyingTime: "2:00", dayChange: 0, updated: "",
            operatingBy: {}, equipment: {}, flags: { checkinAvailable: false, returnToAirport: false, routeChanged: false },
            departure: {
              scheduled: { airport: "SVO", airportCode: "SVO", city: "Moscow", cityCode: "MOW", countryCode: "RU" },
              latest: { airport: "SVO", airportCode: "SVO", city: "Moscow", cityCode: "MOW", countryCode: "RU" },
              dispatch: "", gate: "", terminal: "", checkingStatus: "", parkingStand: "",
              times: { scheduledDeparture: { dayChange: { value: 0, title: "" }, local: "10:00", localTime: "10:00", tzOffset: 3, utc: "07:00" } },
            },
            arrival: {
              scheduled: { airport: "LED", airportCode: "LED", city: "St Petersburg", cityCode: "LED", countryCode: "RU" },
              latest: { airport: "LED", airportCode: "LED", city: "St Petersburg", cityCode: "LED", countryCode: "RU" },
              dispatch: "", gate: "", terminal: "",
              times: { scheduledArrival: { dayChange: { value: 0, title: "" }, local: "12:00", localTime: "12:00", tzOffset: 3, utc: "09:00" } },
            },
          },
        },
      ],
      loading: false,
      error: null,
    };
    render(
      <ScheduleDetailsPage
        flights={[flightId]}
        locale="ru-ru"
        canonicalOrigin="https://example.com"
      />,
    );
    const back = screen.getByTestId("schedule-details-back") as HTMLAnchorElement;
    expect(back.getAttribute("href")).toBe("/ru-ru/schedule");
  });
});
