import { client } from "./example";
import { eventTrigger } from "@trigger.dev/sdk";
import * as fs from "fs";
import * as path from "path";

// ===================================================================
// TYPE DEFINITIONS (aligned with CFN Loop architecture)
// ===================================================================

interface ForceIterationConfig {
  iteration: number;
  gateResult: 'PASS' | 'FAIL';
  consensusResult: 'PASS' | 'FAIL';
  poDecision: 'PROCEED' | 'ITERATE' | 'ABORT';
  gatePassRate?: number;
  consensusScore?: number;
  reason?: string;
}

interface IterationResult {
  iteration: number;
  gatePassed: boolean;
  gatePassRate: number;
  gateThreshold: number;
  consensusMet: boolean;
  consensusScore: number;
  consensusThreshold: number;
  productOwnerDecision: {
    decision: 'PROCEED' | 'ITERATE' | 'ABORT';
    reasoning: string;
    decidedAt: string;
  };
  completedAt: string;
  forceApplied: boolean;
  forceConfig?: ForceIterationConfig;
  deliverablePath?: string;
}

interface AgentResult {
  agentType: string;
  taskId: string;
  iteration: number;
  confidence: number;
  output: string;
  deliverablePath: string;
}

// CFN Agent Job - simulates agent execution with real deliverables
client.defineJob({
  id: "cfn-agent",
  name: "CFN Agent",
  version: "1.0.0",
  trigger: eventTrigger({
    name: "cfn.agent.run",
  }),
  run: async (payload: {
    agentType: string;
    taskId: string;
    iteration: number;
    deliverableDir: string;
  }, io) => {
    await io.logger.info(`Agent ${payload.agentType} starting`, { payload });

    // Create deliverable directory if needed
    if (!fs.existsSync(payload.deliverableDir)) {
      fs.mkdirSync(payload.deliverableDir, { recursive: true });
    }

    // Create iteration-specific deliverable
    const deliverablePath = path.join(
      payload.deliverableDir,
      `iteration-${payload.iteration}.txt`
    );

    const content = `Agent: ${payload.agentType}\nIteration: ${payload.iteration}\nTask ID: ${payload.taskId}\nTimestamp: ${new Date().toISOString()}\n`;

    fs.writeFileSync(deliverablePath, content);
    await io.logger.info(`Created deliverable: ${deliverablePath}`);

    // Simulate agent work
    await io.wait("agent-work", 2);

    const confidence = 0.85 + Math.random() * 0.1; // 0.85-0.95


    const result = {
      agentType: payload.agentType,
      taskId: payload.taskId,
      iteration: payload.iteration,
      confidence,
      output: `Agent ${payload.agentType} result for iteration ${payload.iteration}`,
      deliverablePath,
    };

    // Emit completion event for orchestrator to wait on
    await io.sendEvent(`cfn.agent.complete-${payload.agentType}-${payload.iteration}`, {
      name: "cfn.agent.complete",
      payload: result,
    });

    return result;
  },
});
// Gate Check Job - evaluates pass rate with force override support
client.defineJob({
  id: "cfn-gate-check",
  name: "CFN Gate Check",
  version: "1.0.0",
  trigger: eventTrigger({
    name: "cfn.gate.check",
  }),
  run: async (payload: {
    results: Array<{ confidence: number }>;
    threshold: number;
    iteration: number;
    forceResult?: 'PASS' | 'FAIL';
    forcePassRate?: number;
  }, io) => {
    let avgConfidence: number;
    let passed: boolean;

    // Check for forced result
    if (payload.forceResult) {
      await io.logger.info(`[FORCED] Gate check override applied`, {
        iteration: payload.iteration,
        forceResult: payload.forceResult,
        forcePassRate: payload.forcePassRate,
      });

      avgConfidence = payload.forcePassRate ?? (payload.forceResult === 'PASS' ? 0.96 : 0.70);
      passed = payload.forceResult === 'PASS';
    } else {
      // Normal gate check calculation
      avgConfidence = payload.results.reduce((sum, r) => sum + r.confidence, 0) / payload.results.length;
      passed = avgConfidence >= payload.threshold;
    }

    await io.logger.info(`Gate check: ${passed ? "PASSED" : "FAILED"}`, {
      avgConfidence,
      threshold: payload.threshold,
      iteration: payload.iteration,
      forced: !!payload.forceResult,
    });

    const result = {
      passed,
      passRate: avgConfidence,
      threshold: payload.threshold,
      iteration: payload.iteration,
      forceApplied: !!payload.forceResult,
    };

    // Emit completion event for orchestrator to wait on
    await io.sendEvent(`cfn.gate.complete-${payload.iteration}`, {
      name: "cfn.gate.complete",
      payload: result,
    });

    return result;
  },
});

