// Downloaded catalog service — the "Downloaded" browse surface's data source.
//
// This is the offline library, filtered to what's on the device. It reads the
// dedicated offline-only browse path on the repository (never the hybrid merge),
// so results are authoritative regardless of connectivity: an empty result means
// "nothing downloaded here", never "server unreachable". See the spec
// docs/specs/downloads-as-offline-library.md and ux-flows §7.2–7.3.
//
// It also owns disk-usage: a per-item/container byte map plus the device total,
// aggregated from `downloads.file_size` by the backend.
//
// TRACES: UR-055, UR-056 | DR-082, DR-083, DR-085

import { writable, derived, get } from "svelte/store";
import type { Library, MediaItem, GetItemsOptions } from "$lib/api/types";
import type { DownloadDiskUsage } from "$lib/api/bindings";
import { commands } from "$lib/api/bindings";
import { auth } from "$lib/stores/auth";

interface DownloadedCatalogState {
  libraries: Library[];
  /** item id → bytes on disk (leaf's own size, or a container subtotal). */
  sizes: Record<string, number>;
  /** container id → true when only partially downloaded. */
  partialContainers: Record<string, boolean>;
  deviceTotalBytes: number;
  itemCount: number;
  loading: boolean;
  error: string | null;
}

const initial: DownloadedCatalogState = {
  libraries: [],
  sizes: {},
  partialContainers: {},
  deviceTotalBytes: 0,
  itemCount: 0,
  loading: false,
  error: null,
};

function createDownloadedCatalogStore() {
  const { subscribe, update, set } = writable<DownloadedCatalogState>(initial);

  function repo() {
    return auth.getRepository();
  }

  /** Load the downloaded-library list and disk-usage totals for the top bar. */
  async function refresh(): Promise<void> {
    update((s) => ({ ...s, loading: true, error: null }));
    try {
      const [libraries, usage] = await Promise.all([
        repo().getDownloadedLibraries(),
        repo().getDownloadDiskUsage() as Promise<DownloadDiskUsage>,
      ]);
      // The wire type is Partial<{ [k]: number }>; normalise to a dense record.
      const sizes: Record<string, number> = {};
      for (const [k, v] of Object.entries(usage.sizes)) {
        if (typeof v === "number") sizes[k] = v;
      }
      const partialContainers: Record<string, boolean> = {};
      for (const [k, v] of Object.entries(usage.partialContainers)) {
        if (v) partialContainers[k] = true;
      }
      update((s) => ({
        ...s,
        libraries,
        sizes,
        partialContainers,
        deviceTotalBytes: usage.deviceTotalBytes,
        itemCount: usage.itemCount,
        loading: false,
      }));
    } catch (error) {
      const message = error instanceof Error ? error.message : "Failed to load downloads";
      update((s) => ({ ...s, loading: false, error: message }));
    }
  }

  /** Downloaded-only items under a container (library, album, season, series). */
  async function loadItems(parentId: string, options?: GetItemsOptions): Promise<MediaItem[]> {
    const result = await repo().getDownloadedItems(parentId, options);
    return result.items;
  }

  /** Bytes on disk for an item id (leaf's own, or a container subtotal), or 0. */
  function sizeOf(itemId: string): number {
    return get({ subscribe }).sizes[itemId] ?? 0;
  }

  /**
   * Remove every completed download at or under a container/leaf, then refresh
   * so the browse and totals update. Returns the number of downloads removed.
   * TRACES: UR-055, UR-056 | DR-083
   */
  async function remove(itemId: string): Promise<number> {
    const userId = auth.getUserId();
    if (!userId) throw new Error("Not signed in");
    const removed = await commands.deleteDownloadsUnder(itemId, userId);
    await refresh();
    return removed;
  }

  function reset() {
    set(initial);
  }

  return { subscribe, refresh, loadItems, sizeOf, remove, reset };
}

export const downloadedCatalog = createDownloadedCatalogStore();

export const downloadedLibraries = derived(downloadedCatalog, ($c) => $c.libraries);
export const downloadedDeviceTotal = derived(downloadedCatalog, ($c) => $c.deviceTotalBytes);
export const downloadedItemCount = derived(downloadedCatalog, ($c) => $c.itemCount);
