/**
 * Simple DI Container — Factory-pattern service locator
 *
 * Provides a lightweight dependency injection container using factory
 * functions (no heavy frameworks). Services are lazily instantiated
 * and cached as singletons.
 *
 * Usage:
 *   import { container } from '@/lib/container';
 *   const settings = container.resolve('settings');
 *
 * Registration:
 *   container.register('myService', () => new MyService());
 *
 * @module lib/container
 */

import { evaluateFirstAllowed, evaluateRequest, PolicyEngine } from "../domain/policyEngine.ts";
import { getDbInstance } from "./db/core.ts";
import {
  decrypt,
  decryptConnectionFields,
  encrypt,
  encryptConnectionFields,
} from "./db/encryption.ts";
import { getSettings } from "./localDb.ts";
import { getCircuitBreaker } from "../shared/utils/circuitBreaker.ts";
import { recordTelemetry, RequestTelemetry } from "../shared/utils/requestTelemetry.ts";

type Factory<T = any> = () => T;

class Container {
  private _factories = new Map<string, Factory>();
  private _instances = new Map<string, any>();

  /**
   * Register a factory for a service. Does NOT instantiate until resolve().
   */
  register<T>(name: string, factory: Factory<T>): void {
    this._factories.set(name, factory);
    // Clear cached instance if re-registering (useful for testing)
    this._instances.delete(name);
  }

  /**
   * Resolve a service by name. Lazy-creates via factory on first call,
   * then returns the cached singleton.
   */
  resolve<T = any>(name: string): T {
    if (this._instances.has(name)) {
      return this._instances.get(name) as T;
    }

    const factory = this._factories.get(name);
    if (!factory) {
      throw new Error(`[Container] No factory registered for "${name}"`);
    }

    const instance = factory();
    this._instances.set(name, instance);
    return instance as T;
  }

  /**
   * Check if a service is registered (factory exists).
   */
  has(name: string): boolean {
    return this._factories.has(name);
  }

  /**
   * List all registered service names.
   */
  list(): string[] {
    return Array.from(this._factories.keys());
  }

  /**
   * Reset all factories and instances (for testing).
   */
  reset(): void {
    this._factories.clear();
    this._instances.clear();
  }
}

// ── Singleton container instance ──
export const container = new Container();

// ── Default registrations ──
// Services are still lazily instantiated on first resolve().

container.register("settings", () => {
  return { get: getSettings };
});

container.register("db", () => {
  return getDbInstance();
});

container.register("encryption", () => {
  return {
    encrypt,
    decrypt,
    encryptConnectionFields,
    decryptConnectionFields,
  };
});

container.register("policyEngine", () => {
  return { evaluateRequest, evaluateFirstAllowed, PolicyEngine };
});

container.register("circuitBreaker", () => {
  return { get: getCircuitBreaker };
});

container.register("telemetry", () => {
  return { RequestTelemetry, recordTelemetry };
});

export default container;
