/**
 * P1 e2e coverage: URL guards, breadcrumbs, and cross-section navigation.
 *
 * TZ references:
 *   4.1.2-R11 — out-of-range dates redirect to section start page
 *   4.1.2-R10 — unknown URL renders 404
 *   Table 7   — breadcrumb structure per page tier
 *   Table 10  — cross-section filter carry-over Board ↔ Schedule
 *   §4.1.1 ¶12 — Flight-Map filter is independent (no cross-section carry-over)
 */

import { test, expect } from "./fixtures/console-gate";

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

/** Format a Date as yyyymmdd (no separators). */
function fmt(d: Date): string {
  return `${d.getFullYear()}${String(d.getMonth() + 1).padStart(2, "0")}${String(d.getDate()).padStart(2, "0")}`;
}

/** Return a date that is `n` days offset from today. */
function daysFromNow(n: number): Date {
  const d = new Date();
  d.setDate(d.getDate() + n);
  return d;
}

// ---------------------------------------------------------------------------
// 4.1.2-R11: Out-of-range date redirects to section start page
// ---------------------------------------------------------------------------

test.describe("P1 — 4.1.2-R11: out-of-range dates redirect to start page", () => {
  test("Online-Board flight URL with date +30 days (beyond +14 window) redirects to /onlineboard", async ({
    page,
    consoleMessages,
  }) => {
    const far = fmt(daysFromNow(30));
    await page.goto(`/ru/onlineboard/flight/SU1234-${far}`);
    await page.waitForLoadState("domcontentloaded");
    // Must land on the start page (not the search page).
    // The server normalises /ru → /ru-ru, so accept both locale forms.
    await expect(page).toHaveURL(/\/ru(-ru)?\/onlineboard(\?.*)?$/, { timeout: 10000 });
    await expect(page.locator('[data-testid="online-board-start"]')).toBeVisible({
      timeout: 10000,
    });
  });

  test("Online-Board route URL with date -5 days (before -1 window) redirects to /onlineboard", async ({
    page,
    consoleMessages,
  }) => {
    const past = fmt(daysFromNow(-5));
    await page.goto(`/ru/onlineboard/route/MOW-LED-${past}`);
    await page.waitForLoadState("domcontentloaded");
    await expect(page).toHaveURL(/\/ru(-ru)?\/onlineboard(\?.*)?$/, { timeout: 10000 });
    await expect(page.locator('[data-testid="online-board-start"]')).toBeVisible({
      timeout: 10000,
    });
  });

  test("Schedule route URL with date +400 days (beyond +330 window) redirects to /schedule", async ({
    page,
    consoleMessages,
  }) => {
    const farFrom = fmt(daysFromNow(400));
    const farTo = fmt(daysFromNow(407));
    await page.goto(`/ru/schedule/route/MOW-LED-${farFrom}-${farTo}`);
    await page.waitForLoadState("domcontentloaded");
    await expect(page).toHaveURL(/\/ru(-ru)?\/schedule(\?.*)?$/, { timeout: 10000 });
    await expect(page.locator('[data-testid="schedule-start"]')).toBeVisible({
      timeout: 10000,
    });
  });
});

// ---------------------------------------------------------------------------
// 4.1.2-R10: Unknown URL → 404
// ---------------------------------------------------------------------------

test.describe("P1 — 4.1.2-R10: unknown URL shows 404", () => {
  test("/ru/nonexistent does not crash — shows error or empty body", async ({
    page,
    consoleMessages,
  }) => {
    // Matches the existing pattern in navigation.spec.ts: the app handles
    // unknown routes gracefully (404 page or redirect, not a JS crash).
    await page.goto("/ru/nonexistent");
    await page.waitForLoadState("domcontentloaded");
    await expect(page.locator("body")).not.toBeEmpty();
  });

  test("/error/404 renders 404 content (client-side navigation)", async ({
    page,
    consoleMessages,
  }) => {
    // Direct URL navigate to the error route produces blank SSR output;
    // client-side assign is the established pattern (see navigation.spec.ts).
    await page.goto("/ru/onlineboard");
    await page.waitForLoadState("domcontentloaded");
    await expect(page.locator('[data-testid="online-board-start"]')).toBeVisible({
      timeout: 10000,
    });

    await page.evaluate(() => window.location.assign("/error/404"));
    await page.waitForLoadState("domcontentloaded");
    await expect(page.locator(".error-page__code")).toHaveText("404", {
      timeout: 10000,
    });
  });
});

// ---------------------------------------------------------------------------
// Table 7: Breadcrumbs — start pages show Home only (1 item)
// ---------------------------------------------------------------------------

