'use strict';

const { createAgentRunId, normalizeAgentMode, normalizeAgentRunResult } = require('../agent-runners/contract');
const { createDefaultHarnessRegistry, selectAgentHarness } = require('./harness');
const { createDefaultRuntimeRegistry } = require('./registry');
const { defaultSessionStore } = require('./session-store');
const { WorkerBootTracker, WorkerEvent, classifyFailure } = require('./worker-boot');
const {
  attachTraceToResult,
  createExecutionTrace,
  finalizeTrace,
  recordAttemptFinish,
  recordAttemptStart,
  recordSelection,
} = require('../runtime/execution-trace');

function normalizeAgentTaskRequest(input = {}) {
  const task = input.task || input.prompt || '';
  return {
    id: input.id || input.runId || createAgentRunId(),
    parentRunId: input.parentRunId || input.parentId || null,
    harnessId: input.harnessId || null,
    runtime: input.runtime || input.runtimePreference || 'cli-agent',
    runnerId: input.runnerId || input.runner || input.agent || 'claude-code',
    providerType: input.providerType || input.provider || 'unknown',
    model: input.model || null,
    mode: normalizeAgentMode(input.mode),
    task,
    prompt: task,
    cwd: input.cwd || process.cwd(),
    writeScope: Array.isArray(input.writeScope) ? input.writeScope : (input.writeScope ? [input.writeScope] : ['.']),
    sandbox: input.sandbox || null,
    timeoutMs: input.timeoutMs || 300000,
    sessionId: input.sessionId || null,
    persistent: Boolean(input.persistent),
    cleanupPolicy: input.cleanupPolicy || (input.persistent ? 'manual' : 'delete-on-complete'),
    threadId: input.threadId || null,
    contextPolicy: input.contextPolicy || null,
    attachments: Array.isArray(input.attachments) ? input.attachments : [],
    reviewPolicy: input.reviewPolicy || null,
    metadata: { ...(input.metadata || {}) },
  };
}

