import { describe, expect, it } from "vitest";
import {
  buildScheduleStartSeo,
  buildScheduleSearchSeo,
  buildScheduleDetailsSeo,
} from "./seo.js";
import type { ISimpleFlight, IScheduleFlightId } from "./types.js";

/** Stub t() that returns the key + interpolation vars for assertion. */
function stubT(key: string, opts?: Record<string, unknown>): string {
  if (opts && Object.keys(opts).length > 0) {
    const vars = Object.entries(opts)
      .map(([k, v]) => `${k}=${v}`)
      .join(",");
    return `${key}|${vars}`;
  }
  return key;
}

const CANONICAL = "https://www.aeroflot.ru";

describe("buildScheduleStartSeo", () => {
  it("uses MAIN translation keys", () => {
    const result = buildScheduleStartSeo(stubT, "ru", CANONICAL);

    expect(result.title).toBe("SEO.SCHEDULE.MAIN.TITLE");
    expect(result.description).toBe("SEO.SCHEDULE.MAIN.DESCRIPTION");
  });

  it("sets canonical to /{locale}/schedule", () => {
    const result = buildScheduleStartSeo(stubT, "en", CANONICAL);

    expect(result.canonical).toBe("https://www.aeroflot.ru/en/schedule");
  });

  it("includes hreflang with 10 entries", () => {
    const result = buildScheduleStartSeo(stubT, "ru", CANONICAL);

    expect(result.hreflang).toHaveLength(10);
  });

  it("sets og tags", () => {
    const result = buildScheduleStartSeo(stubT, "ru", CANONICAL);

    expect(result.og.title).toBe(result.title);
    expect(result.og.type).toBe("website");
    expect(result.og.locale).toBe("ru");
    expect(result.og.siteName).toBe("Aeroflot");
  });

  it("sets twitter card", () => {
    const result = buildScheduleStartSeo(stubT, "ru", CANONICAL);

    expect(result.twitter).toBeDefined();
    expect(result.twitter!.card).toBe("summary");
  });

  it("emits WebSite JSON-LD on the start page", () => {
    const result = buildScheduleStartSeo(stubT, "ru", CANONICAL);

    expect(result.jsonLd).toBeDefined();
    const ld = result.jsonLd as Record<string, unknown>;
    expect(ld["@type"]).toBe("WebSite");
    expect(ld["url"]).toBe("https://www.aeroflot.ru/ru/schedule");
    expect(ld["name"]).toBe("Aeroflot");
  });
});

describe("buildScheduleSearchSeo", () => {
  const params = {
    type: "route" as const,
    outbound: {
      departure: "MOW",
      arrival: "KUF",
      dateFrom: "20220425",
      dateTo: "20220501",
    },
  };

  it("uses SEARCH translation keys with interpolation", () => {
    const result = buildScheduleSearchSeo(stubT, params, "ru", CANONICAL);

    expect(result.title).toContain("SEO.SCHEDULE.SEARCH.TITLE");
    expect(result.title).toContain("departureCity=MOW");
    expect(result.title).toContain("arrivalCity=KUF");
    expect(result.title).toContain("dateFrom=25.04.2022");
    expect(result.title).toContain("dateTo=01.05.2022");
  });

  it("uses city names when provided", () => {
    const result = buildScheduleSearchSeo(
      stubT,
      params,
      "ru",
      CANONICAL,
      { departure: "Moscow", arrival: "Samara" },
    );

    expect(result.title).toContain("departureCity=Moscow");
    expect(result.title).toContain("arrivalCity=Samara");
  });

  it("sets canonical to the route search URL", () => {
    const result = buildScheduleSearchSeo(stubT, params, "ru", CANONICAL);

    expect(result.canonical).toBe(
      "https://www.aeroflot.ru/ru/schedule/route/MOW-KUF-20220425-20220501",
    );
  });

  it("includes hreflang entries", () => {
    const result = buildScheduleSearchSeo(stubT, params, "ru", CANONICAL);

    expect(result.hreflang).toHaveLength(10);
  });

  it("handles round-trip params", () => {
    const rtParams = {
      type: "roundtrip" as const,
      outbound: {
        departure: "MOW",
        arrival: "KUF",
        dateFrom: "20220425",
        dateTo: "20220501",
      },
      inbound: {
        departure: "KUF",
        arrival: "MOW",
        dateFrom: "20220502",
        dateTo: "20220508",
      },
    };

    const result = buildScheduleSearchSeo(stubT, rtParams, "ru", CANONICAL);

    expect(result.canonical).toContain(
      "schedule/route/MOW-KUF-20220425-20220501/KUF-MOW-20220502-20220508",
    );
  });
});

