import { v4 as uuidv4 } from 'uuid';

import { runV1CommandBatch } from './v1CommandBatchRuntime';

export const V1_BROWSER_CAPABILITIES = ['browser.page.get'];

export type V1TerminalStatus =
  | 'succeeded'
  | 'failed'
  | 'denied'
  | 'rejected'
  | 'skipped'
  | 'unsupported'
  | 'unavailable'
  | 'expired';

export type V1CommandStatus = V1TerminalStatus;

export interface V1GrammarDiscovery {
  interface_version: number;
  capabilities: string[];
  site_shape?: string;
  settings_rev?: string;
}

export type V1CapabilitySnapshotGrammar = V1GrammarDiscovery;

export interface V1CapabilitySnapshot {
  snapshot_id: string;
  captured_at: string;
  schema_version: 'v1';
  plugin_version: string;
  site_identity: Record<string, unknown>;
  calling_surface: string;
  assistant_settings: Record<string, unknown>;
  // V1 grammar capabilities (names only) plus interface version and fingerprints.
  grammar: V1CapabilitySnapshotGrammar;
}

export interface V1PluginCommand {
  command_id: string;
  capability: string;
  phase: 'execute' | 'validate';
  input: Record<string, unknown>;
  depends_on?: string[];
  continue_after_failure?: boolean;
  display?: {
    label?: string;
    target?: string;
    summary?: string;
  };
}

export interface V1CommandExecutionCommand {
  command_id: string;
  capability: string;
  phase: 'execute' | 'validate';
  input: Record<string, unknown>;
  depends_on?: string[];
  continue_after_failure?: boolean;
  approval?: Record<string, unknown>;
}

export interface V1CommandExecutionRequest {
  batch_id: string;
  commands: V1CommandExecutionCommand[];
}

export interface V1CommandPauseEvent {
  type: 'v1.command_pause';
  pause_id: string;
  command_batch: {
    id: string;
    commands: V1PluginCommand[];
  };
}

export interface V1CommandResult {
  command_id: string;
  capability?: string;
  status: V1TerminalStatus;
  data?: unknown;
  policy?: {
    decision: string;
    reason_codes: string[];
  };
  // V1 grammar results carry meta (site_shape fingerprint) and optional
  // artifacts instead of policy; forwarded verbatim to the resume payload.
  meta?: Record<string, unknown>;
  artifacts?: unknown[];
  error?: {
    code: string;
    message: string;
  };
}

export interface V1ResumePayload {
  pause_id: string;
  command_results: V1CommandResult[];
}

export interface V1RuntimeArtifact {
  id: string;
  kind: 'rendered_screenshot';
  mime: string;
  size_bytes: number;
  origin: Record<string, unknown>;
  visibility: 'llm_visible' | 'trace_only' | 'user_visible';
  retention: Record<string, unknown>;
  llm_projection: Record<string, unknown>;
}

export type V1PauseEvent = V1CommandPauseEvent;
export type V1PauseResumePayload = V1ResumePayload;

export type V1CommandRuntimeEvent =
  | { type: 'command_started'; id: string; batch_id: string; label: string; target?: string }
  | { type: 'approval_needed'; id: string; batch_id: string; label: string; target?: string }
  | { type: 'approval_granted'; id: string; batch_id: string; label: string; target?: string }
  | { type: 'approval_rejected'; id: string; batch_id: string; label: string; target?: string }
  | { type: 'command_completed'; id: string; batch_id: string; ok: boolean; summary?: string };

export interface V1CommandApprovalRequest {
  batch_id?: string;
  approval: V1CommandApprovalRequirement;
  commands: V1PluginCommand[];
  results?: V1CommandResult[];
  display: {
    label: string;
    target?: string;
    summary?: string;
  };
}

interface V1RuntimeDiscovery {
  plugin_version?: string;
  site_identity?: Record<string, unknown>;
  assistant_settings?: Record<string, unknown>;
  grammar: V1GrammarDiscovery;
}

interface V1CommandExecutionResponse {
  results?: V1CommandResult[];
}

interface V1CommandExecutionClientOptions {
  signal?: AbortSignal;
}

export interface V1CommandClient {
  executeCommands: (
    request: V1CommandExecutionRequest,
    options?: V1CommandExecutionClientOptions,
  ) => Promise<V1CommandExecutionResponse>;
  supportsAbortSignal?: boolean;
}

export interface V1CommandApprovalRequirement {
  required?: boolean;
  group?: string;
  action?: string;
  mode?: 'before_execute' | 'after_execute';
  [key: string]: unknown;
}

export interface V1CommandApprovalDecision {
  approved: boolean;
  reason?: string;
  approval?: Record<string, unknown>;
}

export interface V1CommandApprovalPolicy {
  requireReadApproval: boolean;
}

export interface V1RuntimeContext {
  capabilitySnapshot: V1CapabilitySnapshot;
  approvalPolicy: V1CommandApprovalPolicy;
}

interface V1CommandExecutionOptions {
  requestApproval?: (request: V1CommandApprovalRequest) => Promise<V1CommandApprovalDecision>;
  approvalPolicy?: V1CommandApprovalPolicy;
  commandDispatchTimeoutMs?: number;
}

interface PreparedCommandExecution {
  commands: V1CommandExecutionCommand[];
  terminalResults: Map<string, V1CommandResult>;
}

