import {
  withSpan,
  withCapture,
  INSTRUMENTATION_SYMBOLS,
  type ITracer,
  type ILogger,
} from "@repo/core-shared/instrumentation";
import type { BindProductionContext } from "@repo/core-shared/di";
import { assertFeatureConformance } from "@repo/core-shared/conformance";
import type { ProductionUseCase } from "@repo/core-shared/conformance";
import { authManifest } from "../feature.manifest";
import type { AuthManifest } from "../feature.manifest";
import type {
  SignInInput,
  SignInOutput,
} from "../application/use-cases/sign-in.use-case";
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 } = 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 — wrapped with span + capture at bind time
  const wrappedSignIn: ProductionUseCase<
    SignInInput,
    SignInOutput,
    AuthManifest["useCases"]["signIn"]
  > = withSpan(
    tracer,
    { name: "auth.signIn", op: "use-case" },
    withCapture(
      logger,
      { feature: "auth", layer: "use-case", name: "auth.signIn" },
      signInUseCase(repo, authService),
    ),
  );
  const wrappedSignUp = withSpan(
    tracer,
    { name: "auth.signUp", op: "use-case" },
    withCapture(
      logger,
      { feature: "auth", layer: "use-case", name: "auth.signUp" },
      signUpUseCase(repo, authService, bus),
    ),
  );
  const wrappedSignOut = withSpan(
    tracer,
    { name: "auth.signOut", op: "use-case" },
    withCapture(
      logger,
      { feature: "auth", layer: "use-case", name: "auth.signOut" },
      signOutUseCase(authService),
    ),
  );

  for (const sym of [
    AUTH_SYMBOLS.ISignInUseCase,
    AUTH_SYMBOLS.ISignUpUseCase,
    AUTH_SYMBOLS.ISignOutUseCase,
  ]) {
    if (authContainer.isBound(sym)) authContainer.unbind(sym);
  }
  authContainer.bind(AUTH_SYMBOLS.ISignInUseCase).toConstantValue(wrappedSignIn);
  authContainer.bind(AUTH_SYMBOLS.ISignUpUseCase).toConstantValue(wrappedSignUp);
  authContainer.bind(AUTH_SYMBOLS.ISignOutUseCase).toConstantValue(wrappedSignOut);

  // 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 accept-and-forward in Phase 6; consumed by Phase 7 generator
  // output at the <gen:event-handlers> / <gen:jobs> 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,
  );
}