// Consensus Check Job - validates Loop 2 consensus with force override support
client.defineJob({
  id: "cfn-consensus-check",
  name: "CFN Consensus Check",
  version: "1.0.0",
  trigger: eventTrigger({
    name: "cfn.consensus.check",
  }),
  run: async (payload: {
    validatorResults: Array<{ consensusScore: number }>;
    threshold: number;
    iteration: number;
    forceResult?: 'PASS' | 'FAIL';
    forceScore?: number;
  }, io) => {
    let avgConsensus: number;
    let passed: boolean;

    // Check for forced result
    if (payload.forceResult) {
      await io.logger.info(`[FORCED] Consensus check override applied`, {
        iteration: payload.iteration,
        forceResult: payload.forceResult,
        forceScore: payload.forceScore,
      });

      avgConsensus = payload.forceScore ?? (payload.forceResult === 'PASS' ? 0.92 : 0.80);
      passed = payload.forceResult === 'PASS';
    } else {
      // Normal consensus calculation
      avgConsensus = payload.validatorResults.reduce((sum, r) => sum + r.consensusScore, 0) / payload.validatorResults.length;
      passed = avgConsensus >= payload.threshold;
    }

    await io.logger.info(`Consensus check: ${passed ? "PASSED" : "FAILED"}`, {
      avgConsensus,
      threshold: payload.threshold,
      iteration: payload.iteration,
      forced: !!payload.forceResult,
    });

    const result = {
      passed,
      consensusScore: avgConsensus,
      threshold: payload.threshold,
      iteration: payload.iteration,
      forceApplied: !!payload.forceResult,
    };

    // Emit completion event for orchestrator to wait on
    await io.sendEvent(`cfn.consensus.complete-${payload.iteration}`, {
      name: "cfn.consensus.complete",
      payload: result,
    });

    return result;
  },
});

// Product Owner Decision Job - makes final proceed/iterate/abort decision
client.defineJob({
  id: "cfn-product-owner",
  name: "CFN Product Owner",
  version: "1.0.0",
  trigger: eventTrigger({
    name: "cfn.product.owner",
  }),
  run: async (payload: {
    iteration: number;
    maxIterations: number;
    gateResult: { passed: boolean; passRate: number };
    consensusResult: { passed: boolean; consensusScore: number };
    forceDecision?: 'PROCEED' | 'ITERATE' | 'ABORT';
  }, io) => {
    let decision: 'PROCEED' | 'ITERATE' | 'ABORT';
    let reasoning: string;

    // Check for forced decision
    if (payload.forceDecision) {
      await io.logger.info(`[FORCED] Product Owner decision override applied`, {
        iteration: payload.iteration,
        forceDecision: payload.forceDecision,
      });

      decision = payload.forceDecision;
      reasoning = `Forced decision: ${payload.forceDecision}`;
    } else {
      // Normal decision logic
      if (payload.gateResult.passed && payload.consensusResult.passed) {
        decision = 'PROCEED';
        reasoning = 'All quality gates passed, deliverables meet success criteria';
      } else if (payload.iteration >= payload.maxIterations) {
        decision = 'ABORT';
        reasoning = 'Maximum iterations reached without meeting quality gates';
      } else {
        decision = 'ITERATE';
        reasoning = `Gate: ${payload.gateResult.passRate.toFixed(2)}, Consensus: ${payload.consensusResult.consensusScore.toFixed(2)} - improvements needed`;
      }
    }

    await io.logger.info(`Product Owner decision: ${decision}`, {
      iteration: payload.iteration,
      reasoning,
      forced: !!payload.forceDecision,
    });

    const result = {
      decision,
      reasoning,
      decidedAt: new Date().toISOString(),
      iteration: payload.iteration,
      forceApplied: !!payload.forceDecision,
    };

    // Emit completion event for orchestrator to wait on
    await io.sendEvent(`cfn.po.complete-${payload.iteration}`, {
      name: "cfn.po.complete",
      payload: result,
    });

    return result;
  },
});