interface V1PreflightDecision {
  result?: V1CommandResult;
  requiresApproval: boolean;
}

interface V1LifecycleLabels {
  started: string;
  completed: string;
  failed: string;
  skipped: string;
  target?: string;
}

export interface V1BrowserCommandRunner {
  runBrowserCommand: (command: V1CommandExecutionCommand) => Promise<Omit<V1CommandResult, 'command_id' | 'capability'>>;
}

export interface V1CommandRuntimeOptions {
  commandClient?: V1CommandClient;
  browserCommandRunner?: V1BrowserCommandRunner;
  approvalPolicy?: V1CommandApprovalPolicy;
  requestApproval?: (request: V1CommandApprovalRequest) => Promise<V1CommandApprovalDecision>;
  emitEvent?: (event: V1CommandRuntimeEvent) => void;
  commandDispatchTimeoutMs?: number;
}

const DEFAULT_COMMAND_DISPATCH_TIMEOUT_MS = 45000;

export async function fetchPluginV1RuntimeDiscovery(options: {
  endpoint: string;
  restNonce: string;
  fetchImpl?: typeof fetch;
}): Promise<V1RuntimeDiscovery> {
  const fetchImpl = options.fetchImpl || fetch;
  const response = await fetchImpl(options.endpoint, {
    method: 'GET',
    credentials: 'include',
    headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': options.restNonce },
  });
  if (!response.ok) {
    throw new Error(`Capability discovery failed: ${response.status}`);
  }
  return await response.json();
}

export function createFetchV1CommandClient(options: {
  endpoint: string;
  restNonce: string;
  fetchImpl?: typeof fetch;
}): V1CommandClient {
  return {
    supportsAbortSignal: true,
    executeCommands: async (
      request: V1CommandExecutionRequest,
      clientOptions?: V1CommandExecutionClientOptions,
    ): Promise<V1CommandExecutionResponse> => {
      const fetchImpl = options.fetchImpl || fetch;
      const requestInit: RequestInit = {
        method: 'POST',
        credentials: 'include',
        headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': options.restNonce },
        body: JSON.stringify(request),
      };
      if (clientOptions?.signal) {
        requestInit.signal = clientOptions.signal;
      }
      const response = await fetchImpl(options.endpoint, requestInit);
      if (!response.ok) {
        throw new Error(`Plugin command execution failed: ${response.status}`);
      }
      return await response.json();
    },
  };
}

export async function buildV1CapabilitySnapshot(options: {
  fetchJson: () => Promise<V1RuntimeDiscovery>;
  callingSurface: string;
  assistantSettings: Record<string, unknown>;
  requireReadApproval?: boolean;
  browserCapabilities?: string[];
  now?: () => string;
}): Promise<V1CapabilitySnapshot> {
  return (await buildV1RuntimeContext(options)).capabilitySnapshot;
}

export async function buildV1RuntimeContext(options: {
  fetchJson: () => Promise<V1RuntimeDiscovery>;
  callingSurface: string;
  assistantSettings: Record<string, unknown>;
  requireReadApproval?: boolean;
  browserCapabilities?: string[];
  now?: () => string;
}): Promise<V1RuntimeContext> {
  const runtime = await options.fetchJson();
  const browserCapabilities = options.browserCapabilities || V1_BROWSER_CAPABILITIES;
  const grammar = backendGrammarDiscovery(runtime.grammar, browserCapabilities);
  const snapshot: V1CapabilitySnapshot = {
    snapshot_id: `snap_${uuidv4()}`,
    captured_at: options.now ? options.now() : new Date().toISOString(),
    schema_version: 'v1',
    plugin_version: runtime.plugin_version || 'unknown',
    site_identity: runtime.site_identity || {},
    calling_surface: options.callingSurface,
    assistant_settings: backendAssistantSettings(options.assistantSettings),
    grammar,
  };
  return {
    capabilitySnapshot: snapshot,
    approvalPolicy: {
      requireReadApproval: Boolean(options.requireReadApproval),
    },
  };
}

function backendGrammarDiscovery(
  source: V1GrammarDiscovery,
  browserCapabilities: string[],
): V1CapabilitySnapshotGrammar {
  return {
    interface_version: source.interface_version,
    capabilities: mergeCapabilities(source.capabilities, browserCapabilities),
    ...(source.site_shape ? { site_shape: source.site_shape } : {}),
    ...(source.settings_rev ? { settings_rev: source.settings_rev } : {}),
  };
}

function backendAssistantSettings(source: Record<string, unknown>): Record<string, unknown> {
  const settings: Record<string, unknown> = {};
  if ('allow_log_reads' in source) settings.allow_log_reads = Boolean(source.allow_log_reads);
  if ('allow_write_operations' in source) settings.allow_write_operations = Boolean(source.allow_write_operations);
  return settings;
}

function mergeCapabilities(primary: string[] = [], additions: string[] = []): string[] {
  const merged: string[] = [];
  const seen = new Set<string>();
  for (const capability of [...primary, ...additions]) {
    if (typeof capability !== 'string' || !capability.trim() || seen.has(capability)) continue;
    seen.add(capability);
    merged.push(capability);
  }
  return merged;
}

