/**
 * FlowEngine — Main Orchestrator
 *
 * Coordinates the entire autonomous development loop:
 * Intent -> Spec -> Plan -> Implement -> Verify -> Fix -> Checkpoint
 *
 * This is the entry point for SANDCODE Flow. It ties together
 * specGenerator, planDecomposer, loopRunner, and checkpointManager.
 */

import { readFile, writeFile, mkdir } from 'fs/promises'
import { join } from 'path'
import { getCwd } from '../../utils/cwd.js'
import { logForDebugging } from '../../utils/debug.js'
import { getSessionId } from '../../bootstrap/state.js'
import type {
  FlowState,
  FlowId,
  FlowSpec,
  FlowPlan,
  FlowStatus,
  PlanStep,
  TestResults,
  StepStatus,
  FlowCheckpoint,
} from './types.js'
import { createFlowId, FLOW_DEFAULTS } from './types.js'
import { generateSpec, approveSpec } from './specGenerator.js'
import { decomposeSpec, getNextStep, getPlanProgress } from './planDecomposer.js'
import { saveCheckpoint, restoreCheckpoint, listCheckpoints, getLatestCheckpoint, formatCheckpointSummary } from './checkpointManager.js'
import { runStepLoop, generateImplementationPrompt, type LoopRunnerOptions } from './loopRunner.js'

/** Active flow state (singleton per session) */
let activeFlow: FlowState | null = null

// --- Public API ---

/**
 * Start a new flow from user intent.
 *
 * This initiates the spec generation phase.
 */
export async function startFlow(intent: string): Promise<FlowState> {
  const flowId = createFlowId()

  const state: FlowState = {
    version: 1,
    id: flowId,
    status: 'intent-captured',
    intent,
    currentStepIndex: 0,
    checkpoints: [],
    startedAt: Date.now(),
    sessionId: getSessionId(),
  }

  activeFlow = state
  await persistFlowState(state)

  logForDebugging(`Flow started: ${flowId} with intent: ${intent.slice(0, 100)}`)
  return state
}

/**
 * Generate a spec for the current flow.
 *
 * Returns the spec for user approval.
 */
export async function generateFlowSpec(flowState?: FlowState): Promise<FlowSpec> {
  const state = flowState ?? getActiveFlow()
  if (!state) throw new Error('No active flow')

  state.status = 'spec-generating'
  await persistFlowState(state)

  const spec = await generateSpec(state.id, state.intent)

  state.spec = spec
  state.status = 'spec-pending-approval'
  await persistFlowState(state)

  return spec
}

/**
 * Approve the current spec and generate a plan.
 */
export async function approveFlowSpec(flowState?: FlowState): Promise<{ spec: FlowSpec; plan: FlowPlan }> {
  const state = flowState ?? getActiveFlow()
  if (!state) throw new Error('No active flow')
  if (!state.spec) throw new Error('No spec to approve')

  const approved = await approveSpec(state.spec)
  state.spec = approved

  // Decompose spec into plan
  const plan = decomposeSpec(state.id, approved)
  state.plan = plan
  state.status = 'executing'
  await persistFlowState(state)

  return { spec: approved, plan }
}

/**
 * Execute the next step in the plan.
 *
 * Returns the step result or null if the plan is complete.
 */
export async function executeNextStep(
  options: LoopRunnerOptions,
  flowState?: FlowState,
): Promise<{
  completed: boolean
  step?: PlanStep
  progress: { completed: number; total: number; percentage: number }
}> {
  const state = flowState ?? getActiveFlow()
  if (!state || !state.plan) throw new Error('No active flow with plan')

  const nextStep = getNextStep(state.plan)
  if (!nextStep) {
    // All steps complete!
    state.status = 'completed'
    state.completedAt = Date.now()
    await persistFlowState(state)
    activeFlow = null

    return {
      completed: true,
      progress: getPlanProgress(state.plan),
    }
  }

  // Run the step loop
  const result = await runStepLoop(nextStep, state.spec!, state, options)

  // Update plan progress
  const progress = getPlanProgress(state.plan)

  // If step failed and wasn't resolved, mark flow
  if (result.stepStatus === 'failed') {
    state.status = 'step-failed'
  } else if (result.stepStatus === 'escalated') {
    state.status = 'escalated'
  }

  await persistFlowState(state)

  return {
    completed: false,
    step: nextStep,
    progress,
  }
}

/**
 * Pause the current flow.
 */