// ===================================================================
// HELPER FUNCTIONS - Extracted for reduced complexity
// ===================================================================

interface ForceOverrideResult<T> {
  value: T;
  wasForced: boolean;
}

/**
 * Apply force override if configured for current iteration
 * Generic helper to eliminate duplicate force override logic
 */
function isForceIterationApplicable(
  forceConfig: ForceIterationConfig | undefined,
  currentIteration: number
): boolean {
  return forceConfig?.iteration === currentIteration;
}

/**
 * Apply force override for pass/fail checks
 */
function applyForceOverride<T>(
  forceConfig: ForceIterationConfig | undefined,
  currentIteration: number,
  calculatedValue: T,
  forceResult: 'PASS' | 'FAIL' | undefined,
  passValue: T,
  failValue: T
): ForceOverrideResult<T> {
  if (!isForceIterationApplicable(forceConfig, currentIteration) || !forceResult) {
    return { value: calculatedValue, wasForced: false };
  }

  return {
    value: forceResult === 'PASS' ? passValue : failValue,
    wasForced: true,
  };
}

/**
 * Handle gate failure with early iteration
 */
function createGateFailureResult(
  iteration: number,
  gatePassRate: number,
  gateThreshold: number,
  consensusThreshold: number,
  forceApplied: boolean,
  forceConfig: ForceIterationConfig | undefined,
  deliverablePath?: string
): IterationResult {
  return {
    iteration,
    gatePassed: false,
    gatePassRate,
    gateThreshold,
    consensusMet: false,
    consensusScore: 0,
    consensusThreshold,
    productOwnerDecision: {
      decision: 'ITERATE',
      reasoning: `Gate failed: pass rate ${gatePassRate.toFixed(2)} < ${gateThreshold}`,
      decidedAt: new Date().toISOString(),
    },
    completedAt: new Date().toISOString(),
    forceApplied,
    forceConfig: forceApplied ? forceConfig : undefined,
    deliverablePath,
  };
}

/**
 * Make Product Owner decision based on gate/consensus results
 */
function makeProductOwnerDecision(
  iteration: number,
  maxIterations: number,
  gatePassed: boolean,
  consensusMet: boolean,
  forceDecision?: 'PROCEED' | 'ITERATE' | 'ABORT'
): { decision: 'PROCEED' | 'ITERATE' | 'ABORT'; reasoning: string; decidedAt: string } {
  if (forceDecision) {
    return {
      decision: forceDecision,
      reasoning: `Forced decision: ${forceDecision}`,
      decidedAt: new Date().toISOString(),
    };
  }

  if (gatePassed && consensusMet) {
    return {
      decision: 'PROCEED',
      reasoning: 'All quality gates passed',
      decidedAt: new Date().toISOString(),
    };
  }

  if (iteration >= maxIterations) {
    return {
      decision: 'ABORT',
      reasoning: 'Maximum iterations reached',
      decidedAt: new Date().toISOString(),
    };
  }

  return {
    decision: 'ITERATE',
    reasoning: 'Quality gates require iteration',
    decidedAt: new Date().toISOString(),
  };
}

/**
 * Execute single CFN Loop iteration (Loop 3 → Gate → Loop 2 → Consensus → PO)
 */
