import { describe, it, expect, vi, beforeEach } from "vitest";
import { render, screen, fireEvent } from "@testing-library/svelte";

// Controllable stores for the offline "server only" branch. Declared via
// vi.hoisted so they exist when the hoisted vi.mock factories run. A tiny
// writable shim avoids importing svelte inside the hoisted block.
const h = vi.hoisted(() => {
  function shim<T>(initial: T) {
    let value = initial;
    const subs = new Set<(v: T) => void>();
    return {
      set(v: T) {
        value = v;
        subs.forEach((fn) => fn(value));
      },
      subscribe(fn: (v: T) => void) {
        subs.add(fn);
        fn(value);
        return () => subs.delete(fn);
      },
    };
  }
  return {
    isConnectedStore: shim(true),
    showServerCatalogStore: shim(false),
    downloadsStore: shim({ downloads: {} as Record<string, any> }),
    downloadItem: vi.fn(async () => 1),
    getUserId: vi.fn(() => "user-1"),
  };
});

const { isConnectedStore, showServerCatalogStore, downloadsStore, downloadItem, getUserId } = h;

vi.mock("$lib/stores/connectivity", () => ({
  isConnected: { subscribe: h.isConnectedStore.subscribe },
}));

vi.mock("$lib/services/offlineCatalog", () => ({
  showServerCatalog: { subscribe: h.showServerCatalogStore.subscribe },
}));

vi.mock("$lib/stores/downloads", () => ({
  downloads: { subscribe: h.downloadsStore.subscribe, downloadItem: h.downloadItem },
}));

vi.mock("$lib/stores/auth", () => ({
  auth: { getUserId: h.getUserId },
}));

// CachedImage does async repo/image work irrelevant to these tests.
vi.mock("$lib/components/common/CachedImage.svelte", async () => ({
  default: (await import("./__mocks__/StubImage.svelte")).default,
}));

import MediaCard from "./MediaCard.svelte";

const track = {
  id: "track-1",
  name: "Some Song",
  type: "Audio" as const,
  serverId: "server-1",
  artists: ["Artist A"],
  albumName: "Album X",
};

describe("MediaCard server-only (offline browse & queue)", () => {
  beforeEach(() => {
    vi.clearAllMocks();
    isConnectedStore.set(true);
    showServerCatalogStore.set(false);
    downloadsStore.set({ downloads: {} });
  });

  it("shows no queue button while online", () => {
    render(MediaCard, { props: { item: track } });
    expect(screen.queryByLabelText(/Queue download/i)).toBeNull();
  });

  it("shows no queue button when offline but the reveal toggle is off", () => {
    isConnectedStore.set(false);
    render(MediaCard, { props: { item: track } });
    expect(screen.queryByLabelText(/Queue download/i)).toBeNull();
  });

  it("reveals a queue button when offline and the reveal toggle is on", () => {
    isConnectedStore.set(false);
    showServerCatalogStore.set(true);
    render(MediaCard, { props: { item: track } });
    expect(screen.getByLabelText(/Queue download for Some Song/i)).toBeTruthy();
  });

  it("queues the item for download with its metadata on click", async () => {
    isConnectedStore.set(false);
    showServerCatalogStore.set(true);
    render(MediaCard, { props: { item: track } });

    await fireEvent.click(screen.getByLabelText(/Queue download for Some Song/i));

    expect(downloadItem).toHaveBeenCalledTimes(1);
    const args = downloadItem.mock.calls[0] as unknown as any[];
    expect(args[0]).toBe("track-1"); // itemId
    expect(args[1]).toBe("user-1"); // userId
    expect(args[5]).toBe("Some Song"); // itemName
    expect(args[6]).toBe("Artist A"); // artistName
    expect(args[7]).toBe("Album X"); // albumName
  });

  it("shows a Queued badge (not the button) for a pending download", () => {
    isConnectedStore.set(false);
    showServerCatalogStore.set(true);
    downloadsStore.set({
      downloads: { "track-1": { itemId: "track-1", status: "pending", progress: 0 } },
    });
    render(MediaCard, { props: { item: track } });

    expect(screen.getByText(/Queued/i)).toBeTruthy();
    expect(screen.queryByLabelText(/Queue download/i)).toBeNull();
  });

  it("does not grey out a completed download", () => {
    isConnectedStore.set(false);
    showServerCatalogStore.set(true);
    downloadsStore.set({
      downloads: { "track-1": { itemId: "track-1", status: "completed", progress: 1 } },
    });
    render(MediaCard, { props: { item: track } });

    expect(screen.queryByLabelText(/Queue download/i)).toBeNull();
  });
});
