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 { {{camelCase name}}Container } from "./container";
import { {{constantCase name}}_SYMBOLS } from "./symbols";
import { {{camelCase name}}Manifest } from "../feature.manifest";
import { {{pascalCase entity}}Repository } from "../infrastructure/repositories/{{kebabCase entity}}.repository";
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";

export function bindProduction{{pascalCase name}}(ctx: BindProductionContext): void {
  const { config, tracer, logger, bus, queue, realtime, realtimeRegistry } = ctx;

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

  // Real repository
  if ({{camelCase name}}Container.isBound({{constantCase name}}_SYMBOLS.I{{pascalCase entity}}Repository)) {
    {{camelCase name}}Container.unbind({{constantCase name}}_SYMBOLS.I{{pascalCase entity}}Repository);
  }
  const repo = new {{pascalCase entity}}Repository(config, tracer, logger);
  {{camelCase name}}Container
    .bind({{constantCase name}}_SYMBOLS.I{{pascalCase entity}}Repository)
    .toConstantValue(repo);

  // Use case
  const wrappedGet{{pascalCase entity}} = wireUseCase({
    container: {{camelCase name}}Container,
    symbol: {{constantCase name}}_SYMBOLS.IGet{{pascalCase entity}}UseCase,
    factory: get{{pascalCase entity}}UseCase,
    deps: [repo],
    feature: "{{kebabCase name}}",
    layer: "use-case",
    name: "get{{pascalCase entity}}",
    tracer,
    logger,
  });

  // Controller — wrapped with span at bind time
  if ({{camelCase name}}Container.isBound({{constantCase name}}_SYMBOLS.IGet{{pascalCase entity}}Controller)) {
    {{camelCase name}}Container.unbind({{constantCase name}}_SYMBOLS.IGet{{pascalCase entity}}Controller);
  }
  {{camelCase name}}Container
    .bind({{constantCase name}}_SYMBOLS.IGet{{pascalCase entity}}Controller)
    .toConstantValue(
      withSpan(
        tracer,
        { name: "{{camelCase name}}.get{{pascalCase entity}}", op: "controller" },
        withCapture(
          logger,
          { feature: "{{kebabCase name}}", layer: "controller", name: "{{camelCase name}}.get{{pascalCase entity}}" },
          get{{pascalCase entity}}Controller(wrappedGet{{pascalCase entity}}),
        ),
      ),
    );
  // 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(
    {{camelCase name}}Container,
    {{camelCase name}}Manifest,
    {
      get{{pascalCase entity}}: {{constantCase name}}_SYMBOLS.IGet{{pascalCase entity}}UseCase,
    },
    ctx,
  );
}
