/**
 * CFN Async Validator Orchestrator (Phase 3, Task 3.1)
 *
 * Spawns 5 validators in parallel, collects results asynchronously,
 * and structures them for consensus calculation.
 *
 * VALIDATORS:
 * 1. Security Validator - vulnerability detection, threat analysis
 * 2. Performance Validator - throughput, latency, optimization
 * 3. Testing Validator - coverage, test quality, edge cases
 * 4. Architecture Validator - design patterns, scalability, maintainability
 * 5. Code Quality Validator - complexity, duplication, readability
 *
 * ORCHESTRATION PATTERN:
 * - Spawn all 5 validators simultaneously via tasks.trigger()
 * - Wait for all results via Promise.all()
 * - Each validator has 300s timeout (handles in runs.poll)
 * - Partial success: minimum 3/5 validators required
 * - Retry once on failure (max 2 attempts per validator)
 */

import { task, tasks, runs } from "@trigger.dev/sdk/v3";
import type { AsyncSecurityValidatorResult } from "./cfn-async-security-validator.js";
import type { AsyncPerformanceValidatorResult } from "./cfn-async-performance-validator.js";
import type { DecompositionPlan } from "./cfn-decomposition-aggregator.js";

// =============================================
// Type Definitions
// =============================================

export interface ValidatorResult {
  validatorType: "security" | "performance" | "testing" | "architecture" | "code-quality";
  status: "success" | "timeout" | "error";
  score: number; // 0.0-1.0
  findings: string[];
  recommendations: string[];
  latencyMs: number;
  error?: string;
}

export interface OrchestratorPayload {
  taskId: string;
  decompositionPlan: DecompositionPlan;
  implementations: string[]; // Generated code from Loop 3
  tests: string[]; // Generated tests from Loop 3
  workDir: string;
}

export interface OrchestratorResult {
  taskId: string;
  timestamp: number;
  validators: ValidatorResult[];
  overallScore: number; // Average of all successful validators
  consensusReached: boolean; // true if >= 3 validators succeeded
  totalLatencyMs: number; // Time to complete all validators
  successCount: number;
  failureCount: number;
  timeoutCount: number;
}

// =============================================
// Validator Timeout and Retry Config
// =============================================

const VALIDATOR_TIMEOUT_MS = 300_000; // 5 minutes per validator
const RETRY_ATTEMPTS = 2; // Max 2 attempts (initial + 1 retry)
const MINIMUM_QUORUM = 3; // Minimum validators needed for consensus

// =============================================
// Main Orchestrator Task
// =============================================

