// Feature-level test: exercises the full slice
//   use case factory chain → mock repo
// Tests the full dependency chain via direct injection (no container rebinding).

import { describe, expect, it } from "vitest";
import { MockMediaRepository } from "../src/infrastructure/repositories/media.repository.mock";
import { getMediaUseCase } from "../src/application/use-cases/get-media.use-case";
import { listMediaUseCase } from "../src/application/use-cases/list-media.use-case";
import { deleteMediaUseCase } from "../src/application/use-cases/delete-media.use-case";
import { getMediaController } from "../src/interface-adapters/controllers/get-media.controller";
import { listMediaController } from "../src/interface-adapters/controllers/list-media.controller";
import { deleteMediaController } from "../src/interface-adapters/controllers/delete-media.controller";
import { MediaNotFoundError } from "../src/entities/errors/media";
import { mediaFactory } from "../src/__factories__/media.factory";

describe("media feature: end-to-end via direct injection", () => {
  function buildChain() {
    const repo = new MockMediaRepository();
    const getUC = getMediaUseCase(repo);
    const listUC = listMediaUseCase(repo);
    const deleteUC = deleteMediaUseCase(repo);
    const getCtrl = getMediaController(getUC);
    const listCtrl = listMediaController(listUC);
    const deleteCtrl = deleteMediaController(deleteUC);
    return { repo, getCtrl, listCtrl, deleteCtrl };
  }

  it("listMedia returns empty array initially", async () => {
    const { listCtrl } = buildChain();
    const result = await listCtrl({});
    expect(result).toHaveLength(0);
  });

  it("stores media and retrieves it by id", async () => {
    const { repo, getCtrl } = buildChain();
    const seed = mediaFactory.build({ id: "feat-1" });
    await repo._store(seed);

    const result = await getCtrl({ id: "feat-1" });
    expect(result.id).toBe("feat-1");
    expect(result.alt).toBe(seed.alt);
  });

  it("listMedia returns all stored media", async () => {
    const { repo, listCtrl } = buildChain();
    await repo._store(mediaFactory.build());
    await repo._store(mediaFactory.build());
    await repo._store(mediaFactory.build());

    const result = await listCtrl({});
    expect(result).toHaveLength(3);
  });

  it("deleteMedia removes media so it can no longer be retrieved", async () => {
    const { repo, getCtrl, deleteCtrl } = buildChain();
    const seed = mediaFactory.build({ id: "feat-del" });
    await repo._store(seed);

    await deleteCtrl({ id: "feat-del" });

    await expect(getCtrl({ id: "feat-del" })).rejects.toThrow(MediaNotFoundError);
  });

  it("getMediaController throws MediaNotFoundError for missing id", async () => {
    const { getCtrl } = buildChain();
    await expect(getCtrl({ id: "does-not-exist" })).rejects.toBeInstanceOf(MediaNotFoundError);
  });

  it("deleteMediaController throws MediaNotFoundError for missing id", async () => {
    const { deleteCtrl } = buildChain();
    await expect(deleteCtrl({ id: "does-not-exist" })).rejects.toBeInstanceOf(MediaNotFoundError);
  });

  it("listMedia with limit respects pagination", async () => {
    const { repo, listCtrl } = buildChain();
    for (let i = 0; i < 5; i++) {
      await repo._store(mediaFactory.build());
    }

    const result = await listCtrl({ limit: 3 });
    expect(result).toHaveLength(3);
  });
});