describe("buildScheduleDetailsSeo", () => {
  const flightIds: IScheduleFlightId[] = [
    { carrier: "SU", flightNumber: "0012", date: "20220527" },
  ];

  const flight: ISimpleFlight = {
    id: "SU0012-20220527",
    routeType: "Direct",
    flightId: {
      carrier: "SU",
      flightNumber: "0012",
      suffix: "",
      date: "20220527",
    },
    flyingTime: "1h 30m",
    operatingBy: {},
    status: "Scheduled",
    leg: {
      index: 0,
      status: "Scheduled",
      flyingTime: "1h 30m",
      updated: "2022-05-27T10:00:00Z",
      dayChange: 0,
      equipment: { name: "Airbus A320", code: "320" },
      flags: { checkinAvailable: false, returnToAirport: false, routeChanged: false },
      operatingBy: {},
      departure: {
        scheduled: {
          airport: "Sheremetyevo",
          airportCode: "SVO",
          city: "Moscow",
          cityCode: "MOW",
          countryCode: "RU",
        },
        checkingStatus: "closed",
        times: {
          scheduledDeparture: {
            dayChange: { value: 0, title: "" },
            local: "2022-05-27T10:00:00",
            localTime: "10:00",
            tzOffset: 3,
            utc: "2022-05-27T07:00:00Z",
          },
        },
      },
      arrival: {
        scheduled: {
          airport: "Pulkovo",
          airportCode: "LED",
          city: "Saint Petersburg",
          cityCode: "LED",
          countryCode: "RU",
        },
        times: {
          scheduledArrival: {
            dayChange: { value: 0, title: "" },
            local: "2022-05-27T11:30:00",
            localTime: "11:30",
            tzOffset: 3,
            utc: "2022-05-27T08:30:00Z",
          },
        },
      },
    },
  };

  it("uses TITLE-DIRECT key for single flight number (direct)", () => {
    const result = buildScheduleDetailsSeo(stubT, [flight], "ru", CANONICAL, flightIds);

    expect(result.title).toContain("SEO.SCHEDULE.FLIGHT-DETAILS.TITLE-DIRECT");
    expect(result.title).toContain("flightNumber=SU 0012");
    expect(result.title).toContain("routeCities=Moscow-Saint Petersburg");
  });

  it("uses TITLE-CONNECTING key for multiple distinct flight numbers (connecting)", () => {
    const connectingFlightIds: IScheduleFlightId[] = [
      { carrier: "SU", flightNumber: "1234", date: "20220527" },
      { carrier: "SU", flightNumber: "5678", date: "20220527" },
    ];

    const result = buildScheduleDetailsSeo(stubT, [flight], "ru", CANONICAL, connectingFlightIds);

    expect(result.title).toContain("SEO.SCHEDULE.FLIGHT-DETAILS.TITLE-CONNECTING");
    expect(result.title).toContain("flightNumbers=SU 1234, SU 5678");
  });

  it("uses TITLE-DIRECT for multi-segment flight (same flight number across legs)", () => {
    const multiSegFlight: ISimpleFlight = {
      id: "SU0012-20220527",
      routeType: "MultiLeg",
      flightId: { carrier: "SU", flightNumber: "0012", suffix: "", date: "20220527" },
      flyingTime: "3h 00m",
      operatingBy: {},
      status: "Scheduled",
      legs: [
        {
          index: 0,
          status: "Scheduled",
          flyingTime: "1h 30m",
          updated: "2022-05-27T10:00:00Z",
          dayChange: 0,
          equipment: { name: "Airbus A320", code: "320" },
          flags: { checkinAvailable: false, returnToAirport: false, routeChanged: false },
          operatingBy: {},
          departure: {
            scheduled: {
              airport: "Sheremetyevo",
              airportCode: "SVO",
              city: "Moscow",
              cityCode: "MOW",
              countryCode: "RU",
            },
            checkingStatus: "closed",
            times: {
              scheduledDeparture: {
                dayChange: { value: 0, title: "" },
                local: "2022-05-27T10:00:00",
                localTime: "10:00",
                tzOffset: 3,
                utc: "2022-05-27T07:00:00Z",
              },
            },
          },
          arrival: {
            scheduled: {
              airport: "Tolmachevo",
              airportCode: "OVB",
              city: "Novosibirsk",
              cityCode: "OVB",
              countryCode: "RU",
            },
            times: {
              scheduledArrival: {
                dayChange: { value: 0, title: "" },
                local: "2022-05-27T14:00:00",
                localTime: "14:00",
                tzOffset: 7,
                utc: "2022-05-27T07:00:00Z",
              },
            },
          },
        },
        {
          index: 1,
          status: "Scheduled",
          flyingTime: "1h 30m",
          updated: "2022-05-27T14:30:00Z",
          dayChange: 0,
          equipment: { name: "Airbus A320", code: "320" },
          flags: { checkinAvailable: false, returnToAirport: false, routeChanged: false },
          operatingBy: {},
          departure: {
            scheduled: {
              airport: "Tolmachevo",
              airportCode: "OVB",
              city: "Novosibirsk",
              cityCode: "OVB",
              countryCode: "RU",
            },
            checkingStatus: "closed",
            times: {
              scheduledDeparture: {
                dayChange: { value: 0, title: "" },
                local: "2022-05-27T14:30:00",
                localTime: "14:30",
                tzOffset: 7,
                utc: "2022-05-27T07:30:00Z",
              },
            },
          },
          arrival: {
            scheduled: {
              airport: "Vladivostok",
              airportCode: "VVO",
              city: "Vladivostok",
              cityCode: "VVO",
              countryCode: "RU",
            },
            times: {
              scheduledArrival: {
                dayChange: { value: 0, title: "" },
                local: "2022-05-27T17:00:00",
                localTime: "17:00",
                tzOffset: 10,
                utc: "2022-05-27T07:00:00Z",
              },
            },
          },
        },
      ],
    };

    // Multi-segment = same flight number on all legs → treated as direct (singular)
    const result = buildScheduleDetailsSeo(stubT, [multiSegFlight], "ru", CANONICAL, flightIds);

    expect(result.title).toContain("SEO.SCHEDULE.FLIGHT-DETAILS.TITLE-DIRECT");
    expect(result.title).toContain("flightNumber=SU 0012");
    expect(result.title).toContain("routeCities=Moscow-Vladivostok");
  });

  it("uses TITLE-NO-ROUTE-DIRECT when flight data is empty (SSR without loaded data)", () => {
    const result = buildScheduleDetailsSeo(stubT, [], "ru", CANONICAL, flightIds);

    expect(result.title).toContain("SEO.SCHEDULE.FLIGHT-DETAILS.TITLE-NO-ROUTE-DIRECT");
    expect(result.title).toContain("flightNumber=SU 0012");
    expect(result.title).not.toContain("routeCities");
  });

  it("sets canonical to the details URL", () => {
    const result = buildScheduleDetailsSeo(stubT, [flight], "ru", CANONICAL, flightIds);

    expect(result.canonical).toBe(
      "https://www.aeroflot.ru/ru/schedule/SU0012-20220527",
    );
  });

  it("includes hreflang entries", () => {
    const result = buildScheduleDetailsSeo(stubT, [flight], "ru", CANONICAL, flightIds);

    expect(result.hreflang).toHaveLength(10);
  });

  it("sets og.type to article for details", () => {
    const result = buildScheduleDetailsSeo(stubT, [flight], "ru", CANONICAL, flightIds);

    expect(result.og.type).toBe("article");
  });
});

