import { describe, it, expect, beforeEach } from "vitest";
import { TRPCError } from "@trpc/server";

import { authRouter } from "@/integrations/api/router";
import { authContainer } from "@/di/container";
import { AUTH_SYMBOLS } from "@/di/symbols";
import { MockUsersRepository } from "@/infrastructure/repositories/users.repository.mock";
import { MockAuthenticationService } from "@/infrastructure/services/authentication.service.mock";
import type { IUsersRepository } from "@/application/repositories/users.repository.interface";
import type { IAuthenticationService } from "@/application/services/authentication.service.interface";

describe("authRouter", () => {
  it("exposes signIn, signUp, signOut procedures", () => {
    const names = Object.keys(authRouter._def.procedures);
    expect(names).toContain("signIn");
    expect(names).toContain("signUp");
    expect(names).toContain("signOut");
  });

  it("signIn returns a cookie via container-resolved controller", async () => {
    // The router resolves controllers via the authContainer (default mock bindings).
    // MockUsersRepository is seeded with alice/password_alice by default.
    const caller = authRouter.createCaller({});
    const result = await caller.signIn({
      username: "alice",
      password: "password_alice",
    });
    expect(result.name).toBe("session");
  });
});

describe("authRouter (R26 error mapping)", () => {
  beforeEach(() => {
    if (authContainer.isBound(AUTH_SYMBOLS.IUsersRepository)) {
      authContainer.unbind(AUTH_SYMBOLS.IUsersRepository);
    }
    if (authContainer.isBound(AUTH_SYMBOLS.IAuthenticationService)) {
      authContainer.unbind(AUTH_SYMBOLS.IAuthenticationService);
    }
    const users = new MockUsersRepository();
    const auth = new MockAuthenticationService(users);
    authContainer.bind<IUsersRepository>(AUTH_SYMBOLS.IUsersRepository).toConstantValue(users);
    authContainer
      .bind<IAuthenticationService>(AUTH_SYMBOLS.IAuthenticationService)
      .toConstantValue(auth);
  });

  it("translates AuthenticationError → UNAUTHORIZED on missing user", async () => {
    const caller = authRouter.createCaller({});
    try {
      await caller.signIn({ username: "ghost", password: "long-enough" });
      throw new Error("expected throw");
    } catch (e) {
      expect(e).toBeInstanceOf(TRPCError);
      expect((e as TRPCError).code).toBe("UNAUTHORIZED");
    }
  });

  it("translates BAD_REQUEST when zod parse fails at the procedure boundary", async () => {
    const caller = authRouter.createCaller({});
    try {
      await caller.signIn({ username: "ab", password: "x" } as unknown as {
        username: string;
        password: string;
      });
      throw new Error("expected throw");
    } catch (e) {
      expect(e).toBeInstanceOf(TRPCError);
      expect((e as TRPCError).code).toBe("BAD_REQUEST");
    }
  });
});
