import { test, expect } from "@playwright/test";
import { clickVisibleChannelLink, openApp } from "./helpers";

// Locks channel-view entry + exit: opening a channel from a feed card replaces the view, and
// both the in-page Back and the browser Back return to the feed. (This orthogonal channel branch
// is exactly what the Platform refactor folds into the PageShell contract.)
test.describe("channel view", () => {
  test("open from a card, then in-page Back returns to the feed", async ({ page }) => {
    await openApp(page);
    await expect(page.getByTestId("video-card").first()).toBeVisible();

    await page.getByTestId("video-card-channel").first().click();
    await expect(page.getByTestId("channel-title")).toBeVisible();
    await expect(page.getByTestId("channel-back")).toBeVisible();

    await page.getByTestId("channel-back").click();
    await expect(page.getByTestId("channel-title")).toHaveCount(0);
    await expect(page.getByTestId("video-card").first()).toBeVisible();
  });

  test("browser Back also closes the channel page", async ({ page }) => {
    await openApp(page);
    await expect(page.getByTestId("video-card").first()).toBeVisible();

    await page.getByTestId("video-card-channel").first().click();
    await expect(page.getByTestId("channel-title")).toBeVisible();

    await page.goBack();
    await expect(page.getByTestId("channel-title")).toHaveCount(0);
    await expect(page.getByTestId("video-card").first()).toBeVisible();
  });

  // Bug 1 (Platform S4): opening a channel and coming Back restores the feed's scroll position
  // rather than dumping you at the top. The shell owns the scroller and remembers each view's offset.
  test("Back restores the feed scroll position", async ({ page }) => {
    await openApp(page);
    await expect(page.getByTestId("video-card").first()).toBeVisible();

    const main = page.getByRole("main");
    await main.evaluate((el) => el.scrollBy(0, 3000));
    // Let any page-fetch triggered by the scroll settle, so the same content is cached for the
    // return trip (a fetch still in flight at navigation would leave a shorter list to restore into).
    await page.waitForTimeout(600);
    const saved = await main.evaluate((el) => el.scrollTop);
    expect(saved).toBeGreaterThan(1500);

    // Click a channel card that is actually ON SCREEN — a real user clicks what they see. Clicking
    // `.first()` here would grab an overscan card ABOVE the viewport, and Playwright's scroll-into-
    // view would move the feed before navigating, so Back would restore that moved position, not
    // the one under test. (This is what made the test fail while the app was in fact correct.)
    expect(await clickVisibleChannelLink(page)).toBe(true);
    await expect(page.getByTestId("channel-title")).toBeVisible();
    await page.getByTestId("channel-back").click();
    await expect(page.getByTestId("video-card").first()).toBeVisible();

    // Restored to roughly where we were, not reset to the top. Tolerance absorbs the virtualizer's
    // estimate-vs-measured row heights differing slightly between the two mounts.
    await expect
      .poll(() => page.getByRole("main").evaluate((el) => el.scrollTop), { timeout: 5000 })
      .toBeGreaterThan(saved - 500);
  });

  // Bug 2 (Platform S5): opening a channel no longer FORCES Live/Upcoming on — the channel view
  // seeds its content-type prefs (normal / shorts / live) from the global feed. Locked both ways so
  // a future refactor can't silently re-hardcode it.
  test("the channel view inherits the feed's content filters (no forced Live/Upcoming)", async ({
    page,
  }) => {
    await openApp(page);
    await expect(page.getByTestId("video-card").first()).toBeVisible();
    const feedLive = page.getByTestId("feed-content-includeLive");
    await expect(feedLive).toHaveAttribute("aria-pressed", "false");

    // Inherit OFF: the channel toolbar's Live/Upcoming stays off, not forced on.
    await page.getByTestId("video-card-channel").first().click();
    await expect(page.getByTestId("channel-title")).toBeVisible();
    await expect(page.getByTestId("feed-content-includeLive")).toHaveAttribute(
      "aria-pressed",
      "false",
    );
    await page.getByTestId("channel-back").click();
    await expect(page.getByTestId("channel-title")).toHaveCount(0);

    // Inherit ON: flip the feed's Live/Upcoming on, reopen the channel → it inherits the on state.
    await feedLive.click();
    await expect(feedLive).toHaveAttribute("aria-pressed", "true");
    await expect(page.getByTestId("video-card").first()).toBeVisible();
    await page.getByTestId("video-card-channel").first().click();
    await expect(page.getByTestId("channel-title")).toBeVisible();
    await expect(page.getByTestId("feed-content-includeLive")).toHaveAttribute(
      "aria-pressed",
      "true",
    );
  });

  // Bug 2 (Platform S5): the channel view is its OWN filter scope — a content toggle made there
  // must NOT leak back into the global feed. (FeedFiltersProvider owns the feed's filters; the
  // channel page keeps a separate local cell.)
  test("a channel's content toggle does not leak into the feed", async ({ page }) => {
    await openApp(page);
    await expect(page.getByTestId("video-card").first()).toBeVisible();
    await expect(page.getByTestId("feed-content-includeLive")).toHaveAttribute(
      "aria-pressed",
      "false",
    );

    // Turn Live/Upcoming ON inside the channel view.
    await page.getByTestId("video-card-channel").first().click();
    await expect(page.getByTestId("channel-title")).toBeVisible();
    await page.getByTestId("feed-content-includeLive").click();
    await expect(page.getByTestId("feed-content-includeLive")).toHaveAttribute(
      "aria-pressed",
      "true",
    );

    // Back on the feed, its Live/Upcoming is still off — the channel's change stayed local.
    await page.getByTestId("channel-back").click();
    await expect(page.getByTestId("channel-title")).toHaveCount(0);
    await expect(page.getByTestId("feed-content-includeLive")).toHaveAttribute(
      "aria-pressed",
      "false",
    );
  });

  // Platform S4b: the whole channel chrome (banner, identity, tabs, toolbar) is fixed — it stays
  // put while only the video grid scrolls.
  test("the channel chrome stays fixed while the video grid scrolls", async ({ page }) => {
    await openApp(page);
    await page.getByTestId("video-card-channel").first().click();
    const count = page.getByTestId("feed-result-count");
    const title = page.getByTestId("channel-title");
    await expect(count).toBeVisible();
    await expect(page.getByTestId("video-card").first()).toBeVisible();
    const titleTop = await title.evaluate((el) => el.getBoundingClientRect().top);
    const countTop = await count.evaluate((el) => el.getBoundingClientRect().top);

    // Keep nudging until the grid is tall enough to actually scroll (it may still be filling in).
    await expect
      .poll(async () => {
        await page.getByRole("main").evaluate((el) => el.scrollBy(0, 1500));
        return page.getByRole("main").evaluate((el) => el.scrollTop);
      })
      .toBeGreaterThan(400);

    // The chrome didn't move — banner title and toolbar are in exactly the same place.
    expect(await title.evaluate((el) => el.getBoundingClientRect().top)).toBe(titleTop);
    expect(await count.evaluate((el) => el.getBoundingClientRect().top)).toBe(countTop);
  });
});