test.describe("P1 — Table 7: breadcrumbs on start pages (Home only)", () => {
  test("Online-Board start page has exactly 1 breadcrumb (Home)", async ({
    page,
    consoleMessages,
  }) => {
    await page.goto("/ru/onlineboard");
    await page.waitForLoadState("domcontentloaded");
    await expect(page.locator('[data-testid="online-board-start"]')).toBeVisible({
      timeout: 10000,
    });

    const crumbs = page.locator('[data-testid="breadcrumbs"] li');
    await expect(crumbs).toHaveCount(1);
  });

  test("Schedule start page has exactly 1 breadcrumb (Home)", async ({
    page,
    consoleMessages,
  }) => {
    await page.goto("/ru/schedule");
    await page.waitForLoadState("domcontentloaded");
    await expect(page.locator('[data-testid="schedule-start"]')).toBeVisible({
      timeout: 10000,
    });

    const crumbs = page.locator('[data-testid="breadcrumbs"] li');
    await expect(crumbs).toHaveCount(1);
  });

  test("Flight-Map start page has exactly 1 breadcrumb (Home)", async ({
    page,
    consoleMessages,
  }) => {
    await page.goto("/ru/flights-map");
    await page.waitForLoadState("domcontentloaded");

    // Feature flag may disable the map; either state is acceptable for
    // this breadcrumb test — the breadcrumb renders in both cases.
    const mapStart = page.locator('[data-testid="flights-map-start"]');
    const mapDisabled = page.locator('[data-testid="flights-map-disabled"]');
    await expect(mapStart.or(mapDisabled)).toBeVisible({ timeout: 10000 });

    const crumbs = page.locator('[data-testid="breadcrumbs"] li');
    await expect(crumbs).toHaveCount(1);
  });
});

// ---------------------------------------------------------------------------
// Table 7: Breadcrumbs — search pages show Home + Section (2 items)
//   Online-Board search: [Home, "Онлайн-Табло"]
//   Schedule search: [Home, "Расписание", <route heading>] → 3 items
// ---------------------------------------------------------------------------

test.describe("P1 — Table 7: breadcrumbs on search pages", () => {
  test("Online-Board route search page has 2 breadcrumbs (Home + Section)", async ({
    page,
    consoleMessages,
  }) => {
    // Use an in-window date so the guard lets the page through.
    const today = fmt(new Date());
    await page.goto(`/ru/onlineboard/route/MOW-LED-${today}`);
    await page.waitForLoadState("domcontentloaded");
    await expect(page.locator("body")).not.toBeEmpty();

    // Wait for the breadcrumbs to hydrate (lazy load means SSR crumbs may not
    // be present immediately).
    const crumbs = page.locator('[data-testid="breadcrumbs"] li');
    await expect(crumbs).toHaveCount(2, { timeout: 10000 });
  });

  test("Online-Board flight search page has 2 breadcrumbs (Home + Section)", async ({
    page,
    consoleMessages,
  }) => {
    const today = fmt(new Date());
    await page.goto(`/ru/onlineboard/flight/SU0100-${today}`);
    await page.waitForLoadState("domcontentloaded");
    await expect(page.locator("body")).not.toBeEmpty();

    const crumbs = page.locator('[data-testid="breadcrumbs"] li');
    await expect(crumbs).toHaveCount(2, { timeout: 10000 });
  });

  test("Schedule route search page has 3 breadcrumbs (Home + Section + Route heading)", async ({
    page,
    consoleMessages,
  }) => {
    const today = fmt(new Date());
    const weekAhead = fmt(daysFromNow(7));
    await page.goto(`/ru/schedule/route/MOW-LED-${today}-${weekAhead}`);
    await page.waitForLoadState("domcontentloaded");
    await expect(page.locator("body")).not.toBeEmpty();

    const crumbs = page.locator('[data-testid="breadcrumbs"] li');
    await expect(crumbs).toHaveCount(3, { timeout: 10000 });
  });
});

// ---------------------------------------------------------------------------
// Cross-section filter carry-over — TZ Table 10
// Requires city-picker interaction and live API; skipped with TODO markers.
// ---------------------------------------------------------------------------

test.describe("P1 — Table 10: cross-section filter carry-over Board ↔ Schedule", () => {
  // TODO: These tests require filling the city-picker (autocomplete with live
  // API) and submitting a search to write the cross-section store. The store
  // state is then read when navigating to the other section. This is too
  // fiddly to implement reliably without network stubs / API availability
  // guarantees in CI. Re-enable when a mock API or recorded fixture is in place.

  test.fixme(
    "navigating from Online-Board results to Schedule preserves departure/arrival cities",
    async ({ page, consoleMessages }) => {
      // 1. Search on Online-Board (route tab) → MOW-LED.
      // 2. Navigate to /ru/schedule.
      // 3. Schedule start page filter should be pre-filled with MOW/LED via
      //    the cross-section store (crossSectionNavigation.ts).
      // Skipped: needs city-picker interaction + live API for autocomplete.
      void page;
    },
  );

  test.fixme(
    "navigating from Schedule results to Online-Board preserves departure/arrival cities",
    async ({ page, consoleMessages }) => {
      // 1. Search on Schedule (route tab) → MOW-LED.
      // 2. Navigate to /ru/onlineboard.
      // 3. Online-Board start page filter should be pre-filled with MOW/LED.
      // Skipped: needs city-picker interaction + live API for autocomplete.
      void page;
    },
  );
});

// ---------------------------------------------------------------------------
// Flight-Map filter independence — TZ §4.1.1 ¶12
// Requires city-picker interaction; skipped with TODO markers.
// ---------------------------------------------------------------------------

test.describe("P1 — §4.1.1 ¶12: Flight-Map filter is independent (no carry-over)", () => {
  // TODO: Verify that searching on Online-Board does NOT pre-fill the
  // Flight-Map filter, and vice-versa. Requires live city-picker + submit.
  // Re-enable when network stubs are available.

  test.fixme(
    "Online-Board search does not affect Flight-Map departure city",
    async ({ page, consoleMessages }) => {
      // 1. Search on Online-Board → MOW-LED.
      // 2. Navigate to /ru/flights-map.
      // 3. Flight-Map departure input should NOT show MOW (store is separate).
      void page;
    },
  );
});
