import { describe, it, expect, vi, beforeEach } from "vitest";

const goto = vi.fn();
// Capture the afterNavigate callback so tests can simulate navigations and thus
// drive the in-app depth counter that canGoBack/navigateBack rely on.
let afterNavigateCb: ((nav: { from: unknown; to: unknown; delta?: number }) => void) | null =
  null;
vi.mock("$app/navigation", () => ({
  goto: (...args: unknown[]) => goto(...args),
  afterNavigate: (cb: (nav: any) => void) => {
    afterNavigateCb = cb;
  },
}));

import {
  navigateUp,
  navigateBack,
  canGoBack,
  registerNavigationTracking,
  __resetNavigationDepthForTest,
} from "./navigation";

/** Simulate a SvelteKit navigation to move the depth counter. */
function nav(opts: { from?: boolean; delta?: number }) {
  afterNavigateCb?.({
    from: opts.from === false ? null : {},
    to: {},
    delta: opts.delta,
  });
}

describe("navigation", () => {
  beforeEach(() => {
    goto.mockClear();
    // registerNavigationTracking is idempotent; the first call in the suite wins
    // and wires afterNavigateCb. Ensure it is registered, then reset depth so
    // each case starts from the entry page (module state persists otherwise).
    registerNavigationTracking();
    __resetNavigationDepthForTest();
  });

  describe("navigateUp", () => {
    it("always goes to the given parent path, never touching history", () => {
      const back = vi.fn();
      vi.spyOn(history, "back").mockImplementation(back);

      navigateUp("/library/music");

      expect(goto).toHaveBeenCalledWith("/library/music");
      expect(back).not.toHaveBeenCalled();
    });
  });

  describe("navigateBack / canGoBack", () => {
    it("falls back to the path when there is no in-app history yet", () => {
      // Fresh session: only the initial load happened (from == null), so depth
      // stays at 0 and there is nothing to pop.
      nav({ from: false });
      expect(canGoBack()).toBe(false);

      const back = vi.fn();
      vi.spyOn(history, "back").mockImplementation(back);

      navigateBack("/library");

      expect(goto).toHaveBeenCalledWith("/library");
      expect(back).not.toHaveBeenCalled();
    });

    it("pops history after a real in-app forward navigation", () => {
      nav({ from: false }); // initial load
      nav({}); // navigated deeper within the app
      expect(canGoBack()).toBe(true);

      const back = vi.fn();
      vi.spyOn(history, "back").mockImplementation(back);

      navigateBack("/library");

      expect(back).toHaveBeenCalledOnce();
      expect(goto).not.toHaveBeenCalled();
    });

    it("does not go below zero when the user backs out to the entry page", () => {
      nav({ from: false }); // load
      nav({}); // forward → depth 1
      nav({ delta: -1 }); // back → depth 0
      nav({ delta: -1 }); // extra back (e.g. stale delta) must not underflow
      expect(canGoBack()).toBe(false);

      navigateBack("/library/music");
      expect(goto).toHaveBeenCalledWith("/library/music");
    });
  });
});
