// TRACES: UR-062 | DR-102, DR-103, DR-142 | UT-136
import { describe, it, expect } from "vitest";
import type { MediaItem } from "$lib/api/types";
import {
  seasonAnchorId,
  seasonRedirectTarget,
  episodeFocusHref,
  episodeRedirectTarget,
  seriesPlayHref,
  seriesPlayLabel,
  groupEpisodesBySeason,
  initialExpandedSeasons,
} from "./seriesNavigation";

const SERIES = "series-1";

function ep(id: string, season: number | null, number: number | null): MediaItem {
  return {
    id,
    name: `S${season}E${number}`,
    kind: "episode",
    seriesId: SERIES,
    parentIndexNumber: season,
    indexNumber: number,
    durationMs: 1_000_000,
  } as unknown as MediaItem;
}

function seasonHeader(number: number, id = `season-${number}`): MediaItem {
  return {
    id,
    name: `Season ${number}`,
    kind: "season",
    seriesId: SERIES,
    indexNumber: number,
  } as unknown as MediaItem;
}

function withProgress(episode: MediaItem, fraction: number): MediaItem {
  return {
    ...episode,
    userData: { playbackPositionMs: (episode.durationMs ?? 0) * fraction },
  } as MediaItem;
}

describe("seriesPlayHref", () => {
  // The reported bug: Play resolved the first *season* child and navigated to
  // /player/<seasonId>, which bounced back to the season-1 page.
  it("opens the current episode's focus view, never a season or the player", () => {
    const href = seriesPlayHref(SERIES, ep("s2e4", 2, 4));
    expect(href).toBe("/library/series-1?episode=s2e4");
    expect(href).not.toContain("/player/");
  });

  it("returns null for a series with no episodes so the button can hide", () => {
    expect(seriesPlayHref(SERIES, null)).toBeNull();
  });
});

describe("seriesPlayLabel", () => {
  it("names the episode it will open", () => {
    expect(seriesPlayLabel(ep("s2e4", 2, 4))).toBe("Play S2E4");
  });

  it("says Resume for a part-watched episode", () => {
    expect(seriesPlayLabel(withProgress(ep("s2e4", 2, 4), 0.4))).toBe("Resume S2E4");
  });

  it("says Play for a barely-started or nearly-finished episode", () => {
    expect(seriesPlayLabel(withProgress(ep("s1e1", 1, 1), 0.001))).toBe("Play S1E1");
    expect(seriesPlayLabel(withProgress(ep("s1e1", 1, 1), 0.99))).toBe("Play S1E1");
  });

  it("degrades to a bare verb when the numbering is unknown", () => {
    expect(seriesPlayLabel(ep("x", null, null))).toBe("Play");
    expect(seriesPlayLabel(null)).toBe("Play");
  });
});

describe("seasonRedirectTarget", () => {
  it("sends a season to its series, anchored at that season", () => {
    expect(seasonRedirectTarget(seasonHeader(3))).toBe("/library/series-1#season-3");
  });

  it("returns null when the series is unknown, so the caller can fall back", () => {
    const orphan = { ...seasonHeader(3), seriesId: undefined } as MediaItem;
    expect(seasonRedirectTarget(orphan)).toBeNull();
  });

  it("matches the anchor the season section renders", () => {
    expect(seasonRedirectTarget(seasonHeader(2))).toBe(
      `/library/${SERIES}#${seasonAnchorId(2)}`
    );
  });
});

describe("episodeFocusHref", () => {
  it("opens an episode inside its series (never a bare episode page)", () => {
    expect(episodeFocusHref(ep("s1e2", 1, 2))).toBe("/library/series-1?episode=s1e2");
  });

  it("falls back to the bare item page when the series is unknown", () => {
    const orphan = { ...ep("lone", 1, 2), seriesId: undefined } as MediaItem;
    expect(episodeFocusHref(orphan)).toBe("/library/lone");
  });
});

describe("episodeRedirectTarget", () => {
  it("sends a bare episode page to the episode inside its series", () => {
    expect(episodeRedirectTarget(ep("s1e2", 1, 2))).toBe("/library/series-1?episode=s1e2");
  });

  it("does not redirect an episode that has no series to fall back on", () => {
    const orphan = { ...ep("lone", 1, 2), seriesId: undefined } as MediaItem;
    expect(episodeRedirectTarget(orphan)).toBeNull();
  });
});