async function spawnAgentSession(input = {}, deps = {}) {
  const request = normalizeAgentTaskRequest(input);
  const trace = deps.trace || createExecutionTrace({
    runId: request.id,
    parentRunId: request.parentRunId,
    mode: request.mode,
    provider: request.providerType,
    model: request.model,
    runnerId: request.runnerId,
    harnessId: request.harnessId,
    sessionId: request.sessionId || '',
    cwd: request.cwd,
    sandbox: request.sandbox,
    taskType: 'agent-runtime',
  });
  if (deps.harnessRegistry || request.harnessId) {
    const harnessRegistry = deps.harnessRegistry || createDefaultHarnessRegistry();
    const selection = selectAgentHarness(request, [], {
      registry: harnessRegistry,
      providerAvailability: deps.providerAvailability,
    });
    if (!selection.selected) {
      const error = `No supported agent harness found for ${request.harnessId || request.runnerId}`;
      recordSelection(trace, selection);
      finalizeTrace(trace, { status: 'failed', error });
      return normalizeAgentRunResult({
        runId: request.id,
        parentRunId: request.parentRunId,
        runnerId: request.runnerId,
        providerType: request.providerType,
        mode: request.mode,
        sessionId: request.sessionId,
        success: false,
        stderr: error,
        error,
        trace,
      });
    }
    recordSelection(trace, selection);
    request.harnessId = selection.selected.id;
    request.runnerId = selection.selected.runnerId;
    request.providerType = selection.selected.providerType;
    request.metadata = { ...request.metadata, harnessSelection: selection.reason };
  }
  const sessionStore = deps.sessionStore || defaultSessionStore;
  const registry = deps.registry || createDefaultRuntimeRegistry({ runTask: deps.runTask });
  const runtime = registry.getRuntime(request.runtime);
  if (!runtime) {
    const error = `Unknown agent runtime: ${request.runtime}`;
    return normalizeAgentRunResult({
      runId: request.id,
      parentRunId: request.parentRunId,
      runnerId: request.runnerId,
      providerType: request.providerType,
      mode: request.mode,
      sessionId: request.sessionId,
      success: false,
      stderr: error,
      error,
    });
  }

  const existingSession = request.sessionId ? sessionStore.getSession(request.sessionId) : null;
  const session = existingSession || sessionStore.createSession({
    id: request.sessionId,
    parentRunId: request.parentRunId,
    runtime: request.runtime,
    runnerId: request.runnerId,
    harnessId: request.harnessId,
    providerType: request.providerType,
    model: request.model,
    mode: request.mode,
    cwd: request.cwd,
    sandbox: request.sandbox,
    task: request.task,
    persistent: request.persistent,
    cleanupPolicy: request.cleanupPolicy,
    threadId: request.threadId,
    metadata: request.metadata,
  });

  sessionStore.markRunning(session.id, {
    parentRunId: request.parentRunId,
    runtime: request.runtime,
    runnerId: request.runnerId,
    providerType: request.providerType,
    model: request.model,
    mode: request.mode,
    cwd: request.cwd,
    sandbox: request.sandbox,
    task: request.task,
  });

  const workerBoot = deps.workerBoot || new WorkerBootTracker({
    sessionId: session.id,
    runnerId: request.runnerId,
    providerType: request.providerType,
    model: request.model || '',
    cwd: request.cwd,
  });
  workerBoot.observe({ type: WorkerEvent.SPAWNING });
  workerBoot.observe({ type: WorkerEvent.READY_FOR_PROMPT });
  workerBoot.observe({ type: WorkerEvent.PROMPT_SENT, data: { taskLength: request.task.length } });
  trace.workerBoot = workerBoot.snapshot();

  trace.sessionId = session.id;
  trace.request.runnerId = request.runnerId;
  trace.request.harnessId = request.harnessId;
  trace.request.modelPreference = request.model || '';
  const startedAt = Date.now();
  const attempt = recordAttemptStart(trace, {
    providerType: request.providerType,
    model: request.model,
    runnerId: request.runnerId,
    harnessId: request.harnessId,
  });

  try {
    const raw = await runtime.run({
      ...request,
      sessionId: session.id,
      resumeHandle: session.resumeHandle,
    });
    if (raw?.success === false) {
      workerBoot.observe({
        type: WorkerEvent.FAILED,
        error: raw.error || raw.stderr || 'Agent run failed',
        failureKind: classifyFailure(raw),
      });
    } else {
      workerBoot.observe({ type: WorkerEvent.PROMPT_ACCEPTED });
      workerBoot.observe({ type: WorkerEvent.FINISHED });
    }
    trace.workerBoot = workerBoot.snapshot();
    const completedSession = sessionStore.markCompleted(session.id, raw || {}) || sessionStore.getSession(session.id);
    recordAttemptFinish(trace, attempt.id, {
      success: raw?.success !== false,
      status: raw?.success === false ? 'failed' : 'completed',
      error: raw?.error || raw?.stderr || null,
      usage: raw?.usage || null,
    });
    trace.latencyMs = Date.now() - startedAt;
    finalizeTrace(trace, { status: raw?.success === false ? 'failed' : 'completed' });

    const normalized = normalizeAgentRunResult(raw || {}, {
      runId: request.id,
      parentRunId: request.parentRunId,
      runnerId: request.runnerId,
      harnessId: request.harnessId,
      providerType: request.providerType,
      mode: request.mode,
      sessionId: session.id,
      persistent: completedSession?.persistent,
      cleanupPolicy: completedSession?.cleanupPolicy,
      threadId: completedSession?.threadId,
      sessionStatus: completedSession?.status,
      trace,
    });
    return attachTraceToResult(normalized, trace);
  } catch (err) {
    workerBoot.observe({ type: WorkerEvent.FAILED, error: err.message, failureKind: classifyFailure(err) });
    trace.workerBoot = workerBoot.snapshot();
    const failedSession = sessionStore.markFailed(session.id, err) || sessionStore.getSession(session.id);
    recordAttemptFinish(trace, attempt.id, { status: 'failed', error: err.message });
    trace.latencyMs = Date.now() - startedAt;
    finalizeTrace(trace, { status: 'failed', error: err.message });
    return normalizeAgentRunResult({
      runId: request.id,
      parentRunId: request.parentRunId,
      runnerId: request.runnerId,
      harnessId: request.harnessId,
      providerType: request.providerType,
      mode: request.mode,
      sessionId: session.id,
      persistent: failedSession?.persistent,
      cleanupPolicy: failedSession?.cleanupPolicy,
      threadId: failedSession?.threadId,
      sessionStatus: failedSession?.status,
      success: false,
      stderr: err.message,
      error: err.message,
      trace,
    });
  }
}

module.exports = {
  normalizeAgentTaskRequest,
  spawnAgentSession,
};
