import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { TRPCError } from "@trpc/server";
import { injectable } from "inversify";
import { {{camelCase name}}Container } from "@/di/container";
import { {{pascalCase name}}Module } from "@/di/module";
import { {{constantCase name}}_SYMBOLS } from "@/di/symbols";
import { get{{pascalCase entity}}UseCase } from "@/application/use-cases/get-{{kebabCase entity}}.use-case";
import { get{{pascalCase entity}}Controller } from "@/interface-adapters/controllers/get-{{kebabCase entity}}.controller";
import { {{camelCase name}}Router } from "@/integrations/api/router";

describe("{{camelCase name}}Router", () => {
  beforeEach(() => {
    {{camelCase name}}Container.unbindAll();
    {{camelCase name}}Container.load({{pascalCase name}}Module);
  });

  afterEach(() => {
    {{camelCase name}}Container.unbindAll();
  });

  it("exposes the get{{pascalCase entity}} procedure", () => {
    const names = Object.keys({{camelCase name}}Router._def.procedures);
    expect(names).toContain("get{{pascalCase entity}}");
  });

  it("get{{pascalCase entity}} returns the seeded {{camelCase entity}}", async () => {
    const caller = {{camelCase name}}Router.createCaller({});
    const result = await caller.get{{pascalCase entity}}({ id: "seed-1" });
    expect(result.id).toBe("seed-1");
  });
});

describe("{{camelCase name}}Router (error mapping)", () => {
  beforeEach(() => {
    {{camelCase name}}Container.unbindAll();
    {{camelCase name}}Container.load({{pascalCase name}}Module);
  });

  afterEach(() => {
    {{camelCase name}}Container.unbindAll();
  });

  it("translates InputParseError → BAD_REQUEST when extra fields are passed", async () => {
    const caller = {{camelCase name}}Router.createCaller({});
    try {
      await caller.get{{pascalCase entity}}({
        id: "seed-1",
        unexpected: "field",
      } as unknown as { id: string });
      throw new Error("expected throw");
    } catch (e) {
      expect(e).toBeInstanceOf(TRPCError);
      expect((e as TRPCError).code).toBe("BAD_REQUEST");
    }
  });

  it("translates {{pascalCase entity}}NotFoundError → NOT_FOUND when repository returns null", async () => {
    @injectable()
    class Null{{pascalCase entity}}Repository {
      async get{{pascalCase entity}}() {
        return null;
      }
    }

    {{camelCase name}}Container.unbindAll();
    {{camelCase name}}Container
      .bind({{constantCase name}}_SYMBOLS.I{{pascalCase entity}}Repository)
      .to(Null{{pascalCase entity}}Repository);
    {{camelCase name}}Container
      .bind({{constantCase name}}_SYMBOLS.IGet{{pascalCase entity}}UseCase)
      .toDynamicValue((ctx) =>
        get{{pascalCase entity}}UseCase(
          ctx.container.get({{constantCase name}}_SYMBOLS.I{{pascalCase entity}}Repository),
        ),
      );
    {{camelCase name}}Container
      .bind({{constantCase name}}_SYMBOLS.IGet{{pascalCase entity}}Controller)
      .toDynamicValue((ctx) =>
        get{{pascalCase entity}}Controller(
          ctx.container.get({{constantCase name}}_SYMBOLS.IGet{{pascalCase entity}}UseCase),
        ),
      );

    const caller = {{camelCase name}}Router.createCaller({});
    try {
      await caller.get{{pascalCase entity}}({ id: "anything" });
      throw new Error("expected throw");
    } catch (e) {
      expect(e).toBeInstanceOf(TRPCError);
      expect((e as TRPCError).code).toBe("NOT_FOUND");
    }
  });
});
