/**
 * Image cache service tests
 *
 * TRACES: UR-007 | DR-016
 */

import { describe, it, expect, beforeEach, vi } from "vitest";
import {
  getCachedImageUrl,
  getCacheStats,
  setCacheLimit,
  clearCache,
  deleteItemCache,
  formatBytes,
  gbToBytes,
  bytesToGb,
} from "./imageCache";

vi.mock("@tauri-apps/api/core", () => ({
  invoke: vi.fn(async (command: string, args?: any) => {
    if (command === "thumbnail_get_cached") {
      return null; // No cached image
    }
    if (command === "thumbnail_get_stats") {
      return {
        totalSizeBytes: 1024 * 1024,
        itemCount: 10,
        limitBytes: 1024 * 1024 * 1024,
      };
    }
    if (command === "thumbnail_set_limit") {
      return undefined;
    }
    if (command === "thumbnail_clear_cache") {
      return undefined;
    }
    if (command === "thumbnail_delete_item") {
      return undefined;
    }
    if (command === "thumbnail_save") {
      return undefined;
    }
    return null;
  }),
  convertFileSrc: vi.fn((path: string) => `asset://path/${path}`),
}));

describe("image cache service", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  describe("getCachedImageUrl", () => {
    it("should build server URL with default image type", async () => {
      const url = await getCachedImageUrl(
        "http://server.local:8096",
        "item-123"
      );
      expect(url).toContain("http://server.local:8096/Items/item-123/Images/Primary");
    });

    it("should build server URL with custom image type", async () => {
      const url = await getCachedImageUrl(
        "http://server.local:8096",
        "item-123",
        "Backdrop"
      );
      expect(url).toContain("Backdrop");
    });

    it("should include image options in URL", async () => {
      const url = await getCachedImageUrl(
        "http://server.local:8096",
        "item-123",
        "Primary",
        {
          maxWidth: 300,
          maxHeight: 400,
          quality: 90,
          tag: "abc123",
        }
      );
      expect(url).toContain("maxWidth=300");
      expect(url).toContain("maxHeight=400");
      expect(url).toContain("quality=90");
      expect(url).toContain("tag=abc123");
    });

    it("should trigger background caching", async () => {
      const { invoke } = await import("@tauri-apps/api/core");
      const invokeSpy = vi.mocked(invoke);

      await getCachedImageUrl("http://server.local:8096", "item-123");

      const saveCall = invokeSpy.mock.calls.find(
        (call) => call[0] === "thumbnail_save"
      );
      expect(saveCall).toBeDefined();
      expect(saveCall![1]).toHaveProperty("itemId", "item-123");
      expect(saveCall![1]).toHaveProperty("imageType", "Primary");
    });
  });

  describe("cache statistics", () => {
    it("should get cache statistics", async () => {
      const stats = await getCacheStats();
      expect(stats).toHaveProperty("totalSizeBytes");
      expect(stats).toHaveProperty("itemCount");
      expect(stats).toHaveProperty("limitBytes");
      expect(typeof stats.totalSizeBytes).toBe("number");
      expect(typeof stats.itemCount).toBe("number");
    });

    it("should set cache limit", async () => {
      const limit = 1024 * 1024 * 1024 * 5; // 5GB
      await setCacheLimit(limit);

      const { invoke } = await import("@tauri-apps/api/core");
      const invokeSpy = vi.mocked(invoke);
      const setLimitCall = invokeSpy.mock.calls.find(
        (call) => call[0] === "thumbnail_set_limit"
      );
      expect(setLimitCall).toBeDefined();
      expect(setLimitCall![1]).toHaveProperty("limitBytes", limit);
    });
  });

  describe("cache clearing", () => {
    it("should clear all cached thumbnails", async () => {
      await clearCache();

      const { invoke } = await import("@tauri-apps/api/core");
      const invokeSpy = vi.mocked(invoke);
      expect(invokeSpy).toHaveBeenCalledWith("thumbnail_clear_cache");
    });

    it("should delete cache for specific item", async () => {
      await deleteItemCache("item-456");

      const { invoke } = await import("@tauri-apps/api/core");
      const invokeSpy = vi.mocked(invoke);
      const deleteCall = invokeSpy.mock.calls.find(
        (call) => call[0] === "thumbnail_delete_item"
      );
      expect(deleteCall).toBeDefined();
      expect(deleteCall![1]).toHaveProperty("itemId", "item-456");
    });
  });

  describe("byte formatting", () => {
    it("should format bytes", () => {
      expect(formatBytes(512)).toContain("B");
      expect(formatBytes(512)).not.toContain("KB");
    });

    it("should format kilobytes", () => {
      expect(formatBytes(1024)).toContain("KB");
    });

    it("should format megabytes", () => {
      expect(formatBytes(1024 * 1024)).toContain("MB");
    });

    it("should format gigabytes", () => {
      expect(formatBytes(1024 * 1024 * 1024)).toContain("GB");
    });

    it("should format with correct precision", () => {
      const result = formatBytes(1024 * 1.5);
      expect(result).toMatch(/\d+\.\d+ KB/);
    });
  });

  describe("unit conversion", () => {
    it("should convert gigabytes to bytes", () => {
      const bytes = gbToBytes(1);
      expect(bytes).toBe(1024 * 1024 * 1024);
    });

    it("should convert bytes to gigabytes", () => {
      const gb = bytesToGb(1024 * 1024 * 1024);
      expect(gb).toBe(1);
    });

    it("should handle fractional conversions", () => {
      const bytes = gbToBytes(0.5);
      expect(bytes).toBe(512 * 1024 * 1024);

      const gb = bytesToGb(512 * 1024 * 1024);
      expect(gb).toBe(0.5);
    });
  });
});
