// src/engine/core/MatrixContext.ts

import type { IMatrixContext } from './IMatrixContext';
import type { IMatrixRuntime } from './IMatrixRuntime';
import type { IComponentRegistryLike } from './IComponentRegistryLike';
import type { ILispEnvironment } from './ILispEnvironment';
import type { IMatrixContextOptions } from './IMatrixContextOptions';
import type { ITransportAdapter } from '../remoting/ITransportAdapter';
import type { ISecurityRealm } from '../../core/security/ISecurityRealm';
import { resolveService } from '../remoting/ServiceCascadeResolver';

/**
 * Clean a mount string: strip trailing dots/slashes.
 * Returns "" for empty/null input (root context).
 */
function cleanMount(input: string): string {
  const raw = (input ?? '').trim();
  return raw.replace(/[./]+$/, '');
}

/**
 * Normalize a segment for safe path construction.
 * Preserves case for UPPERCASE naming convention.
 * Preserves $ for system-plane messages ($join, $introspect, etc.).
 */
function safeSegment(seg: string): string {
  const normalized = (seg ?? '').trim();
  if (!normalized) return 'component';

  return normalized
    .replace(/\/+/g, '.')              // slashes → dots
    .replace(/[^a-zA-Z0-9._$-]+/g, '_') // other special chars → underscore (preserves $)
    .replace(/_+/g, '_')
    .replace(/^_+/, '')
    .replace(/_+$/, '')
    .replace(/^\.+/, '')
    .replace(/\.+$/, '') || 'component';
}

/** @deprecated Use IMatrixContextOptions from IMatrixContext.ts instead */
export type MatrixContextOptions = IMatrixContextOptions;

/**
 * Canonical implementation of IMatrixContext.
 *
 * Provides:
 * - Mount path identity (dot-delimited, no scheme, no root)
 * - Transport adapter injection
 * - Runtime capabilities injection (NO SINGLETON)
 * - Component registry injection (NO SINGLETON)
 * - Hierarchical service registry
 * - Child context derivation
 */
export class MatrixContext implements IMatrixContext {
  readonly mount: string;
  readonly transport: ITransportAdapter;
  readonly parent?: IMatrixContext;
  /** REQUIRED - actors use runtime.createChild() for UI Control Inversion */
  readonly runtime: IMatrixRuntime;
  readonly registry?: IComponentRegistryLike;
  /** Optional LISP environment for dual dispatch */
  readonly lispEnv?: ILispEnvironment;
  /** Optional security realm for message verification (Vision 27) */
  readonly securityRealm?: ISecurityRealm;

  private readonly _services = new Map<string | symbol, unknown>();

  constructor(
    mount: string,
    transport: ITransportAdapter,
    parent: IMatrixContext | undefined,
    runtime: IMatrixRuntime,
    registry?: IComponentRegistryLike,
    lispEnv?: ILispEnvironment,
    securityRealm?: ISecurityRealm
  ) {
    this.mount = cleanMount(mount);
    this.transport = transport;
    this.parent = parent;
    // Runtime is REQUIRED - inherit from parent if not directly provided
    this.runtime = runtime ?? parent?.runtime;
    if (!this.runtime) {
      throw new Error('[MatrixContext] runtime is REQUIRED for UI Control Inversion pattern');
    }
    this.registry = registry ?? parent?.registry;
    // LISP environment inherited from parent for dual dispatch
    this.lispEnv = lispEnv ?? parent?.lispEnv;
    // Security realm inherited from parent (supervision tree = security tree)
    this.securityRealm = securityRealm ?? parent?.securityRealm;
  }

  derive(segment: string): IMatrixContext {
    const s = safeSegment(segment);
    const newMount = this.mount ? `${this.mount}.${s}` : s;

    // Child inherits transport, runtime, registry, lispEnv, securityRealm from parent
    // Registry creates a child registry for local registrations
    const childRegistry = this.registry?.createChild();
    return new MatrixContext(
      newMount,
      this.transport,
      this,
      this.runtime!,
      childRegistry,
      this.lispEnv,
      this.securityRealm
    );
  }

  getService<T>(key: string | symbol): T | undefined {
    // Check local registry first
    if (this._services.has(key)) {
      return this._services.get(key) as T;
    }
    // Fall back to parent
    return this.parent?.getService<T>(key);
  }

  setService<T>(key: string | symbol, service: T): void {
    this._services.set(key, service);
  }

  /**
   * Resolve a system service by walking the mount hierarchy on the wire.
   *
   * 1. Check in-process registry (getService with endpoint key)
   * 2. If not found, probe the mount tree via $ping/$pong (ServiceCascadeResolver)
   * 3. Cache the result for subsequent sync lookups
   *
   * @param service - Service name (e.g., 'llm', 'component-store')
   * @returns Resolved service mount path (e.g., 'system.llm')
   */
  async resolveServiceAsync(service: string): Promise<string> {
    // Check in-process registry first (sync path)
    const endpointKey = `${service}-endpoint`;
    const cached = this.getService<string>(endpointKey);
    if (cached) return cached;

    // Probe mount tree via $ping/$pong
    const resolved = await resolveService(this.mount, service, this.transport);

    // Cache for subsequent sync lookups
    this.setService(endpointKey, resolved);

    return resolved;
  }

  /**
   * Resolve a service by contract — walks the intent provider chain.
   * Context is provider #1 (fastest). Caches result for subsequent calls.
   */
  async resolve(contract: import('./ServiceContract').ServiceContract): Promise<import('./ServiceContract').IActorService | undefined> {
    // Provider 1: Check context cascade (pre-cached, instant)
    const cached = this.getService<import('./ServiceContract').IActorService>(contract.id);
    if (cached) return cached;

    // Provider 2: BusIntentProvider ($introspect scan of mounted actors)
    const busProviderKey = Symbol.for('matrix.bus.intent.provider');
    const busProvider = this.getService<import('./ServiceContract').IIntentProvider>(busProviderKey);
    if (busProvider) {
      const found = await busProvider.resolve(contract);
      if (found) return found;
    }

    // Provider 3: FederatedIntentProvider (cross-realm) — future
    // Provider 4: SemanticIntentProvider (AI matching) — future

    return undefined;
  }

  /**
   * Create a root context with the given mount and transport.
   * Convenience factory for creating the initial context.
   *
   * @param mount - Root mount path (e.g., "", "daemon", "app")
   * @param transport - Transport adapter
   * @param runtime - REQUIRED runtime for UI Control Inversion
   * @param registry - Optional component registry
   * @param lispEnv - Optional LISP environment for dual dispatch
   * @param securityRealm - Optional security realm for message verification
   */
  static create(
    mount: string,
    transport: ITransportAdapter,
    runtime: IMatrixRuntime,
    registry?: IComponentRegistryLike,
    lispEnv?: ILispEnvironment,
    securityRealm?: ISecurityRealm
  ): MatrixContext {
    return new MatrixContext(mount, transport, undefined, runtime, registry, lispEnv, securityRealm);
  }

  /**
   * Create a context from options object.
   */
  static fromOptions(options: IMatrixContextOptions): MatrixContext {
    return new MatrixContext(
      options.mount,
      options.transport,
      options.parent,
      options.runtime,
      options.registry,
      options.lispEnv,
      options.securityRealm
    );
  }
}
