/**
 * Tests for the app's bottom-UI (mini player + bottom nav) visibility rules.
 *
 * The overlap bug these guard against: on the library page the last rows were
 * hidden behind the bottom nav. It was caused by rendering the bottom UI as a
 * FIXED overlay and trying to reserve its (async-measured, initially-0) height
 * as padding. The fix renders the bottom UI as an in-flow flex child below the
 * scroller, so overlap is structurally impossible — no measurement, no padding.
 *
 * These pure functions only decide *whether* each piece shows on a route. The
 * structural guarantee (flex sibling below the scroller) is exercised by
 * running the app, not by jsdom (which has no layout engine).
 *
 * TRACES: UR-005 | DR-009
 */

import { describe, it, expect } from "vitest";
import {
  showBottomNav,
  showGlobalMiniPlayer,
  showGlobalHeader,
  routeOwnsLayout,
  showBottomUi,
} from "./layoutShell";

const authed = (pathname: string) => ({ pathname, isAuthenticated: true });

describe("showGlobalMiniPlayer", () => {
  it("shows on the main library page (regression: was hidden on non-Android)", () => {
    expect(showGlobalMiniPlayer({ pathname: "/library" })).toBe(true);
  });

  it("shows on a deep library page", () => {
    expect(showGlobalMiniPlayer({ pathname: "/library/abc123" })).toBe(true);
  });

  it("shows on home, search, downloads", () => {
    expect(showGlobalMiniPlayer({ pathname: "/" })).toBe(true);
    expect(showGlobalMiniPlayer({ pathname: "/search" })).toBe(true);
    expect(showGlobalMiniPlayer({ pathname: "/downloads" })).toBe(true);
  });

  it("hides on the full-screen player, login, and settings", () => {
    expect(showGlobalMiniPlayer({ pathname: "/player/xyz" })).toBe(false);
    expect(showGlobalMiniPlayer({ pathname: "/login" })).toBe(false);
    expect(showGlobalMiniPlayer({ pathname: "/settings" })).toBe(false);
  });

  it("does NOT depend on platform or on /library — one code path everywhere", () => {
    // The old bug was a platform/route split that let a second mini player exist.
    expect(showGlobalMiniPlayer({ pathname: "/library" })).toBe(true);
  });
});

describe("showBottomNav", () => {
  it("shows on authenticated content routes including library and settings", () => {
    expect(showBottomNav(authed("/library"))).toBe(true);
    expect(showBottomNav(authed("/"))).toBe(true);
    expect(showBottomNav(authed("/settings"))).toBe(true);
  });

  it("hides when unauthenticated", () => {
    expect(showBottomNav({ pathname: "/library", isAuthenticated: false })).toBe(false);
  });

  it("hides on the full-screen player and login", () => {
    expect(showBottomNav(authed("/player/xyz"))).toBe(false);
    expect(showBottomNav(authed("/login"))).toBe(false);
  });
});

describe("routeOwnsLayout", () => {
  it("is true for library/player/login (they render their own flex column + BottomUi)", () => {
    expect(routeOwnsLayout({ pathname: "/library" })).toBe(true);
    expect(routeOwnsLayout({ pathname: "/library/abc" })).toBe(true);
    expect(routeOwnsLayout({ pathname: "/player/x" })).toBe(true);
    expect(routeOwnsLayout({ pathname: "/login" })).toBe(true);
  });

  it("is false for routes that render into the root scroller (incl. settings)", () => {
    // Settings has no +layout of its own; it must flow through the root scroller
    // so the root's in-flow BottomUi renders below it (otherwise settings loses
    // its nav, since the fixed-overlay nav no longer exists).
    expect(routeOwnsLayout({ pathname: "/" })).toBe(false);
    expect(routeOwnsLayout({ pathname: "/search" })).toBe(false);
    expect(routeOwnsLayout({ pathname: "/downloads" })).toBe(false);
    expect(routeOwnsLayout({ pathname: "/settings" })).toBe(false);
  });
});

describe("showGlobalHeader", () => {
  it("shows the root-owned header (with account menu) on home, search, downloads", () => {
    // The whole point of UR-054: account actions reachable without going to Library.
    expect(showGlobalHeader(authed("/"))).toBe(true);
    expect(showGlobalHeader(authed("/search"))).toBe(true);
    expect(showGlobalHeader(authed("/downloads"))).toBe(true);
  });

  it("does NOT show on library (it renders its own AppHeader)", () => {
    expect(showGlobalHeader(authed("/library"))).toBe(false);
    expect(showGlobalHeader(authed("/library/abc"))).toBe(false);
  });

  it("does NOT show on settings (owns its content; no account menu needed there)", () => {
    expect(showGlobalHeader(authed("/settings"))).toBe(false);
  });

  it("does NOT show on the immersive player or login", () => {
    expect(showGlobalHeader(authed("/player/x"))).toBe(false);
    expect(showGlobalHeader(authed("/login"))).toBe(false);
  });

  it("hides when unauthenticated", () => {
    expect(showGlobalHeader({ pathname: "/", isAuthenticated: false })).toBe(false);
  });
});

describe("structural invariant: every route that shows bottom UI has a scroller above it", () => {
  // With the in-flow model, "the bottom UI is a flex sibling below a scroller"
  // must hold on every route where it shows. That scroller is provided by
  // exactly one owner:
  //   - routeOwnsLayout === true  -> the route's own column (header + main + BottomUi)
  //   - routeOwnsLayout === false -> the root column (scroller + BottomUi)
  // The forbidden state — bottom UI shows but no owning column renders a
  // scroller + BottomUi pair — cannot occur because the two branches are total.
  const routes = ["/", "/search", "/downloads", "/library", "/library/abc", "/settings"];

  for (const pathname of routes) {
    it(`${pathname}: bottom UI shows and has a defined layout owner`, () => {
      expect(showBottomUi(authed(pathname))).toBe(true);
      expect(typeof routeOwnsLayout({ pathname })).toBe("boolean");
    });
  }

  it("library owns its layout, so it renders its own in-flow BottomUi", () => {
    // Directly pins the original regression: library must render BottomUi inside
    // its own column (the root gives owning routes a clipped, non-scrolling box).
    expect(showBottomUi(authed("/library"))).toBe(true);
    expect(routeOwnsLayout({ pathname: "/library" })).toBe(true);
  });

  it("settings does NOT own its layout, so the root scroller + BottomUi cover it", () => {
    expect(showBottomUi(authed("/settings"))).toBe(true);
    expect(routeOwnsLayout({ pathname: "/settings" })).toBe(false);
  });

  it("the full-screen player shows no bottom UI and owns its layout", () => {
    expect(showBottomUi(authed("/player/x"))).toBe(false);
    expect(routeOwnsLayout({ pathname: "/player/x" })).toBe(true);
  });
});
