/**
 * Builds RunAgentOptions for different execution phases.
 *
 * Centralizes the option construction logic that was previously
 * scattered across PieceEngine methods.
 */

import { join } from 'node:path';
import type { PieceMovement, PieceState, Language } from '../../models/types.js';
import type { RunAgentOptions } from '../../../agents/runner.js';
import type { PhaseRunnerContext } from '../phase-runner.js';
import type { PieceEngineOptions, PhaseName } from '../types.js';
import { buildSessionKey } from '../session-key.js';
import { resolveMovementProviderModel } from '../provider-resolution.js';

export class OptionsBuilder {
  constructor(
    private readonly engineOptions: PieceEngineOptions,
    private readonly getCwd: () => string,
    private readonly getProjectCwd: () => string,
    private readonly getSessionId: (persona: string) => string | undefined,
    private readonly getReportDir: () => string,
    private readonly getLanguage: () => Language | undefined,
    private readonly getPieceMovements: () => ReadonlyArray<{ name: string; description?: string }>,
    private readonly getPieceName: () => string,
    private readonly getPieceDescription: () => string | undefined,
  ) {}

  /** Build common RunAgentOptions shared by all phases */
  buildBaseOptions(step: PieceMovement): RunAgentOptions {
    const movements = this.getPieceMovements();
    const currentIndex = movements.findIndex((m) => m.name === step.name);
    const currentPosition = currentIndex >= 0 ? `${currentIndex + 1}/${movements.length}` : '?/?';
    const resolved = resolveMovementProviderModel({
      step,
      provider: this.engineOptions.provider,
      model: this.engineOptions.model,
      personaProviders: this.engineOptions.personaProviders,
    });

    return {
      cwd: this.getCwd(),
      abortSignal: this.engineOptions.abortSignal,
      personaPath: step.personaPath,
      provider: resolved.provider,
      model: resolved.model,
      permissionMode: step.permissionMode,
      language: this.getLanguage(),
      onStream: this.engineOptions.onStream,
      onPermissionRequest: this.engineOptions.onPermissionRequest,
      onAskUserQuestion: this.engineOptions.onAskUserQuestion,
      bypassPermissions: this.engineOptions.bypassPermissions,
      pieceMeta: {
        pieceName: this.getPieceName(),
        pieceDescription: this.getPieceDescription(),
        currentMovement: step.name,
        movementsList: movements,
        currentPosition,
      },
    };
  }

  /** Build RunAgentOptions for Phase 1 (main execution) */
  buildAgentOptions(step: PieceMovement): RunAgentOptions {
    // Phase 1: exclude Write from allowedTools when movement has output contracts AND edit is NOT enabled
    // (If edit is enabled, Write is needed for code implementation even if output contracts exist)
    // Note: edit defaults to undefined, so check !== true to catch both false and undefined
    const hasOutputContracts = step.outputContracts && step.outputContracts.length > 0;
    const allowedTools = hasOutputContracts && step.edit !== true
      ? step.allowedTools?.filter((t) => t !== 'Write')
      : step.allowedTools;

    // Skip session resume when cwd !== projectCwd (worktree execution) to avoid cross-directory contamination
    const shouldResumeSession = step.session !== 'refresh' && this.getCwd() === this.getProjectCwd();

    return {
      ...this.buildBaseOptions(step),
      sessionId: shouldResumeSession ? this.getSessionId(buildSessionKey(step)) : undefined,
      allowedTools,
      mcpServers: step.mcpServers,
    };
  }

  /** Build RunAgentOptions for session-resume phases (Phase 2, Phase 3) */
  buildResumeOptions(
    step: PieceMovement,
    sessionId: string,
    overrides: Pick<RunAgentOptions, 'allowedTools' | 'maxTurns'>,
  ): RunAgentOptions {
    return {
      ...this.buildBaseOptions(step),
      // Report/status phases are read-only regardless of movement settings.
      permissionMode: 'readonly',
      sessionId,
      allowedTools: overrides.allowedTools,
      maxTurns: overrides.maxTurns,
    };
  }

  /** Build RunAgentOptions for Phase 2 retry with a new session */
  buildNewSessionReportOptions(
    step: PieceMovement,
    overrides: Pick<RunAgentOptions, 'allowedTools' | 'maxTurns'>,
  ): RunAgentOptions {
    return {
      ...this.buildBaseOptions(step),
      permissionMode: 'readonly',
      allowedTools: overrides.allowedTools,
      maxTurns: overrides.maxTurns,
    };
  }

  /** Build PhaseRunnerContext for Phase 2/3 execution */
  buildPhaseRunnerContext(
    state: PieceState,
    lastResponse: string | undefined,
    updatePersonaSession: (persona: string, sessionId: string | undefined) => void,
    onPhaseStart?: (step: PieceMovement, phase: 1 | 2 | 3, phaseName: PhaseName, instruction: string) => void,
    onPhaseComplete?: (step: PieceMovement, phase: 1 | 2 | 3, phaseName: PhaseName, content: string, status: string, error?: string) => void,
  ): PhaseRunnerContext {
    return {
      cwd: this.getCwd(),
      reportDir: join(this.getCwd(), this.getReportDir()),
      language: this.getLanguage(),
      interactive: this.engineOptions.interactive,
      lastResponse,
      getSessionId: (persona: string) => state.personaSessions.get(persona),
      buildResumeOptions: this.buildResumeOptions.bind(this),
      buildNewSessionReportOptions: this.buildNewSessionReportOptions.bind(this),
      updatePersonaSession,
      onPhaseStart,
      onPhaseComplete,
    };
  }
}