export async function executeV1CommandPause(
  pause: V1CommandPauseEvent,
  client: V1CommandClient,
  options: V1CommandExecutionOptions = {},
): Promise<V1ResumePayload> {
  const runtimeOptions: V1CommandRuntimeOptions = {
    commandClient: client,
    approvalPolicy: options.approvalPolicy,
    requestApproval: options.requestApproval,
    commandDispatchTimeoutMs: options.commandDispatchTimeoutMs,
  };
  const prepared = await prepareGroupedCommandsForExecution(pause.command_batch, runtimeOptions);
  const command_results = needsLocalBatchState(pause.command_batch.commands, prepared)
    ? await executeCommandsWithLocalBatchState(pause.command_batch, prepared, runtimeOptions)
    : await executePluginOnlyCommands(pause.command_batch, prepared, runtimeOptions);

  return {
    pause_id: pause.pause_id,
    command_results,
  };
}

export async function executeV1PauseEvent(
  pause: V1PauseEvent,
  options: V1CommandRuntimeOptions,
): Promise<V1PauseResumePayload> {
  return executeV1CommandPauseWithEvents(pause, options);
}

async function executeV1CommandPauseWithEvents(
  pause: V1CommandPauseEvent,
  options: V1CommandRuntimeOptions,
): Promise<V1ResumePayload> {
  for (const command of pause.command_batch.commands) {
    const labels = commandLifecycleLabels(command);
    emitRuntimeEvent(options, {
      type: 'command_started',
      id: command.command_id,
      batch_id: pause.command_batch.id,
      label: labels.started,
      target: labels.target,
    });
  }

  const prepared = await prepareGroupedCommandsForExecution(pause.command_batch, options);
  const command_results = needsLocalBatchState(pause.command_batch.commands, prepared)
    ? await executeCommandsWithLocalBatchState(pause.command_batch, prepared, options)
    : await executePluginOnlyCommands(pause.command_batch, prepared, options);

  for (const command of pause.command_batch.commands) {
    const result = command_results.find(item => item.command_id === command.command_id);
    emitRuntimeEvent(options, {
      type: 'command_completed',
      id: command.command_id,
      batch_id: pause.command_batch.id,
      ok: result?.status === 'succeeded',
      summary: commandResultSummary(command, result),
    });
  }

  return {
    pause_id: pause.pause_id,
    command_results,
  };
}

async function executePluginOnlyCommands(
  batch: V1CommandPauseEvent['command_batch'],
  prepared: PreparedCommandExecution,
  options: V1CommandRuntimeOptions,
): Promise<V1CommandResult[]> {
  const response = await executePreparedCommands(
    batch.id,
    prepared.commands,
    options.commandClient || missingCommandClient(),
    options.commandDispatchTimeoutMs,
  );
  return applyGroupedAfterExecuteApprovals(
    batch,
    commandResultsForPause(batch.commands, prepared, response),
    options,
  );
}

async function executeCommandsWithLocalBatchState(
  batch: V1CommandPauseEvent['command_batch'],
  prepared: PreparedCommandExecution,
  options: V1CommandRuntimeOptions,
): Promise<V1CommandResult[]> {
  const executableByCommandId = new Map(prepared.commands.map(command => [command.command_id, command]));
  const commandClient = options.commandClient || missingCommandClient();
  const browserRunner = options.browserCommandRunner || missingBrowserCommandRunner();

  const commandResults = await runV1CommandBatch(batch.commands, {
    initialResults: prepared.terminalResults.values(),
    executeOne: async command => {
      const executableCommand = executableByCommandId.get(command.command_id);
      if (!executableCommand) {
        return missingCommandResult(command.command_id);
      }
      const resolvedCommand: V1CommandExecutionCommand = {
        ...executableCommand,
        input: command.input,
      };
      return isBrowserCapability(resolvedCommand.capability)
        ? executeOneBrowserCommand(resolvedCommand, browserRunner)
        : executeOnePluginCommand(batch.id, resolvedCommand, commandClient, options.commandDispatchTimeoutMs);
    },
  });

  return applyGroupedAfterExecuteApprovals(
    batch,
    commandResults,
    options,
  );
}

async function prepareGroupedCommandsForExecution(
  batch: V1CommandPauseEvent['command_batch'],
  options: V1CommandRuntimeOptions,
): Promise<PreparedCommandExecution> {
  const executable: V1CommandExecutionCommand[] = [];
  const terminalResults = new Map<string, V1CommandResult>();
  const decisions = new Map<string, V1CommandApprovalDecision>();
  const preflight = await preflightBatchCommands(batch, options);
  const approvalGroups = groupedPreflightApprovalRequests(batch, preflight);

  for (const command of batch.commands) {
    const preflightDecision = preflight.get(command.command_id) || {
      requiresApproval: false,
    };
    if (preflightDecision.result && preflightDecision.result.status !== 'succeeded') {
      terminalResults.set(command.command_id, preflightDecision.result);
      continue;
    }
    if (!preflightDecision.requiresApproval) {
      executable.push(commandForExecution(command));
      continue;
    }

    const approvalKey = approvalGroupKey(command, 'before_execute');
    let decision = decisions.get(approvalKey);
    if (!decision) {
      const approvalRequest = approvalGroups.get(approvalKey) || singleCommandApprovalRequest(command, 'before_execute', batch.id);
      const approvalDisplay = approvalRequest.display;
      emitRuntimeEvent(options, {
        type: 'approval_needed',
        id: approvalKey,
        batch_id: batch.id,
        label: approvalDisplay.label,
        target: approvalDisplay.target,
      });
      decision = await approvalDecisionForGroup(approvalRequest, options, 'before_execute');
      decisions.set(approvalKey, decision);
      emitRuntimeEvent(options, {
        type: decision.approved ? 'approval_granted' : 'approval_rejected',
        id: approvalKey,
        batch_id: batch.id,
        label: approvalDisplay.label,
        target: approvalDisplay.target,
      });
    }

    if (!decision.approved) {
      terminalResults.set(command.command_id, rejectedCommandResult(command.command_id, decision.reason));
      continue;
    }

    executable.push(commandForExecution(command, decision.approval || { approved: true }));
  }

  return { commands: executable, terminalResults };
}

