/**
 * RootActor — the runtime's public face in the actor tree.
 *
 * Sits at mount "" (empty string = root). Makes the runtime addressable,
 * introspectable, and uniform with every other actor in the hierarchy.
 *
 * Unix analog: init (PID 1) — first process, supervises all top-level services.
 *
 * This actor does NOT replace MatrixRuntime — it wraps it, providing the
 * actor-model interface on top of the runtime's internal machinery.
 */
import { MatrixActor, type ActorStatusSnapshotV1 } from './MatrixActor.js';
import type { IComponentRuntimeMetadata } from './composite/IComponentRuntimeMetadata.js';

type RuntimeMetaContext = {
  realm?: string;
};

type RuntimeRegisteredDeploymentInstance = {
  id: string;
  packageName: string;
  mount: string;
  rootKind: string;
  class: string;
  state: string;
  packageRevision?: string;
  componentType?: string;
};

type RuntimeRegisteredPackageRequirement = {
  packageName: string;
  revision?: string;
  sourceOverride?: string;
};

type RuntimeRegisteredSnapshot = {
  componentTags: string[];
  supervisedRoots: string[];
  requiredPackages?: RuntimeRegisteredPackageRequirement[];
  deploymentInstances?: RuntimeRegisteredDeploymentInstance[];
};

export class RootActor extends MatrixActor {

  static accepts = {
    '$children': { description: 'List running child actors under this runtime' },
    '$status': { description: 'Get runtime health and inventory summary' },
    'runtime.status': { description: 'Get full runtime introspection data', options: 'object?' },
    'runtime.services': { description: 'List registered service endpoints', options: 'object?' },
    'runtime.actors': { description: 'List all mounted actors with status', options: 'object?' },
    'runtime.registered': {
      description: 'List runtime-registered component tags, supervised roots, required packages, and deployment instances',
      options: 'object?',
    },
    'runtime.start': { mount: 'string' },
    'runtime.stop': { mount: 'string' },
    'runtime.reload': { mount: 'string' },
    'runtime.remove': { mount: 'string' },
    'runtime.restart': { mount: 'string' },
  };

  static description = 'Root actor — the runtime itself. Mount: "" (empty string).';

  private _startTime = Date.now();

  /**
   * Snapshot of all component mounts known to the runtime.
   * Set externally by MatrixRuntime after construction.
   */
  _componentSnapshot: () => Array<{ id: string; mount: string; metadata?: IComponentRuntimeMetadata }> = () => [];

  /**
   * Service registry snapshot — returns registered service keys.
   * Set externally by MatrixRuntime.
   */
  _serviceSnapshot: () => Record<string, string> = () => ({});

  /**
   * Runtime metadata (realm, version, type).
   * Set externally by MatrixRuntime.
   */
  _runtimeMeta: () => Record<string, unknown> = () => ({});

  /**
   * Runtime registration snapshot — registered component tags and supervised roots.
   * Set externally by MatrixRuntime.
   */
  _registeredSnapshot: () => RuntimeRegisteredSnapshot = () => ({
    componentTags: [],
    supervisedRoots: [],
  });

  /**
   * Root component lifecycle hooks. Set externally by MatrixRuntime.
   */
  _startComponent: (mount: string) => Promise<boolean> = async () => {
    throw new Error('Runtime start hook not configured');
  };

  _stopComponent: (mount: string) => Promise<boolean> = async () => {
    throw new Error('Runtime stop hook not configured');
  };

  _reloadComponent: (mount: string) => Promise<void> = async () => {
    throw new Error('Runtime reload hook not configured');
  };

  _removeComponent: (mount: string) => Promise<void> = async () => {
    throw new Error('Runtime remove hook not configured');
  };

  _restartComponent: (mount: string) => Promise<void> = async () => {
    throw new Error('Runtime restart hook not configured');
  };

  // ── $introspect override ─────────────────────────────────────────