export async function pauseFlow(): Promise<void> {
  const state = getActiveFlow()
  if (!state) return

  state.status = 'paused'
  await persistFlowState(state)
}

/**
 * Resume a paused flow.
 */
export async function resumeFlow(flowId?: FlowId): Promise<FlowState | null> {
  if (flowId) {
    const state = await loadFlowState(flowId)
    if (state) {
      state.status = 'executing'
      activeFlow = state
      await persistFlowState(state)
      return state
    }
    return null
  }

  const state = getActiveFlow()
  if (state && state.status === 'paused') {
    state.status = 'executing'
    await persistFlowState(state)
    return state
  }

  return null
}

/**
 * Rewind to the previous checkpoint.
 */
export async function rewindFlow(): Promise<FlowCheckpoint | null> {
  const state = getActiveFlow()
  if (!state) return null

  const checkpoints = await listCheckpoints(state.id)
  if (checkpoints.length < 2) return null

  // Go back one checkpoint (remove the last one)
  const targetCp = checkpoints[checkpoints.length - 2]
  if (!targetCp) return null

  const restored = await restoreCheckpoint(targetCp.id, state.id)
  if (!restored) return null

  // Update flow state
  state.currentStepIndex = targetCp.stepIndex
  state.status = 'executing'

  // Reset step statuses after the checkpoint
  if (state.plan) {
    for (const step of state.plan.steps) {
      if (step.index > targetCp.stepIndex) {
        step.status = 'pending'
        step.iterations = 0
        step.changedFiles = []
        step.testResults = undefined
        step.lastError = undefined
      }
    }
  }

  await persistFlowState(state)
  return restored
}

/**
 * Get the active flow state.
 */
export function getActiveFlow(): FlowState | null {
  return activeFlow
}

/**
 * Get a formatted status of the current flow.
 */
export function getFlowStatus(): string {
  const state = activeFlow
  if (!state) return 'No active flow.'

  const lines: string[] = []
  lines.push(`Flow: ${state.id}`)
  lines.push(`Intent: ${state.intent}`)
  lines.push(`Status: ${state.status}`)

  if (state.plan) {
    const progress = getPlanProgress(state.plan)
    lines.push(`Progress: ${progress.completed}/${progress.total} steps (${progress.percentage}%)`)

    for (const step of state.plan.steps) {
      const icon = getStepIcon(step.status)
      lines.push(`  ${icon} Step ${step.index}: ${step.description} [${step.status}]`)
    }
  }

  return lines.join('\n')
}

/**
 * Get a formatted list of checkpoints for the active flow.
 */
export async function getCheckpointsSummary(): Promise<string> {
  const state = activeFlow
  if (!state) return 'No active flow.'

  const checkpoints = await listCheckpoints(state.id)
  return formatCheckpointSummary(checkpoints)
}

// --- Internal helpers ---

/**
 * Get an icon for a step status.
 */
function getStepIcon(status: StepStatus): string {
  switch (status) {
    case 'pending': return 'o'
    case 'in-progress': return '>'
    case 'verifying': return '?'
    case 'fixing': return '!'
    case 'completed': return '+'
    case 'failed': return 'x'
    case 'skipped': return '-'
    case 'escalated': return '*'
    default: return ' '
  }
}

/**
 * Persist flow state to disk.
 */
async function persistFlowState(state: FlowState): Promise<void> {
  const flowPath = join(getCwd(), FLOW_DEFAULTS.flowFile)
  await mkdir(join(getCwd(), '.sandcode'), { recursive: true })
  await writeFile(flowPath, JSON.stringify(state, null, 2), 'utf-8')
}

/**
 * Load flow state from disk.
 */
async function loadFlowState(flowId: FlowId): Promise<FlowState | null> {
  try {
    const flowPath = join(getCwd(), FLOW_DEFAULTS.flowFile)
    const content = await readFile(flowPath, 'utf-8')
    const state = JSON.parse(content) as FlowState
    if (state.id === flowId) return state
  } catch {
    // File doesn't exist
  }
  return null
}

// Re-export for convenience
export { generateSpec, approveSpec } from './specGenerator.js'
export { decomposeSpec, getNextStep, getPlanProgress } from './planDecomposer.js'
export { saveCheckpoint, restoreCheckpoint, listCheckpoints, getLatestCheckpoint } from './checkpointManager.js'
export { runStepLoop, generateImplementationPrompt } from './loopRunner.js'
export { type LoopRunnerOptions, type LoopIterationResult } from './loopRunner.js'