async function preflightBatchCommands(
  batch: V1CommandPauseEvent['command_batch'],
  options: V1CommandRuntimeOptions,
): Promise<Map<string, V1PreflightDecision>> {
  const client = options.commandClient;
  const pluginCommands = batch.commands.filter(command => (
    !isBrowserCapability(command.capability) && isBeforeExecutePreflightCandidate(command)
  ));
  if (!client || pluginCommands.length < 1) {
    return new Map();
  }

  const response = await executePreparedCommands(
    `${batch.id}:validate`,
    pluginCommands.map(commandForPreflight),
    client,
    options.commandDispatchTimeoutMs,
  );
  const results = new Map((response.results || []).map(result => [result.command_id, result]));
  const decisions = new Map<string, V1PreflightDecision>();
  for (const command of pluginCommands) {
    const result = results.get(command.command_id) || missingCommandResult(command.command_id);
    decisions.set(command.command_id, {
      result,
      requiresApproval: result.status === 'succeeded'
        ? preflightRequiresApproval(result)
        : false,
    });
  }
  return decisions;
}

function groupedPreflightApprovalRequests(
  batch: V1CommandPauseEvent['command_batch'],
  preflight: Map<string, V1PreflightDecision>,
): Map<string, V1CommandApprovalRequest> {
  const approvalGroups = new Map<string, V1CommandApprovalRequest>();
  for (const command of batch.commands) {
    const decision = preflight.get(command.command_id);
    const requiresApproval = decision?.result?.status === 'succeeded' && decision.requiresApproval;
    if (!requiresApproval) continue;
    const key = approvalGroupKey(command, 'before_execute');
    const existing = approvalGroups.get(key);
    if (existing) {
      existing.commands.push(command);
      approvalGroups.set(key, approvalRequestForCommands(existing.commands, [], 'before_execute', batch.id));
      continue;
    }
    approvalGroups.set(key, singleCommandApprovalRequest(command, 'before_execute', batch.id));
  }
  return approvalGroups;
}

function isBeforeExecutePreflightCandidate(command: V1PluginCommand): boolean {
  return command.capability === 'wp.http.request' || !isReadApprovalCapability(command.capability);
}

async function applyGroupedAfterExecuteApprovals(
  batch: V1CommandPauseEvent['command_batch'],
  commandResults: V1CommandResult[],
  options: V1CommandRuntimeOptions,
): Promise<V1CommandResult[]> {
  const nextResults = [...commandResults];
  const decisions = new Map<string, V1CommandApprovalDecision>();
  const approvalGroups = groupedAfterExecuteApprovalRequests(batch, options.approvalPolicy, commandResults);
  const resultsByCommandId = new Map(commandResults.map(result => [result.command_id, result]));

  for (const command of batch.commands) {
    if (!requiresReadSharingApproval(command, resultsByCommandId.get(command.command_id), options.approvalPolicy)) continue;

    const approvalKey = approvalGroupKey(command, 'after_execute');
    let decision = decisions.get(approvalKey);
    if (!decision) {
      const approvalRequest = approvalGroups.get(approvalKey) || singleCommandApprovalRequest(command, 'after_execute', batch.id);
      const approvalDisplay = approvalRequest.display;
      emitRuntimeEvent(options, {
        type: 'approval_needed',
        id: approvalKey,
        batch_id: batch.id,
        label: approvalDisplay.label,
        target: approvalDisplay.target,
      });
      decision = await approvalDecisionForGroup(approvalRequest, options, 'after_execute');
      decisions.set(approvalKey, decision);
      emitRuntimeEvent(options, {
        type: decision.approved ? 'approval_granted' : 'approval_rejected',
        id: approvalKey,
        batch_id: batch.id,
        label: approvalDisplay.label,
        target: approvalDisplay.target,
      });
    }

    if (!decision.approved) {
      const resultIndex = nextResults.findIndex(result => result.command_id === command.command_id);
      if (resultIndex >= 0) {
        nextResults[resultIndex] = rejectedCommandResult(command.command_id, decision.reason);
      }
    }
  }

  return nextResults;
}

