import { beforeEach, describe, expect, it, vi } from "vitest";
import type { EFMedia } from "../../EFMedia";
import { AssetIdMediaEngine } from "../AssetIdMediaEngine";
import { AssetMediaEngine } from "../AssetMediaEngine";
import { JitMediaEngine } from "../JitMediaEngine";
import { createMediaEngine } from "./makeMediaEngineTask";

// Mock the engine classes
vi.mock("../AssetIdMediaEngine", () => ({
  AssetIdMediaEngine: {
    fetchByAssetId: vi.fn(),
  },
}));
vi.mock("../AssetMediaEngine");
vi.mock("../JitMediaEngine");

describe("makeMediaEngineTask", () => {
  const mockUrlGenerator = {
    generateManifestUrl: vi.fn(),
  };

  const createMockHost = (overrides: Partial<EFMedia> = {}): EFMedia =>
    ({
      src: "",
      assetId: null,
      apiHost: "https://api.example.com",
      urlGenerator: mockUrlGenerator,
      ...overrides,
    }) as EFMedia;

  beforeEach(() => {
    vi.clearAllMocks();
  });

  describe("createMediaEngine", () => {
    it("should use AssetIdMediaEngine when assetId is provided", async () => {
      const mockEngine = { durationMs: 15000 };
      vi.mocked(AssetIdMediaEngine.fetchByAssetId).mockResolvedValue(
        mockEngine as any,
      );

      const host = createMockHost({
        assetId: "test-asset-123",
        apiHost: "https://api.example.com",
      });

      const result = await createMediaEngine(host);

      expect(AssetIdMediaEngine.fetchByAssetId).toHaveBeenCalledWith(
        host,
        mockUrlGenerator,
        "test-asset-123",
        "https://api.example.com",
      );
      expect(result).toBe(mockEngine);
    });

    it("should throw error when assetId is provided but apiHost is missing", async () => {
      const host = createMockHost({
        assetId: "test-asset-123",
        apiHost: undefined,
      });

      await expect(createMediaEngine(host)).rejects.toThrow(
        "API host is required for AssetID mode",
      );
    });

    it("should ignore empty assetId and use src instead", async () => {
      const mockEngine = { durationMs: 15000 };
      vi.mocked(AssetMediaEngine.fetch).mockResolvedValue(mockEngine as any);

      const host = createMockHost({
        assetId: "",
        src: "/path/to/asset",
      });

      const result = await createMediaEngine(host);

      expect(AssetMediaEngine.fetch).toHaveBeenCalledWith(
        host,
        mockUrlGenerator,
        "/path/to/asset",
      );
      expect(AssetIdMediaEngine.fetchByAssetId).not.toHaveBeenCalled();
      expect(result).toBe(mockEngine);
    });

    it("should ignore whitespace-only assetId and use src instead", async () => {
      const mockEngine = { durationMs: 15000 };
      vi.mocked(AssetMediaEngine.fetch).mockResolvedValue(mockEngine as any);

      const host = createMockHost({
        assetId: "   ",
        src: "/path/to/asset",
      });

      const result = await createMediaEngine(host);

      expect(AssetMediaEngine.fetch).toHaveBeenCalledWith(
        host,
        mockUrlGenerator,
        "/path/to/asset",
      );
      expect(AssetIdMediaEngine.fetchByAssetId).not.toHaveBeenCalled();
      expect(result).toBe(mockEngine);
    });

    it("should use JitMediaEngine for HTTP URLs", async () => {
      const mockEngine = { durationMs: 15000 };
      const manifestUrl = "https://api.example.com/manifest.json";

      vi.mocked(mockUrlGenerator.generateManifestUrl).mockReturnValue(
        manifestUrl,
      );
      vi.mocked(JitMediaEngine.fetch).mockResolvedValue(mockEngine as any);

      const host = createMockHost({
        src: "https://example.com/video.mp4",
      });

      const result = await createMediaEngine(host);

      expect(mockUrlGenerator.generateManifestUrl).toHaveBeenCalledWith(
        "https://example.com/video.mp4",
      );
      expect(JitMediaEngine.fetch).toHaveBeenCalledWith(
        host,
        mockUrlGenerator,
        manifestUrl,
      );
      expect(result).toBe(mockEngine);
    });

    it("should use JitMediaEngine for HTTPS URLs", async () => {
      const mockEngine = { durationMs: 15000 };
      const manifestUrl = "https://api.example.com/manifest.json";

      vi.mocked(mockUrlGenerator.generateManifestUrl).mockReturnValue(
        manifestUrl,
      );
      vi.mocked(JitMediaEngine.fetch).mockResolvedValue(mockEngine as any);

      const host = createMockHost({
        src: "https://example.com/video.mp4",
      });

      const result = await createMediaEngine(host);

      expect(JitMediaEngine.fetch).toHaveBeenCalledWith(
        host,
        mockUrlGenerator,
        manifestUrl,
      );
      expect(result).toBe(mockEngine);
    });

    it("should use AssetMediaEngine for local paths", async () => {
      const mockEngine = { durationMs: 15000 };
      vi.mocked(AssetMediaEngine.fetch).mockResolvedValue(mockEngine as any);

      const host = createMockHost({
        src: "/local/asset.mp4",
      });

      const result = await createMediaEngine(host);

      expect(AssetMediaEngine.fetch).toHaveBeenCalledWith(
        host,
        mockUrlGenerator,
        "/local/asset.mp4",
      );
      expect(result).toBe(mockEngine);
    });

    it("should throw error for empty src when no assetId", async () => {
      const host = createMockHost({
        src: "",
        assetId: null,
      });

      await expect(createMediaEngine(host)).rejects.toThrow(
        "Unsupported media source",
      );
    });

    it("should throw error for whitespace-only src when no assetId", async () => {
      const host = createMockHost({
        src: "   ",
        assetId: null,
      });

      await expect(createMediaEngine(host)).rejects.toThrow(
        "Unsupported media source",
      );
    });

    it("should throw error for null src when no assetId", async () => {
      const host = createMockHost({
        src: null as any,
        assetId: null,
      });

      await expect(createMediaEngine(host)).rejects.toThrow(
        "Unsupported media source",
      );
    });

    it("should prioritize assetId over src when both are provided", async () => {
      const mockEngine = { durationMs: 15000 };
      vi.mocked(AssetIdMediaEngine.fetchByAssetId).mockResolvedValue(
        mockEngine as any,
      );

      const host = createMockHost({
        assetId: "test-asset-123",
        src: "/local/asset.mp4",
        apiHost: "https://api.example.com",
      });

      const result = await createMediaEngine(host);

      expect(AssetIdMediaEngine.fetchByAssetId).toHaveBeenCalledWith(
        host,
        mockUrlGenerator,
        "test-asset-123",
        "https://api.example.com",
      );
      expect(AssetMediaEngine.fetch).not.toHaveBeenCalled();
      expect(result).toBe(mockEngine);
    });
  });
});
