import { goto, afterNavigate } from "$app/navigation";

/**
 * App navigation has two distinct affordances (per the Android guidelines):
 *
 *  - **Up** — move to the current screen's *logical parent* in the app
 *    hierarchy (e.g. `/library/music/albums` → `/library/music`). Deterministic,
 *    derived from the route, and never depends on how the user got here. This is
 *    what the in-app header arrows should do almost everywhere.
 *
 *  - **Back** — pop the *actual* history stack: return to wherever the user came
 *    from, which may be a sibling branch (a detail page reached from search vs.
 *    from the library) or even outside the app. This is the hardware/gesture
 *    back button's job; use it in-app only where "return to origin" is genuinely
 *    better than Up (e.g. a detail page with many entry points).
 *
 * The old single `navigateBack` conflated the two: it called `history.back()`
 * first and only fell back to a path. On resume-from-background the WebView can
 * restore a history stack whose `length` is still > 1 but which cannot actually
 * go back within the app — so `history.back()` no-ops and the user is trapped on
 * the page. Splitting Up (pure `goto`) from Back (tracked in-app depth) removes
 * that trap: Up can never get stuck, and Back only fires when we *know* there is
 * an in-app entry to return to.
 */

// In-app navigation depth, maintained via the public `afterNavigate` hook rather
// than reading SvelteKit's internal history-state key. Starts at 0 (the entry
// page). Each forward in-app navigation increments it; a popstate (back/forward
// gesture) sets it to the delta-adjusted value. When it is > 0 we know a real
// in-app Back exists and won't strand the user — independent of the WebView's
// possibly-stale `history.length` after a background/restore.
let inAppDepth = 0;
let navHookRegistered = false;

/**
 * Register the navigation-depth tracker. Call once from the root layout's
 * component init (afterNavigate must run in a component context). Safe to call
 * more than once — only the first registration takes effect.
 */
export function registerNavigationTracking(): void {
  if (navHookRegistered) return;
  navHookRegistered = true;

  afterNavigate((nav) => {
    // A popstate (hardware/gesture back or forward) carries a delta; apply it so
    // depth tracks the true stack position. Programmatic goto/link navigations
    // have no delta and move one step deeper.
    const delta = nav.delta;
    if (typeof delta === "number") {
      inAppDepth = Math.max(0, inAppDepth + delta);
    } else if (nav.from) {
      // A real forward navigation from an existing page (not the initial load).
      inAppDepth += 1;
    }
  });
}

/**
 * Reset the tracked depth. Intended for tests only, so each case starts from a
 * known baseline (module state persists across a test file otherwise).
 */
export function __resetNavigationDepthForTest(): void {
  inAppDepth = 0;
}

/**
 * True when there is at least one in-app history entry to pop. Unlike
 * `history.length > 1`, this reflects navigations that happened *within this app
 * session*, so a stale WebView stack after a background/restore can't fool it.
 */
export function canGoBack(): boolean {
  return inAppDepth > 0;
}

/**
 * **Up**: go to the given logical parent path. Always deterministic; never
 * consults history, so it cannot trap the user. Prefer this for header arrows.
 */
export function navigateUp(parentPath: string): void {
  goto(parentPath);
}

/**
 * **Back**: return to the previous in-app page when there is one, otherwise fall
 * back to `fallbackPath` (typically the logical parent) so the user is never
 * stranded. Use only where returning to the exact origin is preferable to Up
 * (e.g. a detail page reachable from multiple branches).
 */
export function navigateBack(fallbackPath: string): void {
  if (canGoBack()) {
    history.back();
  } else {
    goto(fallbackPath);
  }
}