function groupedAfterExecuteApprovalRequests(
  batch: V1CommandPauseEvent['command_batch'],
  approvalPolicy?: V1CommandApprovalPolicy,
  commandResults: V1CommandResult[] = [],
): Map<string, V1CommandApprovalRequest> {
  const groups = new Map<string, V1PluginCommand[]>();
  const resultsByCommandId = new Map(commandResults.map(result => [result.command_id, result]));
  for (const command of batch.commands) {
    const requiresApproval = requiresReadSharingApproval(
      command,
      resultsByCommandId.get(command.command_id),
      approvalPolicy,
    );
    if (!requiresApproval) continue;
    const key = approvalGroupKey(command, 'after_execute');
    const groupedCommands = groups.get(key);
    if (groupedCommands) {
      groupedCommands.push(command);
    } else {
      groups.set(key, [command]);
    }
  }

  return new Map(
    Array.from(groups.entries()).map(([key, groupedCommands]) => [
      key,
      approvalRequestForCommands(
        groupedCommands,
        commandResultsForCommands(groupedCommands, commandResults),
        'after_execute',
        batch.id,
      ),
    ])
  );
}

function needsLocalBatchState(
  commands: V1PluginCommand[],
  prepared: PreparedCommandExecution,
): boolean {
  return prepared.terminalResults.size > 0 || commands.some(command => isBrowserCapability(command.capability));
}

function approvalGroupKey(command: V1PluginCommand, timing: 'before_execute' | 'after_execute'): string {
  return approvalPromptForCommand(command, timing).group || command.command_id;
}

function singleCommandApprovalRequest(
  command: V1PluginCommand,
  timing: 'before_execute' | 'after_execute',
  batchId?: string,
): V1CommandApprovalRequest {
  return approvalRequestForCommands([command], [], timing, batchId);
}

function approvalRequestForCommands(
  commands: V1PluginCommand[],
  results: V1CommandResult[] = [],
  timing: 'before_execute' | 'after_execute',
  batchId?: string,
): V1CommandApprovalRequest {
  const first = commands[0];
  const approval = approvalPromptForCommand(first, timing);
  const request: V1CommandApprovalRequest = {
    ...(batchId ? { batch_id: batchId } : {}),
    approval,
    commands,
    display: {
      label: approval.action || first.display?.label || 'Approve command',
      target: displayTargets(commands),
      summary: stringValue(approval.summary) || first.display?.summary,
    },
  };
  if (results.length > 0) {
    request.results = results;
  }
  return request;
}

function displayTargets(commands: V1PluginCommand[]): string | undefined {
  const targets = Array.from(new Set(
    commands
      .map(command => command.display?.target)
      .filter((target): target is string => Boolean(target))
  ));
  return targets.length ? targets.join(', ') : undefined;
}

function commandResultsForPause(
  commands: V1PluginCommand[],
  prepared: PreparedCommandExecution,
  response: V1CommandExecutionResponse,
): V1CommandResult[] {
  const resultsByCommandId = commandResultsByCommandId(response.results || []);
  return commands.map(command => (
    prepared.terminalResults.get(command.command_id)
    || takeCommandResult(resultsByCommandId, command.command_id)
    || missingCommandResult(command.command_id)
  ));
}

function commandResultsForCommands(
  commands: V1PluginCommand[],
  commandResults: V1CommandResult[],
): V1CommandResult[] {
  const resultsByCommandId = commandResultsByCommandId(commandResults);
  return commands
    .map(command => takeCommandResult(resultsByCommandId, command.command_id))
    .filter((result): result is V1CommandResult => Boolean(result));
}

function commandResultsByCommandId(commandResults: V1CommandResult[]): Map<string, V1CommandResult[]> {
  const resultsByCommandId = new Map<string, V1CommandResult[]>();
  for (const result of commandResults) {
    const existing = resultsByCommandId.get(result.command_id);
    if (existing) {
      existing.push(result);
    } else {
      resultsByCommandId.set(result.command_id, [result]);
    }
  }
  return resultsByCommandId;
}

function takeCommandResult(
  resultsByCommandId: Map<string, V1CommandResult[]>,
  commandId: string,
): V1CommandResult | undefined {
  return resultsByCommandId.get(commandId)?.shift();
}

function isBrowserCapability(capability: string): boolean {
  return capability.startsWith('browser.');
}

async function executeOnePluginCommand(
  batchId: string,
  command: V1CommandExecutionCommand,
  client: V1CommandClient,
  timeoutMs?: number,
): Promise<V1CommandResult> {
  const dispatchCommand = commandForSinglePluginDispatch(command);
  const response = await executePreparedCommands(batchId, [dispatchCommand], client, timeoutMs);
  return response.results?.find(result => result.command_id === command.command_id)
    || missingCommandResult(command.command_id);
}

function commandForSinglePluginDispatch(command: V1CommandExecutionCommand): V1CommandExecutionCommand {
  const dispatchCommand = { ...command };
  delete dispatchCommand.depends_on;
  return dispatchCommand;
}

async function executeOneBrowserCommand(
  command: V1CommandExecutionCommand,
  runner: V1BrowserCommandRunner,
): Promise<V1CommandResult> {
  try {
    const result = await runner.runBrowserCommand(command);
    return {
      ...result,
      command_id: command.command_id,
      capability: command.capability,
      artifacts: result.artifacts || [],
    };
  } catch (error) {
    return {
      command_id: command.command_id,
      capability: command.capability,
      status: 'failed',
      artifacts: [],
      error: {
        code: 'browser_command_failed',
        message: error instanceof Error ? error.message : String(error),
      },
    };
  }
}