// ---------------------------------------------------------------------------
// TZ §4.1.19 — OpenGraph completeness assertions
// ---------------------------------------------------------------------------

describe("4.1.19-R: OpenGraph completeness (Schedule)", () => {
  const ORIGIN = "https://flights.aeroflot.ru";

  it("4.1.19-R: Schedule start page has all required OG tags", () => {
    const seo = buildScheduleStartSeo(stubT, "ru", ORIGIN);

    expect(seo.og.title).toBeTruthy();
    expect(seo.og.description).toBeTruthy();
    expect(seo.og.url).toMatch(/^https:\/\//);
    expect(seo.og.image).toMatch(/^https:\/\//);
    expect(seo.og.type).toBe("website");
    expect(seo.og.locale).toBe("ru");
    expect(seo.og.siteName).toBe("Aeroflot");
  });

  it("4.1.19-R: Schedule search page has all required OG tags", () => {
    const params = {
      type: "route" as const,
      outbound: { departure: "MOW", arrival: "LED", dateFrom: "20250115", dateTo: "20250120" },
    };
    const seo = buildScheduleSearchSeo(stubT, params, "ru", ORIGIN);

    expect(seo.og.title).toBeTruthy();
    expect(seo.og.description).toBeTruthy();
    expect(seo.og.url).toMatch(/^https:\/\//);
    expect(seo.og.image).toMatch(/^https:\/\//);
    expect(seo.og.type).toBe("website");
    expect(seo.og.locale).toBe("ru");
    expect(seo.og.siteName).toBe("Aeroflot");
  });

  it("4.1.19-R: Schedule details page has all required OG tags", () => {
    const fIds: IScheduleFlightId[] = [{ carrier: "SU", flightNumber: "0012", date: "20220527" }];
    const seo = buildScheduleDetailsSeo(stubT, [], "ru", ORIGIN, fIds);

    expect(seo.og.title).toBeTruthy();
    expect(seo.og.description).toBeTruthy();
    expect(seo.og.url).toMatch(/^https:\/\//);
    expect(seo.og.image).toMatch(/^https:\/\//);
    expect(seo.og.type).toBe("article");
    expect(seo.og.locale).toBe("ru");
    expect(seo.og.siteName).toBe("Aeroflot");
  });
});

// ---------------------------------------------------------------------------
// TZ §4.1.20 — Robots, canonical, hreflang assertions (Schedule)
// ---------------------------------------------------------------------------

describe("4.1.20-R: canonical + hreflang (Schedule)", () => {
  const ORIGIN = "https://flights.aeroflot.ru";

  it("4.1.20-R: Schedule start canonical has no query params", () => {
    const seo = buildScheduleStartSeo(stubT, "ru", ORIGIN);

    expect(seo.canonical).not.toContain("?");
    expect(seo.canonical).toBe(`${ORIGIN}/ru/schedule`);
  });

  it("4.1.20-R: hreflang has all 9 languages + x-default", () => {
    const seo = buildScheduleStartSeo(stubT, "ru", ORIGIN);

    expect(seo.hreflang.length).toBeGreaterThanOrEqual(9);
    expect(seo.hreflang.some((h) => h.lang === "x-default")).toBe(true);
  });

  it("4.1.20-R: hreflang covers all 9 supported languages", () => {
    const seo = buildScheduleStartSeo(stubT, "ru", ORIGIN);
    const langs = seo.hreflang.map((h) => h.lang);

    expect(langs).toContain("ru");
    expect(langs).toContain("en");
    expect(langs).toContain("es");
    expect(langs).toContain("fr");
    expect(langs).toContain("it");
    expect(langs).toContain("ja");
    expect(langs).toContain("ko");
    expect(langs).toContain("zh");
    expect(langs).toContain("de");
  });

  it("4.1.20-R: hreflang hrefs are absolute URLs", () => {
    const seo = buildScheduleStartSeo(stubT, "ru", ORIGIN);

    for (const entry of seo.hreflang) {
      expect(entry.href).toMatch(/^https:\/\//);
    }
  });

  it("4.1.20-R: noindex is not set on standard indexable Schedule pages", () => {
    const seo = buildScheduleStartSeo(stubT, "ru", ORIGIN);

    expect(seo.noindex).toBeFalsy();
  });
});