  protected override onIntrospect(payload?: { depth?: 'basic' | 'full' }) {
    const base = super.onIntrospect(payload);
    const meta = this._runtimeMeta();
    const components = this._componentSnapshot();

    return {
      ...base,
      type: 'runtime',
      realm: meta.realm ?? (this._context as RuntimeMetaContext | undefined)?.realm ?? 'unknown',
      version: meta.version ?? '0.0.0',
      runtimeType: meta.type ?? 'headless',
      running: meta.running ?? true,
      uptime: Date.now() - this._startTime,
      actors: components.length,
      registeredComponents: this._registeredSnapshot().componentTags.length,
      supervisedRoots: this._registeredSnapshot().supervisedRoots,
      services: this._serviceSnapshot(),
      // Override children with actual component list
      children: components.map(c => ({
        id: c.id,
        mount: c.mount,
        slot: '',
        componentClass: 'unknown',
        accepts: [],
        emits: [],
        tracks: [],
        metadata: c.metadata,
      })),
    };
  }

  // ── $ping override ───────────────────────────────────────────────

  override on$ping() {
    return { status: 'ok', mount: '', type: 'runtime' };
  }

  // ── Domain ops ───────────────────────────────────────────────────

  onRuntimeStatus() {
    return this.onIntrospect({ depth: 'full' });
  }

  onRuntimeServices() {
    return this._serviceSnapshot();
  }

  onRuntimeActors() {
    return this._componentSnapshot();
  }

  onRuntimeRegistered() {
    return this._registeredSnapshot();
  }

  on$children() {
    const children = this._componentSnapshot().map((entry) => entry.mount);
    return {
      ok: true,
      children,
      count: children.length,
    };
  }

  on$status(): ActorStatusSnapshotV1 & {
    ok: true;
    type: 'runtime';
    runtimeType: unknown;
    realm: unknown;
    running: unknown;
    actors: number;
    registeredComponents: number;
    supervisedRoots: string[];
    services: string[];
  } {
    const meta = this._runtimeMeta();
    const registered = this._registeredSnapshot();
    const services = this._serviceSnapshot();
    const actors = this._componentSnapshot();
    const base = super.on$status();

    // Slice 1b (control-actor-nav): this is the runtime root actor's own
    // $status payload field, NOT a registry runtime-directory claim. The
    // runtime directory authority is `system.runtimes` (RuntimeManagerActor);
    // consumers must NOT use this kind field for runtime discovery.
    return {
      ...base,
      ok: true,
      kind: 'runtime',
      type: 'runtime',
      runtimeType: meta.type ?? 'headless',
      realm: meta.realm ?? (this._context as RuntimeMetaContext | undefined)?.realm ?? 'unknown',
      running: meta.running ?? true,
      uptime: Date.now() - this._startTime,
      actors: actors.length,
      registeredComponents: registered.componentTags.length,
      supervisedRoots: registered.supervisedRoots,
      services: Object.keys(services),
    };
  }

  async onRuntimeRemove(payload: { mount?: string }) {
    const mount = typeof payload?.mount === 'string' ? payload.mount.trim() : '';
    if (!mount) {
      throw new Error('runtime.remove requires a non-empty mount');
    }
    await this._removeComponent(mount);
    return {
      ok: true,
      removed: true,
      mount,
    };
  }

  async onRuntimeStart(payload: { mount?: string }) {
    const mount = typeof payload?.mount === 'string' ? payload.mount.trim() : '';
    if (!mount) {
      throw new Error('runtime.start requires a non-empty mount');
    }
    const started = await this._startComponent(mount);
    return {
      ok: true,
      mount,
      started,
      alreadyRunning: !started,
    };
  }

  async onRuntimeStop(payload: { mount?: string }) {
    const mount = typeof payload?.mount === 'string' ? payload.mount.trim() : '';
    if (!mount) {
      throw new Error('runtime.stop requires a non-empty mount');
    }
    const stopped = await this._stopComponent(mount);
    return {
      ok: true,
      mount,
      stopped,
      alreadyStopped: !stopped,
    };
  }

  async onRuntimeReload(payload: { mount?: string }) {
    const mount = typeof payload?.mount === 'string' ? payload.mount.trim() : '';
    if (!mount) {
      throw new Error('runtime.reload requires a non-empty mount');
    }
    await this._reloadComponent(mount);
    return {
      ok: true,
      mount,
      reloaded: true,
    };
  }

  async onRuntimeRestart(payload: { mount?: string }) {
    const mount = typeof payload?.mount === 'string' ? payload.mount.trim() : '';
    if (!mount) {
      throw new Error('runtime.restart requires a non-empty mount');
    }
    await this._restartComponent(mount);
    return {
      ok: true,
      restarted: true,
      mount,
    };
  }
}
