/**
 * WorkerRegistrationCompat — WP-10 legacy AgentRegistry.register() shape.
 *
 * Demonstrates the separated lifecycle without rewriting production
 * AgentRegistry.register (18766 / SDK create path stays untouched):
 *
 *   registerIdentity(agentSpec)
 *   → ensureBinding(workerId, defaultHostId)  // mirrors platform session id
 *   → return AgentRecord-like projection with sdk_agent_id = externalSessionId
 *
 * Platform identity (sdk_agent_id / externalSessionId) ≠ Worker identity (workerId).
 */

import type { Agent } from "@codeflowmu/protocol";

import type { AgentRecord } from "../../types/state.ts";
import type { InMemorySdkAdapter } from "../../registry/AgentSdkAdapter.ts";
import type { AgentSdkAdapter } from "../../registry/AgentSdkAdapter.ts";
import { AgentHostRegistry } from "../hosts/AgentHostRegistry.ts";
import { SwitchboardPaths } from "../paths/SwitchboardPaths.ts";
import {
  WorkerBindingService,
  type EnsureBindingResult,
} from "./WorkerBindingService.ts";
import {
  WorkerIdentityService,
  mapAgentToWorker,
} from "./WorkerIdentityService.ts";

export interface CompatibleRegisterResult {
  record: AgentRecord;
  identityPath: string;
  binding: EnsureBindingResult;
  /** Proof: workerId is not equal to mirrored platform id when host prepared. */
  workerId: string;
  platformSessionId: string;
  invokedSdkCreate: boolean;
}

export interface WorkerRegistrationCompatOptions {
  paths: SwitchboardPaths;
  defaultHostId: string;
  hostRegistry?: AgentHostRegistry;
  /**
   * Optional legacy SDK — when provided, create() is called to mirror old
   * register() external behavior (sdk_agent_id). Identity is still registered
   * first and does not depend on SDK success for existence.
   */
  sdk?: AgentSdkAdapter | InMemorySdkAdapter;
  identities?: WorkerIdentityService;
  bindings?: WorkerBindingService;
}

export class WorkerRegistrationCompat {
  readonly #identities: WorkerIdentityService;
  readonly #bindings: WorkerBindingService;
  readonly #defaultHostId: string;
  readonly #sdk: AgentSdkAdapter | InMemorySdkAdapter | undefined;

  constructor(opts: WorkerRegistrationCompatOptions) {
    this.#defaultHostId = String(opts.defaultHostId ?? "").trim();
    if (!this.#defaultHostId) {
      throw new Error("WorkerRegistrationCompat: defaultHostId required");
    }
    this.#identities =
      opts.identities ??
      new WorkerIdentityService({ paths: opts.paths, mode: "shadow" });
    this.#bindings =
      opts.bindings ??
      new WorkerBindingService({
        paths: opts.paths,
        mode: "shadow",
        hostRegistry: opts.hostRegistry,
        identities: this.#identities,
      });
    this.#sdk = opts.sdk;
  }

  get identities(): WorkerIdentityService {
    return this.#identities;
  }

  get bindings(): WorkerBindingService {
    return this.#bindings;
  }

  /**
   * Compatible register: identity first, then binding, optional SDK create.
   * Host create failure does not drop identity.
   */
  async register(agentSpec: Agent): Promise<CompatibleRegisterResult> {
    if (agentSpec.layer === "admin") {
      throw new Error("WorkerRegistrationCompat: layer=admin forbidden");
    }

    const { identity, path: identityPath } =
      await this.#identities.registerIdentityFromAgent(agentSpec);

    let sdk_agent_id: string | undefined;
    let invokedSdkCreate = false;
    if (this.#sdk) {
      invokedSdkCreate = true;
      const created = await this.#sdk.create({
        agentId: agentSpec.agent_id,
        role: agentSpec.role,
        layer: agentSpec.layer,
        runtime: agentSpec.runtime,
        ...(agentSpec.workspace !== undefined
          ? { workspace: agentSpec.workspace }
          : {}),
        ...(agentSpec.model?.id !== undefined
          ? { modelId: agentSpec.model.id }
          : {}),
      });
      sdk_agent_id = created.sdk_agent_id;
    }

    const binding = await this.#bindings.ensureBinding(
      identity.workerId,
      this.#defaultHostId,
      {
        reason: "compatible-register",
        externalSessionId: sdk_agent_id,
        workspaceId: identity.workspaceId,
        roleId: identity.roleId,
        layer: identity.layer,
      },
    );

    const platformSessionId =
      binding.status === "binding_failed"
        ? sdk_agent_id ?? ""
        : binding.revision.host.externalSessionId;

    const now = new Date().toISOString();
    const record: AgentRecord = {
      protocol: {
        ...agentSpec,
        sdk_agent_id: platformSessionId || sdk_agent_id || agentSpec.agent_id,
        status: "idle",
        ...(agentSpec.started_at ? {} : { started_at: now }),
        last_active_at: now,
      },
      runtime_binding_mode: agentSpec.runtime,
      runtime_last_reconciled_at: now,
    };

    return {
      record,
      identityPath,
      binding,
      workerId: identity.workerId,
      platformSessionId: String(record.protocol.sdk_agent_id ?? ""),
      invokedSdkCreate,
    };
  }
}

export { mapAgentToWorker };
