/**
 * Validates that the API layer can parse real responses captured from the
 * test environment on 2026-04-17. Each test wires a real fixture into a
 * mock fetch, then runs the production API function and asserts the
 * parsed value has the structure the UI relies on.
 *
 * These tests catch drift between the declared TS types and the actual
 * API contract. To refresh fixtures: `AEROFLOT_API_AUTH=user:pass
 * ./scripts/fetch-api-fixtures.sh`.
 */

import { describe, it, expect, vi } from "vitest";
import { ApiClient } from "@/shared/api/client";
import { searchFlights, getFlightDetails, getCalendarDays } from "@/features/online-board/api";
import { searchSchedule, getScheduleDetails, getScheduleCalendarDays } from "@/features/schedule/api";
import { searchDestinations, getFlightsMapCalendar } from "@/features/flights-map/api";
import { getPopularRequests } from "@/features/popular-requests/api";
import { fetchDictionaries } from "@/shared/dictionaries/api";
import { fixtures } from "../fixtures/api";

function clientWith(body: unknown, status = 200): { client: ApiClient; fetchMock: ReturnType<typeof vi.fn> } {
  const fetchMock = vi.fn<typeof fetch>().mockResolvedValue(
    new Response(JSON.stringify(body), {
      status,
      headers: { "Content-Type": "application/json" },
    }),
  );
  const client = new ApiClient({
    baseUrl: "https://api.test.com",
    locale: "ru",
    fetchImpl: fetchMock,
    retry: { maxRetries: 0 },
  });
  return { client, fetchMock };
}

describe("online-board API against real fixtures", () => {
  it("searchFlights parses a by-route response with multiple flights", async () => {
    const { client } = clientWith(fixtures.boardByRoute);
    const result = await searchFlights(client, {
      dateFrom: "2026-04-17",
      dateTo: "2026-04-18",
      departure: "SVO",
      arrival: "LED",
    });
    expect(result.data.routes.length).toBeGreaterThan(0);
    expect(result.data.partners).toBeInstanceOf(Array);
    const first = result.data.routes[0]!;
    expect(first.routeType).toMatch(/Direct|MultiLeg/);
    expect(first.flightId.carrier).toBe("SU");
  });

  it("searchFlights parses a by-flight-number response", async () => {
    const { client } = clientWith(fixtures.boardByFlight);
    const result = await searchFlights(client, {
      dateFrom: "2026-04-17",
      dateTo: "2026-04-18",
      flightNumber: "SU6951",
    });
    expect(result.data.routes[0]!.flightId.flightNumber).toBe("6951");
  });

  it("getFlightDetails parses the onlineboard/details response", async () => {
    const { client } = clientWith(fixtures.onlineboardDetails);
    const result = await getFlightDetails(client, {
      flights: "SU6951",
      dates: "2026-04-17",
    });
    expect(result.data.routes.length).toBeGreaterThan(0);
  });

  it("getCalendarDays parses bitmask into date list", async () => {
    const { client } = clientWith(fixtures.boardDaysFlight);
    const days = await getCalendarDays(client, {
      date: "2026-04-17",
      searchType: "flight",
      flightNumber: "SU6951",
    });
    expect(days.length).toBeGreaterThan(0);
    expect(days[0]).toMatch(/^\d{8}$/);
  });
});

describe("schedule API against real fixtures", () => {
  it("searchSchedule parses an array response", async () => {
    const { client } = clientWith(fixtures.scheduleSearch);
    const result = await searchSchedule(client, {
      departure: "SVO",
      arrival: "LED",
      dateFrom: "2026-04-18",
      dateTo: "2026-04-22",
    });
    expect(Array.isArray(result)).toBe(true);
    expect(result.length).toBeGreaterThan(0);
  });

  it("getScheduleDetails parses a details response", async () => {
    const { client } = clientWith(fixtures.scheduleDetails);
    const result = await getScheduleDetails(client, {
      flights: ["SU6951"],
      dates: ["2026-04-17"],
      departure: "SVO",
      arrival: "LED",
    });
    expect(result.data).toBeDefined();
  });

  it("getScheduleCalendarDays parses the days response", async () => {
    const { client } = clientWith(fixtures.scheduleDaysRoute);
    const days = await getScheduleCalendarDays(client, {
      date: "20260417",
      departure: "SVO",
      arrival: "LED",
      connections: false,
    });
    expect(days).toBeInstanceOf(Array);
  });
});

describe("flights-map API against real fixtures", () => {
  it("searchDestinations parses the destinations response", async () => {
    const { client } = clientWith(fixtures.destinationsFrom);
    const result = await searchDestinations(client, {
      departure: "SVO",
      dateFrom: "2026-04-18",
      dateTo: "2026-04-22",
      connections: 0,
    });
    expect(result.data.routes).toBeInstanceOf(Array);
    expect(result.data.routes.length).toBeGreaterThan(0);
    const first = result.data.routes[0]!;
    expect(first.route).toBeInstanceOf(Array);
    expect(typeof first.isDirect).toBe("boolean");
  });

  it("getFlightsMapCalendar parses the days bitmap", async () => {
    const { client } = clientWith(fixtures.mapDaysRoute);
    const days = await getFlightsMapCalendar(client, {
      date: "20260417",
      departure: "SVO",
      arrival: "LED",
      connections: false,
    });
    expect(days).toBeInstanceOf(Array);
  });
});

describe("popular-requests API against real fixture", () => {
  it("getPopularRequests returns a list of discriminated requests", async () => {
    const { client } = clientWith(fixtures.popularRequests);
    const result = await getPopularRequests(client);
    expect(result.length).toBeGreaterThan(0);
    for (const req of result) {
      expect(["FlightNumber", "Route", "RouteWithBack", "Departure", "Arrival"]).toContain(req.mode);
      expect(["Schedule", "Onlineboard"]).toContain(req.type);
    }
  });
});

describe("dictionaries API against real fixtures", () => {
  it("fetchDictionaries parses all four endpoints", async () => {
    const calls = [
      fixtures.dictionaryRegions,
      fixtures.dictionaryCountries,
      fixtures.dictionaryCities,
      fixtures.dictionaryAirports,
    ];
    let i = 0;
    const fetchMock = vi.fn<typeof fetch>().mockImplementation(() =>
      Promise.resolve(
        new Response(JSON.stringify(calls[i++]!), {
          status: 200,
          headers: { "Content-Type": "application/json" },
        }),
      ),
    );
    const client = new ApiClient({
      baseUrl: "https://api.test.com",
      locale: "ru",
      fetchImpl: fetchMock,
      retry: { maxRetries: 0 },
    });

    const result = await fetchDictionaries(client);
    expect(result.regions.length).toBeGreaterThan(0);
    expect(result.countries.length).toBeGreaterThan(0);
    expect(result.cities.length).toBeGreaterThan(0);
    expect(result.airports.length).toBeGreaterThan(0);
    expect(result.regions[0]!.title).toBeTypeOf("object");
    expect(result.airports[0]!.code).toBeTypeOf("string");
  });
});