async function executeIteration(
  iteration: number,
  payload: {
    agentTypes: string[];
    gateThreshold: number;
    consensusThreshold: number;
    maxIterations: number;
    forceIteration?: ForceIterationConfig;
  },
  taskId: string,
  deliverableDir: string,
  io: any
): Promise<{ result: IterationResult; decision: 'PROCEED' | 'ITERATE' | 'ABORT' }> {
  await io.logger.info(`\n=== ITERATION ${iteration} START ===`);

  // ---------------------------------------------------------------
  // LOOP 3: Run agents sequentially
  // ---------------------------------------------------------------
  await io.logger.info(`[Loop 3] Running ${payload.agentTypes.length} agents`);

  const agentResults: AgentResult[] = [];
  for (const agentType of payload.agentTypes) {
    await io.sendEvent(`agent-${agentType}-${iteration}`, {
      name: "cfn.agent.run",
      payload: { agentType, taskId, iteration, deliverableDir },
    });

    // Wait for agent completion event
    const agentEvent = await io.waitForEvent(`cfn.agent.complete-${agentType}-${iteration}`, {
      timeout: { seconds: 300 },
    });

    agentResults.push(agentEvent.payload);
  }

  await io.logger.info(`[Loop 3] Collected ${agentResults.length} agent results`);

  // ---------------------------------------------------------------
  // GATE CHECK: Apply force override
  // ---------------------------------------------------------------
  const forceGateResult = payload.forceIteration?.gateResult;
  const forceGatePassRate = payload.forceIteration?.gatePassRate;



  await io.sendEvent(`gate-check-${iteration}`, {
    name: "cfn.gate.check",
    payload: {
      results: agentResults.map(r => ({ confidence: r.confidence })),
      threshold: payload.gateThreshold,
      iteration,
      forceResult: forceGateResult,
      forcePassRate: forceGatePassRate,
    },
  });

  // Wait for gate check completion
  const gateEvent = await io.waitForEvent(`cfn.gate.complete-${iteration}`, {
    timeout: { seconds: 60 },
  });
  const gatePassed = gateEvent.payload.passed;
  const gatePassRate = gateEvent.payload.passRate;


  await io.logger.info(`[Gate Check] Result: ${gatePassed ? 'PASSED' : 'FAILED'}`, {
    passRate: gatePassRate.toFixed(4),
    threshold: payload.gateThreshold,
    forced: gateEvent.payload.forceApplied,
  });

  // Handle gate failure early exit
  if (!gatePassed) {
    await io.logger.info(`[Gate Failed] Skipping Loop 2, iterating...`);
    const failureResult = createGateFailureResult(
      iteration,
      gatePassRate,
      payload.gateThreshold,
      payload.consensusThreshold,
      gateEvent.payload.forceApplied,
      payload.forceIteration,
      agentResults[0]?.deliverablePath
    );
    return { result: failureResult, decision: 'ITERATE' };
  }

  // ---------------------------------------------------------------
  // LOOP 2: Run validators (gate passed)
  // ---------------------------------------------------------------
  await io.logger.info(`[Loop 2] Gate passed, running validators`);

  const validatorTypes = ['code-reviewer', 'qa-engineer', 'security-specialist'];
  const validatorResults = validatorTypes.map(() => ({
    consensusScore: 0.88 + Math.random() * 0.1,
  }));

  // ---------------------------------------------------------------
  // CONSENSUS CHECK: Apply force override
  // ---------------------------------------------------------------
  const forceConsensusResult = payload.forceIteration?.consensusResult;
  const forceConsensusScore = payload.forceIteration?.consensusScore;



  await io.sendEvent(`consensus-check-${iteration}`, {
    name: "cfn.consensus.check",
    payload: {
      validatorResults,
      threshold: payload.consensusThreshold,
      iteration,
      forceResult: forceConsensusResult,
      forceScore: forceConsensusScore,
    },
  });

  // Wait for consensus check completion
  const consensusEvent = await io.waitForEvent(`cfn.consensus.complete-${iteration}`, {
    timeout: { seconds: 60 },
  });
  const consensusMet = consensusEvent.payload.passed;
  const consensusScore = consensusEvent.payload.consensusScore;


  await io.logger.info(`[Consensus Check] Result: ${consensusMet ? 'PASSED' : 'FAILED'}`, {
    score: consensusScore.toFixed(4),
    threshold: payload.consensusThreshold,
    forced: consensusEvent.payload.forceApplied,
  });

  // ---------------------------------------------------------------
  // PRODUCT OWNER DECISION
  // ---------------------------------------------------------------
  const forcePODecision = payload.forceIteration?.poDecision;

  await io.sendEvent(`product-owner-${iteration}`, {
    name: "cfn.product.owner",
    payload: {
      iteration,
      maxIterations: payload.maxIterations,
      gateResult: { passed: gatePassed, passRate: gatePassRate },
      consensusResult: { passed: consensusMet, consensusScore },
      forceDecision: forcePODecision,
    },
  });

  // Wait for product owner decision
  const poEvent = await io.waitForEvent(`cfn.po.complete-${iteration}`, {
    timeout: { seconds: 60 },
  });
  const decision = poEvent.payload.decision;
  const poDecision = {
    decision,
    reasoning: poEvent.payload.reasoning,
    decidedAt: poEvent.payload.decidedAt,
  };

  await io.logger.info(`[Product Owner] Decision: ${poDecision.decision}`, {
    reasoning: poDecision.reasoning,
    forced: !!forcePODecision,
  });

  // Build iteration result
  const iterationResult: IterationResult = {
    iteration,
    gatePassed: gatePassed,
    gatePassRate,
    gateThreshold: payload.gateThreshold,
    consensusMet: consensusMet,
    consensusScore,
    consensusThreshold: payload.consensusThreshold,
    productOwnerDecision: poDecision,
    completedAt: new Date().toISOString(),
    forceApplied: gateEvent.payload.forceApplied || consensusEvent.payload.forceApplied || !!forcePODecision,
    forceConfig: (gateEvent.payload.forceApplied || consensusEvent.payload.forceApplied || !!forcePODecision)
      ? payload.forceIteration : undefined,
    deliverablePath: agentResults[0]?.deliverablePath,
  };

  await io.logger.info(`=== ITERATION ${iteration} END ===\n`);

  return { result: iterationResult, decision: poDecision.decision };
}

