/**
 * Loop Runner
 *
 * Executes the Implement-Verify-Fix cycle for each plan step.
 *
 * For each step:
 * 1. Mark step as in-progress
 * 2. Generate implementation prompt from step description + spec
 * 3. Execute implementation (Edit/Bash tools)
 * 4. Run verification (Forge: tests, lint, type-check)
 * 5. If fail: analyze error, generate fix, re-verify (up to maxIterations)
 * 6. If pass: save checkpoint, move to next step
 * 7. If max iterations reached: escalate to user
 */

import { logForDebugging } from '../../utils/debug.js'
import type {
  FlowSpec,
  FlowPlan,
  PlanStep,
  FlowState,
  TestResults,
  StepStatus,
} from './types.js'
import { FLOW_DEFAULTS } from './types.js'
import { saveCheckpoint } from './checkpointManager.js'

/** Result of a single loop iteration */
export interface LoopIterationResult {
  /** Whether the step passed verification */
  passed: boolean
  /** Current step status */
  stepStatus: StepStatus
  /** Number of iterations used */
  iterationsUsed: number
  /** Error from last failure (if any) */
  lastError?: string
  /** Test results from verification */
  testResults?: TestResults
  /** Files changed during this step */
  changedFiles: string[]
  /** Whether the step was escalated to the user */
  escalated: boolean
}

/** Options for the loop runner */
export interface LoopRunnerOptions {
  /** Callback to execute implementation for a step */
  implementStep: (step: PlanStep, spec: FlowSpec, previousError?: string) => Promise<string[]>
  /** Callback to run verification (Forge) */
  verifyStep: (step: PlanStep, changedFiles: string[]) => Promise<TestResults>
  /** Callback when escalation is needed */
  onEscalate: (step: PlanStep, iterations: number, lastError: string) => Promise<'retry' | 'skip' | 'abort'>
  /** Callback when a checkpoint should be saved */
  onSaveCheckpoint: (stepIndex: number, description: string, testResults?: TestResults) => Promise<void>
}

/**
 * Run the Implement-Verify-Fix loop for a single step.
 *
 * This is the core of SANDCODE Flow's autonomous behavior.
 */
export async function runStepLoop(
  step: PlanStep,
  spec: FlowSpec,
  flowState: FlowState,
  options: LoopRunnerOptions,
): Promise<LoopIterationResult> {
  let lastError: string | undefined
  let iterationsUsed = 0

  // Update step status
  step.status = 'in-progress'

  while (iterationsUsed < step.maxIterations) {
    iterationsUsed++

    logForDebugging(
      `Flow step ${step.index} iteration ${iterationsUsed}/${step.maxIterations}`,
    )

    // Phase 1: Implement
    step.status = 'in-progress'
    const changedFiles = await options.implementStep(step, spec, lastError)
    step.changedFiles = [...step.changedFiles, ...changedFiles]

    // Phase 2: Verify
    step.status = 'verifying'
    const testResults = await options.verifyStep(step, changedFiles)
    step.testResults = testResults

    if (testResults.passed) {
      // Step passed!
      step.status = 'completed'
      step.iterations = iterationsUsed

      // Save checkpoint
      await options.onSaveCheckpoint(
        step.index,
        step.description,
        testResults,
      )

      logForDebugging(`Flow step ${step.index} PASSED after ${iterationsUsed} iteration(s)`)

      return {
        passed: true,
        stepStatus: 'completed',
        iterationsUsed,
        testResults,
        changedFiles: step.changedFiles,
        escalated: false,
      }
    }

    // Phase 3: Fix (prepare for next iteration)
    step.status = 'fixing'
    lastError = testResults.errors.join('\n')
    step.lastError = lastError
    step.iterations = iterationsUsed

    logForDebugging(
      `Flow step ${step.index} FAILED (iteration ${iterationsUsed}): ${lastError.slice(0, 200)}`,
    )

    // Check if we should continue or escalate
    if (iterationsUsed >= step.maxIterations) {
      break
    }
  }

  // Max iterations reached — escalate
  step.status = 'escalated'

  const userDecision = await options.onEscalate(
    step,
    iterationsUsed,
    lastError ?? 'Unknown error',
  )

  if (userDecision === 'skip') {
    step.status = 'skipped'
    return {
      passed: false,
      stepStatus: 'skipped',
      iterationsUsed,
      lastError,
      changedFiles: step.changedFiles,
      escalated: true,
    }
  }

  if (userDecision === 'abort') {
    step.status = 'failed'
    return {
      passed: false,
      stepStatus: 'failed',
      iterationsUsed,
      lastError,
      changedFiles: step.changedFiles,
      escalated: true,
    }
  }

  // 'retry' — reset and try again with user guidance
  step.status = 'pending'
  step.maxIterations += FLOW_DEFAULTS.maxIterationsPerStep

  return {
    passed: false,
    stepStatus: 'pending',
    iterationsUsed,
    lastError,
    changedFiles: step.changedFiles,
    escalated: true,
  }
}

/**
 * Generate a prompt for implementing a step.
 * This is used by the AI to understand what to implement.
 */
export function generateImplementationPrompt(
  step: PlanStep,
  spec: FlowSpec,
  previousError?: string,
): string {
  const lines: string[] = []

  lines.push(`## Implementing Step ${step.index}: ${step.description}`)
  lines.push('')

  lines.push('### Context')
  lines.push(`**Goal:** ${spec.intent}`)
  lines.push(`**Step:** ${step.description}`)
  if (step.files.length > 0) {
    lines.push(`**Target files:** ${step.files.join(', ')}`)
  }
  lines.push('')

  lines.push('### Pass Criteria')
  for (const criterion of step.passCriteria) {
    lines.push(`- ${criterion}`)
  }
  lines.push('')

  if (previousError) {
    lines.push('### Previous Error (fix this)')
    lines.push('```')
    lines.push(previousError)
    lines.push('```')
    lines.push('')
    lines.push('The previous implementation attempt failed with the error above. Fix the issue and try again.')
  }

  lines.push('### Instructions')
  lines.push('1. Implement the changes described above')
  lines.push('2. Make minimal, focused changes')
  lines.push('3. Follow existing code patterns and conventions')
  lines.push('4. Do not modify unrelated code')

  return lines.join('\n')
}

/**
 * Check if a plan can proceed (no blocking failures).
 */
export function canPlanProceed(plan: FlowPlan): boolean {
  // Check if any step has failed or been escalated without resolution
  const blocked = plan.steps.some(
    s => s.status === 'failed' || (s.status === 'escalated' && s.iterations >= s.maxIterations),
  )
  return !blocked
}
