import type { IAnalytics } from "./analytics.interface";
import { attachBrand } from "@repo/core-shared/conformance";

/**
 * Phantom-type brand attached at wrap time by `withAnalytics`. The conformance
 * system uses this as the type-level seam for use cases that declare
 * `analyticsEvents: [...]` in their manifest — without `__analyzed`, the
 * binding is not assignable to `ProductionUseCase<I, O, M>` when M demands it.
 * At runtime the brand is a non-enumerable property attached by `attachBrand`
 * from `@repo/core-shared/conformance`, so the boot-time assertion can verify
 * the binding went through the analytics-aware path.
 */
export type Analyzed<F> = F & { readonly __analyzed: true };

/**
 * Use-case wrapper applied at DI bind time. The wrapper is a thin closure
 * that forwards to `fn` unchanged and carries the `__analyzed` brand. The
 * forward closure (instead of returning `fn` directly) keeps the brand on
 * a fresh function so the caller's original `fn` is not mutated — important
 * when the same factory output is used elsewhere unwrapped (dev-seed paths,
 * tests).
 */
export function withAnalytics<Args extends unknown[], R>(
  // TODO: wire automated event recording from manifest declarations.
  // `analyticsEvents[]` declarations. For now, the wrapper exists to:
  //   (1) require callers to pass the analytics instance at bind time (dep is available)
  //   (2) attach the `__analyzed` brand so the boot-time assertion can verify
  //       use cases were bound through the analytics-aware path.
  analytics: IAnalytics,
  fn: (...args: Args) => Promise<R>,
): Analyzed<(...args: Args) => Promise<R>> {
  void analytics;
  const wrapped: (...args: Args) => Promise<R> = (...args) => fn(...args);
  attachBrand(wrapped, "__analyzed");
  return wrapped as Analyzed<(...args: Args) => Promise<R>>;
}
