// Copyright (c) 2026 Hellmai Ltd
// SPDX-License-Identifier: AGPL-3.0-only
//
// WU-2733 (INIT-060 Phase 3, ADR-013 §1 + §6):
// Real kernel-side RemoteControlPort adapter + manifest-entrypoint tool
// functions. Replaces the mock tracer-bullet entries from WU-2732
// without changing tool names, permissions, scopes, approvals, input
// schemas, or result shapes — the wire contract survives unchanged
// because every shape is pinned by `remote-controls/port.ts` +
// `remote-controls/types.ts`.
//
// Side effects: this adapter persists remote-control state under
// `.agent-runtime/remote-controls/<session>.json` (sidecar, isolated
// from the core orchestration workflow state store so existing
// workflow schema validation is untouched) and emits events through
// the supplied sink.

import type { ExecutionContext, ToolOutput } from '@lumenflow/kernel';
import {
  abortTurn,
  approveInflight,
  elevateAutonomy,
  lowerAutonomy,
  pauseTurn,
  resumeWorkflow,
  type RemoteControlOperationContext,
} from '../remote-controls/operations.js';
import type {
  AbortTurnInvocationInput,
  RemoteControlInvocationInput,
  RemoteControlPort,
} from '../remote-controls/port.js';
import {
  createRemoteControlStateStore,
  type RemoteControlStateStore,
} from '../remote-controls/state-store.js';
import {
  AGENT_RUNTIME_REMOTE_CONTROL_TOOL_NAMES,
  type AbortTurnInvocationResult,
  type AgentRuntimeRemoteControlToolName,
  type AnyRemoteControlInvocationResult,
  type RemoteControlInvocationResult,
} from '../remote-controls/types.js';
import type { AgentRuntimeEventSink } from '../turn-lifecycle-events.js';

export interface RealRemoteControlAdapterInput {
  store: RemoteControlStateStore;
  eventSink?: AgentRuntimeEventSink;
  now?: () => string;
}

/**
 * Real (non-mock) implementation of RemoteControlPort. Wires each port
 * method to the corresponding operation in `operations.ts`. Every port
 * method load-bears a `RemoteControlStateStore` so state survives
 * restarts; emission goes through the `AgentRuntimeEventSink` supplied
 * at construction (matches the mock's pattern so wiring parity holds).
 */
export class RealRemoteControlAdapter implements RemoteControlPort {
  private readonly context: RemoteControlOperationContext;

  constructor(input: RealRemoteControlAdapterInput) {
    this.context = {
      store: input.store,
      eventSink: input.eventSink,
      now: input.now ?? (() => new Date().toISOString()),
    };
  }

  async resumeWorkflow(
    input: RemoteControlInvocationInput,
  ): Promise<RemoteControlInvocationResult> {
    return resumeWorkflow(input, this.context);
  }

  async pauseTurn(input: RemoteControlInvocationInput): Promise<RemoteControlInvocationResult> {
    return pauseTurn(input, this.context);
  }

  async abortTurn(input: AbortTurnInvocationInput): Promise<AbortTurnInvocationResult> {
    return abortTurn(input, this.context);
  }

  async elevateAutonomy(
    input: RemoteControlInvocationInput,
  ): Promise<RemoteControlInvocationResult> {
    return elevateAutonomy(input, this.context);
  }

  async lowerAutonomy(input: RemoteControlInvocationInput): Promise<RemoteControlInvocationResult> {
    return lowerAutonomy(input, this.context);
  }

  async approveInflight(
    input: RemoteControlInvocationInput,
  ): Promise<RemoteControlInvocationResult> {
    return approveInflight(input, this.context);
  }
}

export function createRealRemoteControlAdapter(
  input: RealRemoteControlAdapterInput,
): RealRemoteControlAdapter {
  return new RealRemoteControlAdapter(input);
}

// ---------------------------------------------------------------------------
// Manifest-entrypoint wiring
// ---------------------------------------------------------------------------
//
// Each exported `*Tool` below matches the kernel's InProcessToolFn
// signature `(input, ctx) => Promise<ToolOutput>`. The `ctx` carries
// the run identifiers and allowed_scopes; we derive the storage root
// from the runtime's allowlist `.agent-runtime/**` path — or the
// module-scoped override when the host wires one directly (test
// harness + conductor runtime).

let moduleEventSink: AgentRuntimeEventSink | undefined;
let moduleStore: RemoteControlStateStore | undefined;
let moduleNow: (() => string) | undefined;

/**
 * Test/host-wiring hooks so the manifest-entrypoint tool functions
 * reuse a single adapter across invocations. Production wiring rebinds
 * these during pack activation (capability factory registers the
 * adapter once); tests clear them in beforeEach.
 */
export function setRealRemoteControlEventSink(sink: AgentRuntimeEventSink | undefined): void {
  moduleEventSink = sink;
}

export function setRealRemoteControlStateStore(store: RemoteControlStateStore | undefined): void {
  moduleStore = store;
}

export function setRealRemoteControlNow(now: (() => string) | undefined): void {
  moduleNow = now;
}

/** Resets all module-scoped wiring. Intended for unit-test teardown. */
export function resetRealRemoteControlModuleWiring(): void {
  moduleEventSink = undefined;
  moduleStore = undefined;
  moduleNow = undefined;
}

