import {
  withSpan,
  withCapture,
  INSTRUMENTATION_SYMBOLS,
  type ITracer,
  type ILogger,
} from "@repo/core-shared/instrumentation";
import type { BindProductionContext } from "@repo/core-shared/di";
import {
  assertFeatureConformance,
  wireUseCase,
} from "@repo/core-shared/conformance";
import { NoopRateLimit } from "@repo/core-shared/rate-limit";
import { authManifest } from "../feature.manifest";
import { authContainer } from "./container";
import { AUTH_SYMBOLS } from "./symbols";
import { UsersRepository } from "../infrastructure/repositories/users.repository";
import { AuthenticationService } from "../infrastructure/services/authentication.service";
import { signInUseCase } from "../application/use-cases/sign-in.use-case";
import { signUpUseCase } from "../application/use-cases/sign-up.use-case";
import { signOutUseCase } from "../application/use-cases/sign-out.use-case";
import { signInController } from "../interface-adapters/controllers/sign-in.controller";
import { signUpController } from "../interface-adapters/controllers/sign-up.controller";
import { signOutController } from "../interface-adapters/controllers/sign-out.controller";
import type { IUsersRepository } from "../application/repositories/users.repository.interface";
import type { IAuthenticationService } from "../application/services/authentication.service.interface";

let bound = false;

export function bindProductionAuth(ctx: BindProductionContext): void {
  if (bound) return;
  bound = true;

  const {
    config,
    tracer,
    logger,
    bus,
    queue,
    realtime,
    realtimeRegistry,
    consentFactory,
  } = ctx;

  // Bind shared instrumentation into feature container
  if (authContainer.isBound(INSTRUMENTATION_SYMBOLS.TRACER)) {
    authContainer.unbind(INSTRUMENTATION_SYMBOLS.TRACER);
  }
  if (authContainer.isBound(INSTRUMENTATION_SYMBOLS.LOGGER)) {
    authContainer.unbind(INSTRUMENTATION_SYMBOLS.LOGGER);
  }
  authContainer
    .bind<ITracer>(INSTRUMENTATION_SYMBOLS.TRACER)
    .toConstantValue(tracer);
  authContainer
    .bind<ILogger>(INSTRUMENTATION_SYMBOLS.LOGGER)
    .toConstantValue(logger);

  // Real repositories
  if (authContainer.isBound(AUTH_SYMBOLS.IUsersRepository)) {
    authContainer.unbind(AUTH_SYMBOLS.IUsersRepository);
  }
  const repo = new UsersRepository(config, tracer, logger);
  authContainer
    .bind<IUsersRepository>(AUTH_SYMBOLS.IUsersRepository)
    .toConstantValue(repo);

  if (authContainer.isBound(AUTH_SYMBOLS.IAuthenticationService)) {
    authContainer.unbind(AUTH_SYMBOLS.IAuthenticationService);
  }
  const authService = new AuthenticationService(config);
  authContainer
    .bind<IAuthenticationService>(AUTH_SYMBOLS.IAuthenticationService)
    .toConstantValue(authService);

  // Use cases
  const wrappedSignIn = wireUseCase({
    container: authContainer,
    symbol: AUTH_SYMBOLS.ISignInUseCase,
    factory: signInUseCase,
    deps: [repo, authService, ctx.rateLimit ?? new NoopRateLimit()],
    feature: "auth",
    layer: "use-case",
    name: "signIn",
    tracer,
    logger,
    rateLimit: ctx.rateLimit ?? new NoopRateLimit(),
  });
  const wrappedSignUp = wireUseCase({
    container: authContainer,
    symbol: AUTH_SYMBOLS.ISignUpUseCase,
    factory: signUpUseCase,
    deps: [repo, authService, bus, consentFactory],
    feature: "auth",
    layer: "use-case",
    name: "signUp",
    tracer,
    logger,
  });
  const wrappedSignOut = wireUseCase({
    container: authContainer,
    symbol: AUTH_SYMBOLS.ISignOutUseCase,
    factory: signOutUseCase,
    deps: [authService],
    feature: "auth",
    layer: "use-case",
    name: "signOut",
    tracer,
    logger,
  });

  // Controllers — wrapped with span at bind time
  for (const sym of [
    AUTH_SYMBOLS.ISignInController,
    AUTH_SYMBOLS.ISignUpController,
    AUTH_SYMBOLS.ISignOutController,
  ]) {
    if (authContainer.isBound(sym)) authContainer.unbind(sym);
  }
  authContainer
    .bind(AUTH_SYMBOLS.ISignInController)
    .toConstantValue(
      withSpan(
        tracer,
        { name: "auth.signIn", op: "controller" },
        withCapture(
          logger,
          { feature: "auth", layer: "controller", name: "auth.signIn" },
          signInController(wrappedSignIn),
        ),
      ),
    );
  authContainer
    .bind(AUTH_SYMBOLS.ISignUpController)
    .toConstantValue(
      withSpan(
        tracer,
        { name: "auth.signUp", op: "controller" },
        withCapture(
          logger,
          { feature: "auth", layer: "controller", name: "auth.signUp" },
          signUpController(wrappedSignUp),
        ),
      ),
    );
  authContainer
    .bind(AUTH_SYMBOLS.ISignOutController)
    .toConstantValue(
      withSpan(
        tracer,
        { name: "auth.signOut", op: "controller" },
        withCapture(
          logger,
          { feature: "auth", layer: "controller", name: "auth.signOut" },
          signOutController(wrappedSignOut),
        ),
      ),
    );
  // bus + queue are passed through; generated handlers consume them at the anchors below.
  void bus;
  void queue;
  void realtime;
  void realtimeRegistry;
  // <gen:event-handlers>
  // <gen:jobs>
  // <gen:realtime-handlers>

  // Boot-time conformance check: refuses to start if any use-case binding
  // is missing a required brand (withSpan / withCapture / withAudit).
  assertFeatureConformance(
    authContainer,
    authManifest,
    {
      signIn: AUTH_SYMBOLS.ISignInUseCase,
      signUp: AUTH_SYMBOLS.ISignUpUseCase,
      signOut: AUTH_SYMBOLS.ISignOutUseCase,
    },
    ctx,
  );
}