function requiresReadSharingApproval(
  command: V1PluginCommand,
  result: V1CommandResult | undefined,
  approvalPolicy?: V1CommandApprovalPolicy,
): boolean {
  return approvalPolicy?.requireReadApproval === true
    && isReadApprovalCapability(command.capability)
    && result?.status === 'succeeded';
}

// Builds Frontend prompt grouping and copy after Plugin preflight or local read policy
// has already determined that approval is required.
function approvalPromptForCommand(
  command: V1PluginCommand,
  timing: 'before_execute' | 'after_execute',
): V1CommandApprovalRequirement {
  const group = approvalGroupForCommand(command, timing);
  const summary = command.display?.summary;
  if (timing === 'after_execute') {
    return {
      required: true,
      group,
      action: readApprovalAction(command),
      mode: 'after_execute',
      ...(summary ? { summary } : {}),
    };
  }

  return {
    required: true,
    group,
    action: group === 'content_mutation' ? 'Apply content changes' : group === 'site_mutation' ? 'Apply site changes' : command.display?.label || 'Approve command',
    ...(summary ? { summary } : {}),
  };
}

function approvalGroupForCommand(
  command: V1PluginCommand,
  timing: 'before_execute' | 'after_execute',
): string {
  if (timing === 'after_execute') {
    return isContentReadCapability(command.capability) ? 'content_read' : 'site_read';
  }
  if (command.display?.label === 'Review content changes') {
    return 'content_mutation';
  }
  if (isReadApprovalCapability(command.capability)) {
    return 'site_read';
  }
  return 'site_mutation';
}

function readApprovalAction(command: V1PluginCommand): string {
  if (isContentReadCapability(command.capability)) return 'Read page content';
  if (command.capability === 'browser.page.get') return 'Analyze rendered page';
  if (command.capability === 'wp.http.request') return 'Run site request';
  if (command.capability === 'wp.performance.inspect') return 'Inspect page performance';
  return 'Read site data';
}

function isReadApprovalCapability(capability: string): boolean {
  return capability.endsWith('.get')
    || capability.endsWith('.query')
    || capability === 'wp.http.request'
    || capability === 'wp.performance.inspect'
    || capability === 'browser.page.get';
}

async function executePreparedCommands(
  batchId: string,
  commands: V1CommandExecutionCommand[],
  client: V1CommandClient,
  timeoutMs = DEFAULT_COMMAND_DISPATCH_TIMEOUT_MS,
): Promise<V1CommandExecutionResponse> {
  if (commands.length < 1) {
    return { results: [] };
  }

  const request = commandExecutionRequestFromCommands(batchId, commands);
  const abortController = canAbortCommandDispatch(timeoutMs) ? new AbortController() : null;
  const dispatchPromise = abortController && client.supportsAbortSignal === true
    ? client.executeCommands(request, { signal: abortController.signal })
    : client.executeCommands(request);
  try {
    return await withCommandDispatchTimeout(
      dispatchPromise,
      timeoutMs,
      () => ({
        results: commands.map(command => commandDispatchErrorResult(
          command,
          'plugin_command_dispatch_timeout',
          `Plugin command dispatch timed out after ${timeoutMs}ms. Command outcome is unknown; inspect the site before retrying mutating work.`,
        )),
      }),
      () => abortController?.abort(),
    );
  } catch (error) {
    return {
      results: commands.map(command => commandDispatchErrorResult(
        command,
        'plugin_command_dispatch_failed',
        `Plugin command dispatch failed before command outcomes were confirmed; inspect the site before retrying mutating work: ${error instanceof Error ? error.message : String(error)}`,
      )),
    };
  }
}

function canAbortCommandDispatch(timeoutMs: number): boolean {
  return Number.isFinite(timeoutMs) && timeoutMs > 0 && typeof AbortController !== 'undefined';
}

function withCommandDispatchTimeout<T>(
  promise: Promise<T>,
  timeoutMs: number,
  fallback: () => T,
  onTimeout?: () => void,
): Promise<T> {
  if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
    return promise;
  }
  return new Promise<T>((resolve, reject) => {
    let settled = false;
    const timer = window.setTimeout(() => {
      if (settled) return;
      settled = true;
      onTimeout?.();
      resolve(fallback());
    }, timeoutMs);
    promise.then(
      value => {
        if (settled) return;
        settled = true;
        window.clearTimeout(timer);
        resolve(value);
      },
      error => {
        if (settled) return;
        settled = true;
        window.clearTimeout(timer);
        reject(error);
      },
    );
  });
}

function commandDispatchErrorResult(
  command: V1CommandExecutionCommand,
  code: string,
  message: string,
): V1CommandResult {
  return {
    command_id: command.command_id,
    capability: command.capability,
    status: 'failed',
    error: { code, message },
  };
}

async function approvalDecisionForGroup(
  request: V1CommandApprovalRequest,
  options: V1CommandRuntimeOptions,
  timing: 'before_execute' | 'after_execute' = 'before_execute',
): Promise<V1CommandApprovalDecision> {
  if (options.requestApproval) {
    return options.requestApproval(request);
  }

  return {
    approved: false,
    reason: timing === 'after_execute'
      ? 'Approval is required before sharing this command result.'
      : 'Approval is required before this command can run.',
  };
}

function commandExecutionRequestFromCommands(
  batchId: string,
  commands: V1CommandExecutionCommand[],
): V1CommandExecutionRequest {
  return {
    batch_id: batchId,
    commands,
  };
}