function resolveStore(ctx: ExecutionContext): RemoteControlStateStore {
  if (moduleStore) {
    return moduleStore;
  }
  const workspaceRoot = resolveWorkspaceRoot(ctx);
  return createRemoteControlStateStore({ workspaceRoot });
}

/**
 * Pull the workspace root from the kernel ExecutionContext. Falls back
 * to the process cwd when no explicit workspace_root metadata is
 * present — matching how the rest of the agent-runtime pack resolves
 * storage roots (keeps us on the kernel-provided path when available
 * without hard-failing when a test harness omits it).
 */
function resolveWorkspaceRoot(ctx: ExecutionContext): string {
  const metadata = (ctx as unknown as { metadata?: Record<string, unknown> }).metadata;
  const fromMetadata = metadata?.workspace_root;
  if (typeof fromMetadata === 'string' && fromMetadata.length > 0) {
    return fromMetadata;
  }
  const fromCtx = (ctx as unknown as { workspace_root?: unknown }).workspace_root;
  if (typeof fromCtx === 'string' && fromCtx.length > 0) {
    return fromCtx;
  }
  return process.cwd();
}

function buildToolOutput(data: AnyRemoteControlInvocationResult): ToolOutput {
  return {
    success: true,
    data,
    metadata: {
      real: true,
      source: 'WU-2733',
    },
  };
}

function parseInput(input: unknown): AbortTurnInvocationInput {
  if (input === null || input === undefined) {
    return {};
  }
  if (typeof input !== 'object') {
    return {};
  }
  const record = input as Record<string, unknown>;
  const result: AbortTurnInvocationInput = {};
  if (typeof record.session_id === 'string') {
    result.session_id = record.session_id;
  }
  if (typeof record.reason === 'string') {
    result.reason = record.reason;
  }
  return result;
}

async function invoke(
  toolName: AgentRuntimeRemoteControlToolName,
  input: unknown,
  ctx: ExecutionContext,
): Promise<ToolOutput> {
  const store = resolveStore(ctx);
  const adapter = new RealRemoteControlAdapter({
    store,
    eventSink: moduleEventSink,
    now: moduleNow,
  });
  const parsed = parseInput(input);

  switch (toolName) {
    case AGENT_RUNTIME_REMOTE_CONTROL_TOOL_NAMES.RESUME_WORKFLOW:
      return buildToolOutput(await adapter.resumeWorkflow(parsed));
    case AGENT_RUNTIME_REMOTE_CONTROL_TOOL_NAMES.PAUSE_TURN:
      return buildToolOutput(await adapter.pauseTurn(parsed));
    case AGENT_RUNTIME_REMOTE_CONTROL_TOOL_NAMES.ABORT_TURN:
      return buildToolOutput(await adapter.abortTurn(parsed));
    case AGENT_RUNTIME_REMOTE_CONTROL_TOOL_NAMES.ELEVATE_AUTONOMY:
      return buildToolOutput(await adapter.elevateAutonomy(parsed));
    case AGENT_RUNTIME_REMOTE_CONTROL_TOOL_NAMES.LOWER_AUTONOMY:
      return buildToolOutput(await adapter.lowerAutonomy(parsed));
    case AGENT_RUNTIME_REMOTE_CONTROL_TOOL_NAMES.APPROVE_INFLIGHT:
      return buildToolOutput(await adapter.approveInflight(parsed));
    default:
      return exhaustive(toolName);
  }
}

function exhaustive(value: never): never {
  throw new Error(`Unhandled remote-control tool: ${String(value)}`);
}

export async function resumeWorkflowTool(
  input: unknown,
  ctx: ExecutionContext,
): Promise<ToolOutput> {
  return invoke(AGENT_RUNTIME_REMOTE_CONTROL_TOOL_NAMES.RESUME_WORKFLOW, input, ctx);
}

export async function pauseTurnTool(input: unknown, ctx: ExecutionContext): Promise<ToolOutput> {
  return invoke(AGENT_RUNTIME_REMOTE_CONTROL_TOOL_NAMES.PAUSE_TURN, input, ctx);
}

export async function abortTurnTool(input: unknown, ctx: ExecutionContext): Promise<ToolOutput> {
  return invoke(AGENT_RUNTIME_REMOTE_CONTROL_TOOL_NAMES.ABORT_TURN, input, ctx);
}

export async function elevateAutonomyTool(
  input: unknown,
  ctx: ExecutionContext,
): Promise<ToolOutput> {
  return invoke(AGENT_RUNTIME_REMOTE_CONTROL_TOOL_NAMES.ELEVATE_AUTONOMY, input, ctx);
}

export async function lowerAutonomyTool(
  input: unknown,
  ctx: ExecutionContext,
): Promise<ToolOutput> {
  return invoke(AGENT_RUNTIME_REMOTE_CONTROL_TOOL_NAMES.LOWER_AUTONOMY, input, ctx);
}

export async function approveInflightTool(
  input: unknown,
  ctx: ExecutionContext,
): Promise<ToolOutput> {
  return invoke(AGENT_RUNTIME_REMOTE_CONTROL_TOOL_NAMES.APPROVE_INFLIGHT, input, ctx);
}
