import { describe, expect, it, vi } from "vitest";
import { RecordingTracer } from "@repo/core-testing/instrumentation";
import { MediaRepository } from "@/infrastructure/repositories/media.repository";
import { mediaRepositoryContract } from "@/__contracts__/media-repository.contract";
import { stubPayloadConfig } from "@repo/core-testing/payload/stub-config";

// ---------------------------------------------------------------------------
// In-memory Payload stub
// ---------------------------------------------------------------------------

function buildPayloadStub() {
  const store = new Map<string, Record<string, unknown>>();

  return {
    find: vi.fn(
      async ({
        limit,
        page,
      }: {
        collection: string;
        limit?: number;
        page?: number;
        overrideAccess?: boolean;
      }) => {
        let docs = Array.from(store.values());
        const lim = limit ?? 50;
        const pg = page ?? 1;
        const offset = (pg - 1) * lim;
        docs = docs.slice(offset, offset + lim);
        return { docs };
      },
    ),
    findByID: vi.fn(
      async ({ id }: { collection: string; id: string; overrideAccess?: boolean }) => {
        const doc = store.get(String(id));
        if (!doc) {
          const err = Object.assign(new Error(`Not found: ${id}`), { status: 404 });
          throw err;
        }
        return doc;
      },
    ),
    delete: vi.fn(
      async ({ id }: { collection: string; id: string; overrideAccess?: boolean }) => {
        store.delete(String(id));
        return { id };
      },
    ),
  };
}

vi.mock("payload", () => ({
  getPayload: vi.fn(),
}));

// ---------------------------------------------------------------------------
// Contract suite
// ---------------------------------------------------------------------------

describe("MediaRepository", () => {
  describe("contract", () => {
    const tracer = new RecordingTracer();
    mediaRepositoryContract.run(
      async () => {
        const stub = buildPayloadStub();
        const { getPayload } = await import("payload");
        (getPayload as ReturnType<typeof vi.fn>).mockResolvedValue(stub);
        return new MediaRepository(stubPayloadConfig, tracer);
      },
      { tracer: () => tracer },
    );
  });

  // -------------------------------------------------------------------------
  // Impl-specific tests: Payload doc → domain mapping
  // -------------------------------------------------------------------------

  describe("getMedia", () => {
    it("returns undefined when Payload throws (not found)", async () => {
      const { getPayload } = await import("payload");
      (getPayload as ReturnType<typeof vi.fn>).mockResolvedValue({
        findByID: vi.fn().mockRejectedValue(
          Object.assign(new Error("Not found"), { status: 404 }),
        ),
      });

      const repo = new MediaRepository(stubPayloadConfig);
      const result = await repo.getMedia("missing-id");
      expect(result).toBeUndefined();
    });

    it("maps Payload doc fields to domain Media", async () => {
      const { getPayload } = await import("payload");
      (getPayload as ReturnType<typeof vi.fn>).mockResolvedValue({
        findByID: vi.fn().mockResolvedValue({
          id: "p-1",
          alt: "Test image",
          url: "https://cdn.example.com/test.png",
          filename: "test.png",
          mimeType: "image/png",
          filesize: 2048,
          width: 800,
          height: 600,
        }),
      });

      const repo = new MediaRepository(stubPayloadConfig);
      const result = await repo.getMedia("p-1");
      expect(result?.id).toBe("p-1");
      expect(result?.alt).toBe("Test image");
      expect(result?.width).toBe(800);
      expect(result?.height).toBe(600);
    });
  });

  describe("listMedia", () => {
    it("returns an array of mapped Media docs", async () => {
      const { getPayload } = await import("payload");
      (getPayload as ReturnType<typeof vi.fn>).mockResolvedValue({
        find: vi.fn().mockResolvedValue({
          docs: [
            {
              id: "m-1",
              alt: "First",
              url: "https://cdn.example.com/first.png",
              filename: "first.png",
              mimeType: "image/png",
              filesize: 100,
            },
          ],
        }),
      });

      const repo = new MediaRepository(stubPayloadConfig);
      const result = await repo.listMedia();
      expect(result).toHaveLength(1);
      expect(result[0]?.id).toBe("m-1");
    });
  });
});