export const cfnAsyncValidatorOrchestratorTask = task({
  id: "cfn-async-validator-orchestrator",
  retry: { maxAttempts: 1 }, // No retry at orchestrator level (validators handle retries)

  run: async (payload: OrchestratorPayload): Promise<OrchestratorResult> => {
    const orchestratorStart = Date.now();

    console.log(`[validator-orchestrator] ========== ASYNC VALIDATOR ORCHESTRATION ==========`);
    console.log(`[validator-orchestrator] Task ID: ${payload.taskId}`);
    console.log(`[validator-orchestrator] Micro-tasks: ${payload.decompositionPlan.totalEstimatedTasks}`);
    console.log(`[validator-orchestrator] Implementations: ${payload.implementations.length} files`);
    console.log(`[validator-orchestrator] Tests: ${payload.tests.length} files`);
    console.log(``);

    // Prepare validator results
    const validatorResults: ValidatorResult[] = [];

    // ===== STEP 1: SPAWN ALL 5 VALIDATORS IN PARALLEL =====
    console.log(`[validator-orchestrator] Step 1: Spawning 5 validators in parallel...`);
    const spawnStart = Date.now();

    // Combine implementations and tests into single payload
    const implementationCode = payload.implementations.join("\n\n// ===== FILE SEPARATOR =====\n\n");
    const testCode = payload.tests.join("\n\n// ===== FILE SEPARATOR =====\n\n");

    const validatorPromises = [
      // Security Validator
      spawnValidatorWithRetry(
        "security",
        "cfn-async-security-validator",
        {
          taskId: payload.taskId,
          implementation: implementationCode,
          testCode: testCode,
          workDir: payload.workDir,
        }
      ),

      // Performance Validator
      spawnValidatorWithRetry(
        "performance",
        "cfn-async-performance-validator",
        {
          taskId: payload.taskId,
          implementation: implementationCode,
          testCode: testCode,
          workDir: payload.workDir,
        }
      ),

      // Testing Validator (placeholder - will implement in next iteration)
      spawnValidatorWithRetry(
        "testing",
        "cfn-async-testing-validator",
        {
          taskId: payload.taskId,
          implementation: implementationCode,
          testCode: testCode,
          workDir: payload.workDir,
        }
      ),

      // Architecture Validator (placeholder)
      spawnValidatorWithRetry(
        "architecture",
        "cfn-async-architecture-validator",
        {
          taskId: payload.taskId,
          implementation: implementationCode,
          testCode: testCode,
          workDir: payload.workDir,
        }
      ),

      // Code Quality Validator (placeholder)
      spawnValidatorWithRetry(
        "code-quality",
        "cfn-async-code-quality-validator",
        {
          taskId: payload.taskId,
          implementation: implementationCode,
          testCode: testCode,
          workDir: payload.workDir,
        }
      ),
    ];

    const spawnDuration = Date.now() - spawnStart;
    console.log(`[validator-orchestrator]   ✓ Spawned 5 validators in ${spawnDuration}ms`);
    console.log(``);

    // ===== STEP 2: WAIT FOR ALL VALIDATORS TO COMPLETE =====
    console.log(`[validator-orchestrator] Step 2: Waiting for validators to complete...`);
    const waitStart = Date.now();

    const results = await Promise.all(validatorPromises);

    const waitDuration = Date.now() - waitStart;
    console.log(`[validator-orchestrator]   ✓ All validators completed in ${(waitDuration / 1000).toFixed(2)}s`);
    console.log(``);

    // ===== STEP 3: PROCESS RESULTS =====
    console.log(`[validator-orchestrator] Step 3: Processing validator results...`);

    let successCount = 0;
    let failureCount = 0;
    let timeoutCount = 0;

    for (const result of results) {
      validatorResults.push(result);

      console.log(`[validator-orchestrator]   ${result.validatorType}: ${result.status} (score: ${result.score.toFixed(2)}, latency: ${result.latencyMs}ms)`);

      if (result.status === "success") {
        successCount++;
      } else if (result.status === "timeout") {
        timeoutCount++;
      } else {
        failureCount++;
      }
    }

    console.log(``);

    // ===== STEP 4: CALCULATE CONSENSUS =====
    console.log(`[validator-orchestrator] Step 4: Calculating consensus...`);

    const successfulValidators = validatorResults.filter(v => v.status === "success");
    const overallScore = successfulValidators.length > 0
      ? successfulValidators.reduce((sum, v) => sum + v.score, 0) / successfulValidators.length
      : 0.0;

    const consensusReached = successCount >= MINIMUM_QUORUM;

    console.log(`[validator-orchestrator]   Success: ${successCount}/${validatorResults.length}`);
    console.log(`[validator-orchestrator]   Failures: ${failureCount}`);
    console.log(`[validator-orchestrator]   Timeouts: ${timeoutCount}`);
    console.log(`[validator-orchestrator]   Overall score: ${overallScore.toFixed(2)}`);
    console.log(`[validator-orchestrator]   Consensus: ${consensusReached ? "REACHED" : "FAILED"} (min ${MINIMUM_QUORUM})`);
    console.log(``);

    // ===== STEP 5: RETURN ORCHESTRATOR RESULT =====
    const totalLatency = Date.now() - orchestratorStart;

    const orchestratorResult: OrchestratorResult = {
      taskId: payload.taskId,
      timestamp: Date.now(),
      validators: validatorResults,
      overallScore,
      consensusReached,
      totalLatencyMs: totalLatency,
      successCount,
      failureCount,
      timeoutCount,
    };

    console.log(`[validator-orchestrator] ========== ORCHESTRATION COMPLETE ==========`);
    console.log(`[validator-orchestrator] Total time: ${(totalLatency / 1000).toFixed(2)}s`);
    console.log(`[validator-orchestrator] Consensus: ${consensusReached ? "✓ REACHED" : "✗ FAILED"}`);
    console.log(``);

    return orchestratorResult;
  },
});