function commandForExecution(command: V1PluginCommand, approval?: Record<string, unknown>): V1CommandExecutionCommand {
  const executionCommand: V1CommandExecutionCommand = {
    command_id: command.command_id,
    capability: command.capability,
    phase: command.phase,
    input: command.input,
  };
  if (command.depends_on) {
    executionCommand.depends_on = command.depends_on;
  }
  if (command.continue_after_failure === true) {
    executionCommand.continue_after_failure = true;
  }
  if (approval) {
    executionCommand.approval = approval;
  }
  return executionCommand;
}

function commandForPreflight(command: V1PluginCommand): V1CommandExecutionCommand {
  const preflightCommand = commandForExecution(command);
  delete preflightCommand.depends_on;
  preflightCommand.phase = 'validate';
  return preflightCommand;
}

function preflightRequiresApproval(result: V1CommandResult): boolean {
  if (!result.data || typeof result.data !== 'object') return true;
  const approval = (result.data as { approval?: unknown }).approval;
  if (!approval || typeof approval !== 'object') return true;
  return (approval as { required?: unknown }).required === true;
}

function missingCommandResult(commandId: string): V1CommandResult {
  return {
    command_id: commandId,
    status: 'failed',
    error: {
      code: 'missing_command_result',
      message: 'Plugin command endpoint did not return a terminal result for this command.',
    },
  };
}

function rejectedCommandResult(commandId: string, reason?: string): V1CommandResult {
  return {
    command_id: commandId,
    status: 'rejected',
    data: {
      status: 'rejected',
      user_reason: reason || 'User rejected this command.',
    },
  };
}

function emitRuntimeEvent(options: V1CommandRuntimeOptions, event: V1CommandRuntimeEvent): void {
  options.emitEvent?.(event);
}

function commandResultSummary(command: V1PluginCommand, result?: V1CommandResult): string | undefined {
  if (!result) return undefined;
  const labels = commandLifecycleLabels(command);
  if (result.status === 'succeeded') return labels.completed;
  if (result.status === 'denied' || result.status === 'rejected' || result.status === 'skipped') {
    return labels.skipped;
  }
  if (result.error?.message) return `${labels.failed}: ${result.error.message}`;
  if (result.error?.code) return `${labels.failed}: ${result.error.code}`;
  if (result.policy?.decision && result.policy.decision !== 'allowed') {
    return `${labels.failed}: ${result.policy.reason_codes?.[0] || result.policy.decision}`;
  }
  return labels.failed;
}

function commandLifecycleLabels(command: V1PluginCommand): V1LifecycleLabels {
  if (command.capability === 'wp.http.request') {
    const input = objectValue(command.input) || {};
    if (stringValue(input.check_type) || command.display?.label === 'Verify site change') {
      return actionLabels('Checking page response', 'Checked page response', 'check page response', command.display?.target);
    }
    return actionLabels('Fetching page response', 'Fetched page response', 'fetch page response', command.display?.target);
  }

  if (isContentReadCapability(command.capability)) {
    const target = contentReadTarget(command.input, command.display?.target);
    return actionLabels(
      `Reading ${target} content`,
      `Read ${target} content`,
      `read ${target} content`,
      command.display?.target,
    );
  }

  const siteReadResource = siteReadResourceFromCommand(command);
  if (siteReadResource) {
    const label = siteReadResourceLabel(siteReadResource);
    return actionLabels(`Reading ${label}`, `Read ${label}`, `read ${label}`, command.display?.target);
  }

  const siteWriteLabels = siteWriteLifecycleLabels(command);
  if (siteWriteLabels) return siteWriteLabels;

  const fallback = humanizeCapability(command.capability);
  return actionLabels(`Running ${fallback}`, `Ran ${fallback}`, `run ${fallback}`, command.display?.target);
}

function actionLabels(started: string, completed: string, action: string, target?: string): V1LifecycleLabels {
  return {
    started,
    completed,
    failed: `Failed to ${action}`,
    skipped: `Skipped ${action}`,
    target,
  };
}

function siteReadResourceFromCommand(command: V1PluginCommand): string | undefined {
  const input = objectValue(command.input) || {};
  const resource = stringValue(input.resource);
  if (resource) return resource;
  const resourcesByCapability: Record<string, string> = {
    'wp.posts.query': 'posts',
    'wp.media.query': 'media',
    'wp.terms.query': 'terms',
    'wp.post_types.query': 'post_types',
    'wp.taxonomies.query': 'taxonomies',
    'wp.block_patterns.query': 'block_patterns',
    'wp.template_parts.query': 'template_parts',
    'wp.block_types.query': 'block_types',
    'wp.shortcodes.query': 'shortcodes',
    'wp.options.query': 'options',
    'wp.plugins.query': 'plugins',
    'wp.themes.get': 'theme',
    'wp.global_styles.get': 'global_styles',
    'wp.templates.query': 'templates',
    'wp.runtime.get': 'runtime',
    'wp.server.get': 'server',
    'wp.site_health.get': 'site_health',
    'wp.performance.inspect': 'performance',
    'wp.rest_routes.query': 'rest_routes',
    'wp.hooks.query': 'hooks',
    'wp.files.query': 'files',
    'wp.transients.query': 'transients',
    'wp.users.query': 'users',
    'wp.logs.query': 'logs',
  };
  return resourcesByCapability[command.capability];
}

