// TRACES: UR-067, UR-068 | DR-117, DR-119 | UT-105, UT-106

import { describe, it, expect, beforeEach } from "vitest";
import { get } from "svelte/store";
import type { MediaItem } from "$lib/api/types";
import {
  favoriteOverrides,
  setFavorite,
  clearFavorite,
  clearAllFavorites,
  resolveIsFavorite,
  isFavoriteNow,
  retainFavorites,
} from "./favorites";

function item(id: string, isFavorite?: boolean): MediaItem {
  return {
    id,
    name: `Item ${id}`,
    type: "Movie",
    kind: "movie",
    isFolder: false,
    serverId: "s1",
    userData: isFavorite === undefined ? undefined : { isFavorite },
  } as unknown as MediaItem;
}

describe("favorites store", () => {
  beforeEach(() => clearAllFavorites());

  describe("resolveIsFavorite (UT-105)", () => {
    it("falls back to the server's userData when nothing was toggled here", () => {
      expect(resolveIsFavorite(item("a", true), new Map())).toBe(true);
      expect(resolveIsFavorite(item("a", false), new Map())).toBe(false);
    });

    it("treats an item with no userData as not favourited", () => {
      expect(resolveIsFavorite(item("a"), new Map())).toBe(false);
    });

    it("lets a session override win over userData", () => {
      // The whole point: after tapping the heart on a card, the item object
      // still carries the server's stale value until the next fetch.
      expect(resolveIsFavorite(item("a", false), new Map([["a", true]]))).toBe(true);
      expect(resolveIsFavorite(item("a", true), new Map([["a", false]]))).toBe(false);
    });

    it("is false for a missing item rather than throwing", () => {
      expect(resolveIsFavorite(null, new Map())).toBe(false);
      expect(resolveIsFavorite(undefined, new Map())).toBe(false);
    });
  });

  describe("overrides", () => {
    it("publishes a toggle to subscribers", () => {
      setFavorite("a", true);
      expect(get(favoriteOverrides).get("a")).toBe(true);
      expect(isFavoriteNow(item("a", false))).toBe(true);

      setFavorite("a", false);
      expect(isFavoriteNow(item("a", true))).toBe(false);
    });

    it("clearing an override hands authority back to the item's userData", () => {
      setFavorite("a", false);
      expect(isFavoriteNow(item("a", true))).toBe(false);

      clearFavorite("a");
      expect(isFavoriteNow(item("a", true))).toBe(true);
    });

    it("replaces the map so Svelte sees a new reference", () => {
      const before = get(favoriteOverrides);
      setFavorite("a", true);
      expect(get(favoriteOverrides)).not.toBe(before);
    });
  });

  describe("retainFavorites (UT-106)", () => {
    it("drops an item un-favourited during this session", () => {
      const items = [item("a", true), item("b", true)];
      const kept = retainFavorites(items, new Map([["a", false]]));
      expect(kept.map((i) => i.id)).toEqual(["b"]);
    });

    it("keeps everything when nothing was toggled", () => {
      const items = [item("a", true), item("b", true)];
      expect(retainFavorites(items, new Map())).toHaveLength(2);
    });

    it("keeps an item favourited during this session even if the server said otherwise", () => {
      const items = [item("a", false)];
      expect(retainFavorites(items, new Map([["a", true]]))).toHaveLength(1);
    });

    it("drops items the server never marked as favourites", () => {
      // A listing fetched with a stale scope should not keep non-favourites.
      expect(retainFavorites([item("a")], new Map())).toHaveLength(0);
    });
  });
});