describe("groupEpisodesBySeason", () => {
  it("groups episodes under their season headers, in season order", () => {
    const seasons = [seasonHeader(2), seasonHeader(1)];
    const episodes = [ep("s1e1", 1, 1), ep("s1e2", 1, 2), ep("s2e1", 2, 1)];

    const grouped = groupEpisodesBySeason(seasons, episodes);
    expect(grouped.map((g) => g.season.indexNumber)).toEqual([1, 2]);
    expect(grouped[0].episodes.map((e) => e.id)).toEqual(["s1e1", "s1e2"]);
    expect(grouped[1].episodes.map((e) => e.id)).toEqual(["s2e1"]);
  });

  it("puts specials after the numbered seasons", () => {
    const grouped = groupEpisodesBySeason(
      [seasonHeader(0), seasonHeader(1)],
      [ep("s0e1", 0, 1), ep("s1e1", 1, 1)]
    );
    expect(grouped.map((g) => g.season.indexNumber)).toEqual([1, 0]);
  });

  // A flat series: episodes hang off the series, no season folders exist.
  it("synthesizes headers when the server returned no seasons", () => {
    const grouped = groupEpisodesBySeason([], [ep("s1e1", 1, 1), ep("s2e1", 2, 1)]);
    expect(grouped.map((g) => g.season.name)).toEqual(["Season 1", "Season 2"]);
    expect(grouped.every((g) => g.season.kind === "season")).toBe(true);
  });

  it("names a synthesized season 0 'Specials'", () => {
    const grouped = groupEpisodesBySeason([], [ep("s0e1", 0, 1)]);
    expect(grouped[0].season.name).toBe("Specials");
  });

  it("gives synthesized headers distinct ids so keyed #each blocks are stable", () => {
    const grouped = groupEpisodesBySeason([], [ep("s1e1", 1, 1), ep("s2e1", 2, 1)]);
    const ids = grouped.map((g) => g.season.id);
    expect(new Set(ids).size).toBe(ids.length);
  });

  it("drops seasons that have no episodes", () => {
    const grouped = groupEpisodesBySeason(
      [seasonHeader(1), seasonHeader(2), seasonHeader(3)],
      [ep("s2e1", 2, 1)]
    );
    expect(grouped.map((g) => g.season.indexNumber)).toEqual([2]);
  });

  it("buckets season-less episodes into season 1 rather than losing them", () => {
    const grouped = groupEpisodesBySeason([], [ep("lone", null, 1)]);
    expect(grouped).toHaveLength(1);
    expect(grouped[0].episodes.map((e) => e.id)).toEqual(["lone"]);
  });
});

describe("initialExpandedSeasons", () => {
  const seasons = groupEpisodesBySeason(
    [seasonHeader(1), seasonHeader(2), seasonHeader(3)],
    [
      ep("s1e1", 1, 1),
      ep("s2e1", 2, 1),
      ep("s2e2", 2, 2),
      ep("s3e1", 3, 1),
    ]
  );

  it("expands only the season holding the current episode", () => {
    const expanded = initialExpandedSeasons(seasons, "s2e2");
    expect([...expanded]).toEqual(["season-2"]);
  });

  it("also expands the season of a ?episode= deep link", () => {
    const expanded = initialExpandedSeasons(seasons, "s1e1", "s3e1");
    expect(expanded.has("season-1")).toBe(true);
    expect(expanded.has("season-3")).toBe(true);
    expect(expanded.has("season-2")).toBe(false);
  });

  it("collapses nothing extra when current and focused share a season", () => {
    const expanded = initialExpandedSeasons(seasons, "s2e1", "s2e2");
    expect([...expanded]).toEqual(["season-2"]);
  });

  it("falls back to the first season when there is no current episode", () => {
    expect([...initialExpandedSeasons(seasons, null)]).toEqual(["season-1"]);
  });

  it("falls back to the first season when the current episode is unknown here", () => {
    expect([...initialExpandedSeasons(seasons, "not-in-this-show")]).toEqual(["season-1"]);
  });

  it("returns nothing for a series with no seasons", () => {
    expect(initialExpandedSeasons([], "s1e1").size).toBe(0);
  });
});