function siteReadResourceLabel(resource: string): string {
  const labels: Record<string, string> = {
    posts: 'posts and pages',
    media: 'media library',
    terms: 'taxonomy terms',
    post_types: 'post types',
    taxonomies: 'taxonomies',
    block_patterns: 'block patterns',
    template_parts: 'template parts',
    block_types: 'block types',
    shortcodes: 'shortcodes',
    dynamic_blocks: 'dynamic blocks',
    options: 'WordPress settings',
    plugins: 'plugin information',
    theme: 'theme information',
    global_styles: 'global styles',
    templates: 'site templates',
    runtime: 'WordPress runtime',
    server: 'server details',
    site_health: 'Site Health results',
    performance: 'performance diagnostics',
    rest_routes: 'REST routes',
    hooks: 'WordPress hooks',
    files: 'file snippet',
    transients: 'transients',
    commerce: 'commerce data',
    users: 'user summaries',
    logs: 'debug logs',
  };
  return labels[resource] || titleCase(resource);
}

function isContentReadCapability(capability: string): boolean {
  return capability === 'wp.posts.get'
    || capability === 'wp.template_parts.get';
}

function contentReadTarget(input: Record<string, unknown>, fallback?: string): string {
  const source = objectValue(input.source);
  const kind = stringValue(source?.kind) || 'post';
  const id = stringValue(source?.id);
  if (id) return `${titleCase(kind).toLowerCase()} ${id}`;
  return fallback || 'selected';
}

function siteWriteLifecycleLabels(command: V1PluginCommand): V1LifecycleLabels | undefined {
  const opName = siteWriteOperationFromCapability(command.capability) || command.display?.target;
  const input = objectValue(command.input) || {};
  const values = objectValue(input.values) || {};
  const fields = objectValue(values.fields) || values;
  const target = command.display?.target;

  if (opName === 'post.create') {
    const postType = stringValue(fields.post_type) || 'content';
    const status = stringValue(fields.post_status ?? fields.status);
    const createdThing = status === 'draft' ? `draft ${postType}` : postType;
    return actionLabels(`Creating ${createdThing}`, `Created ${createdThing}`, `create ${createdThing}`, target);
  }
  if (opName === 'post.update') {
    return actionLabels('Updating post settings', 'Updated post settings', 'update post settings', target);
  }
  if (opName === 'option.patch') {
    return actionLabels('Updating WordPress settings', 'Updated WordPress settings', 'update WordPress settings', target);
  }
  if (opName === 'cache.clear') {
    return actionLabels('Clearing caches', 'Cleared caches', 'clear caches', target);
  }
  if (opName === 'rewrite.flush') {
    return actionLabels('Refreshing rewrite rules', 'Refreshed rewrite rules', 'refresh rewrite rules', target);
  }
  if (opName === 'maintenance.run') {
    return actionLabels('Running maintenance', 'Ran maintenance', 'run maintenance', target);
  }
  if (opName === 'post_type.create') {
    return actionLabels('Creating custom post type', 'Created custom post type', 'create custom post type', target);
  }
  if (opName === 'post_type.update') {
    return actionLabels('Updating custom post type', 'Updated custom post type', 'update custom post type', target);
  }
  return undefined;
}

function siteWriteOperationFromCapability(capability: string): string | undefined {
  const operationsByCapability: Record<string, string> = {
    'wp.rewrite_rules.flush': 'rewrite.flush',
    'wp.options.update': 'option.patch',
    'wp.cache.clear': 'cache.clear',
    'wp.cron.run': 'maintenance.run',
    'wp.posts.create': 'post.create',
    'wp.posts.update': 'post.update',
    'wp.post_types.create': 'post_type.create',
    'wp.post_types.update': 'post_type.update',
  };
  return operationsByCapability[capability];
}

function humanizeCapability(capability: string): string {
  return capability.replace(/^wp\./, '').replace(/[._-]+/g, ' ').trim() || 'command';
}

function missingCommandClient(): V1CommandClient {
  return {
    executeCommands: async (request: V1CommandExecutionRequest) => ({
      results: request.commands.map(command => ({
        command_id: command.command_id,
        status: 'unavailable' as const,
        error: {
          code: 'plugin_command_client_unavailable',
          message: 'Plugin command execution is unavailable in this Frontend runtime.',
        },
      })),
    }),
  };
}

function missingBrowserCommandRunner(): V1BrowserCommandRunner {
  return {
    runBrowserCommand: async command => ({
      status: 'unsupported' as const,
      artifacts: [],
      error: {
        code: 'browser_command_runner_unavailable',
        message: `Browser command execution is unavailable for ${command.capability}.`,
      },
    }),
  };
}

function stringValue(value: unknown): string | undefined {
  return typeof value === 'string' && value.trim() ? value : undefined;
}

function objectValue(value: unknown): Record<string, unknown> | undefined {
  return value !== null && typeof value === 'object' && !Array.isArray(value)
    ? value as Record<string, unknown>
    : undefined;
}

function titleCase(value: string): string {
  return value
    .replace(/[._-]+/g, ' ')
    .replace(/\s+/g, ' ')
    .trim()
    .replace(/\b\w/g, char => char.toUpperCase());
}