// ===================================================================
// CFN LOOP WORKFLOW - Main orchestrator with 5-iteration control
// ===================================================================

client.defineJob({
  id: "cfn-loop-workflow",
  name: "CFN Loop Workflow",
  version: "1.0.0",
  trigger: eventTrigger({
    name: "cfn.loop.start",
  }),
  run: async (payload: {
    taskDescription: string;
    maxIterations: number;
    gateThreshold: number;
    consensusThreshold: number;
    agentTypes: string[];
    forceIteration?: ForceIterationConfig;
  }, io) => {
    await io.logger.info("Starting CFN Loop workflow", { payload });

    const taskId = `task-${Date.now()}`;
    const deliverableDir = `/tmp/trigger-dev-deliverables/${taskId}`;

    // Create deliverable directory
    fs.mkdirSync(deliverableDir, { recursive: true });
    await io.logger.info(`Created deliverable directory: ${deliverableDir}`);

    let iteration = 1;
    let decision: 'PROCEED' | 'ITERATE' | 'ABORT' = 'ITERATE';
    const iterationHistory: IterationResult[] = [];

    // Main iteration loop - delegated to executeIteration()
    while (iteration <= payload.maxIterations && decision === 'ITERATE') {
      const { result, decision: newDecision } = await executeIteration(
        iteration,
        payload,
        taskId,
        deliverableDir,
        io
      );

      iterationHistory.push(result);
      decision = newDecision;

      if (decision === 'ITERATE') {
        iteration++;
      }
    }

    // Final status
    const finalStatus = decision === 'PROCEED' ? 'COMPLETED' : decision === 'ABORT' ? 'ABORTED' : 'MAX_ITERATIONS_REACHED';

    await io.logger.info(`\n=== CFN Loop workflow finished: ${finalStatus} ===`);
    await io.logger.info(`Total iterations: ${iteration}`);
    await io.logger.info(`Final decision: ${decision}`);

    return {
      status: finalStatus,
      iterations: iteration,
      decision,
      taskId,
      deliverableDir,
      iterationHistory,
      summary: {
        totalIterations: iterationHistory.length,
        gatePassCount: iterationHistory.filter(i => i.gatePassed).length,
        consensusPassCount: iterationHistory.filter(i => i.consensusMet).length,
        forcedIterations: iterationHistory.filter(i => i.forceApplied).length,
      },
    };
  },
});