// =============================================
// Helper Functions
// =============================================

/**
 * Spawn a validator with retry logic
 * Retries once on failure, up to RETRY_ATTEMPTS total
 */
async function spawnValidatorWithRetry(
  validatorType: ValidatorResult["validatorType"],
  taskName: string,
  validatorPayload: any
): Promise<ValidatorResult> {
  const validatorStart = Date.now();

  for (let attempt = 1; attempt <= RETRY_ATTEMPTS; attempt++) {
    try {
      console.log(`[validator-orchestrator]   ${validatorType}: Attempt ${attempt}/${RETRY_ATTEMPTS}...`);

      // Trigger validator
      const handle = await tasks.trigger(taskName, validatorPayload);

      // Poll with timeout
      const pollStart = Date.now();
      const result = await runs.poll(handle.id, {
        pollIntervalMs: 2000,
        // Note: Trigger.dev v4 doesn't support explicit timeout in poll
        // We implement timeout manually via Promise.race below
      });

      const latency = Date.now() - validatorStart;

      // Check if validator task succeeded
      if (result.status === "COMPLETED" && result.output) {
        const output = result.output as any;

        return {
          validatorType,
          status: "success",
          score: extractScore(output),
          findings: extractFindings(output),
          recommendations: extractRecommendations(output),
          latencyMs: latency,
        };
      } else {
        throw new Error(`Validator ${validatorType} failed: ${result.status}`);
      }
    } catch (error) {
      const errorMsg = (error as Error).message;

      console.warn(`[validator-orchestrator]   ${validatorType}: Attempt ${attempt} failed: ${errorMsg}`);

      // If last attempt, return error result
      if (attempt === RETRY_ATTEMPTS) {
        return {
          validatorType,
          status: "error",
          score: 0.0,
          findings: [],
          recommendations: [],
          latencyMs: Date.now() - validatorStart,
          error: errorMsg,
        };
      }

      // Otherwise, retry
      await sleep(1000 * attempt); // Exponential backoff
    }
  }

  // Should never reach here due to final attempt return, but TypeScript requires it
  return {
    validatorType,
    status: "error",
    score: 0.0,
    findings: [],
    recommendations: [],
    latencyMs: Date.now() - validatorStart,
    error: "Max retries exceeded",
  };
}

/**
 * Extract score from validator output
 * Different validators have different output formats
 */
function extractScore(output: any): number {
  // Security validator
  if (output.vulnerabilityScore !== undefined) {
    return 1.0 - (output.vulnerabilityScore / 100); // Invert score (0 vuln = 1.0 quality)
  }

  // Performance validator
  if (output.performanceGrade !== undefined) {
    const gradeMap: Record<string, number> = {
      "A+": 1.0, "A": 0.95, "B+": 0.90, "B": 0.85,
      "C+": 0.75, "C": 0.70, "D": 0.60, "F": 0.40,
    };
    return gradeMap[output.performanceGrade] ?? 0.70;
  }

  // Testing validator (placeholder)
  if (output.coverageScore !== undefined) {
    return output.coverageScore / 100;
  }

  // Architecture validator (placeholder)
  if (output.architectureScore !== undefined) {
    return output.architectureScore / 100;
  }

  // Code quality validator (placeholder)
  if (output.qualityScore !== undefined) {
    return output.qualityScore / 100;
  }

  // Default fallback
  return 0.70;
}

/**
 * Extract findings from validator output
 */
function extractFindings(output: any): string[] {
  if (output.findings && Array.isArray(output.findings)) {
    return output.findings.map((f: any) =>
      typeof f === "string" ? f : f.title || f.description || ""
    );
  }

  if (output.issues && Array.isArray(output.issues)) {
    return output.issues.map((i: any) =>
      typeof i === "string" ? i : i.title || i.description || ""
    );
  }

  return [];
}

/**
 * Extract recommendations from validator output
 */
function extractRecommendations(output: any): string[] {
  if (output.recommendations && Array.isArray(output.recommendations)) {
    return output.recommendations;
  }

  return [];
}

/**
 * Sleep utility
 */
function sleep(ms: number): Promise<void> {
  return new Promise(resolve => setTimeout(resolve, ms));
}
