/**
 * A2A Pipeline Runner — executes the incident pipeline in sequence:
 *   DispatcherAgent → TelemetryAnalystAgent → DiagnosticsOrchestratorAgent → ReportWriterAgent
 *
 * Every step transition is logged with runId + stepId for full audit tracing.
 * Phase 1: handoffs are persisted via structured req.log entries.
 * Phase 2+: handoffs will additionally be written to a Cosmos container.
 */

import { randomUUID } from 'node:crypto';
import type { McpLogger } from '../tools/types.js';
import type {
  DiagnosticsSessionResult,
  FinalIncidentReport,
  SupportIncidentBrief,
} from './types.js';
import { dispatch } from './agents/dispatcher.js';
import { analyze } from './agents/telemetry-analyst.js';
import { orchestrate } from './agents/diagnostics-orchestrator.js';
import { writeReport } from './agents/report-writer.js';
import {
  trackRunCompleted,
  trackRunFailed,
  trackRunStarted,
  trackStepCompleted,
  trackStepFailed,
  trackStepStarted,
} from './run-tracker.js';

export interface PipelineOptions {
  token?: string;
  requestId?: string;
  initiatedBy?: string;
  log: McpLogger;
}

export async function runIncidentPipeline(
  brief: SupportIncidentBrief,
  opts: PipelineOptions
): Promise<FinalIncidentReport> {
  const runId = `run_${randomUUID()}`;
  const { log } = opts;
  let currentStep:
    | {
        stepName: 'dispatcher' | 'telemetry_analyst' | 'diagnostics_orchestrator' | 'report_writer';
        order: number;
      }
    | undefined;

  await safeTrack(() =>
    trackRunStarted({
      runId,
      productId: brief.productId,
      name: 'incident-pipeline',
      initiatedBy: opts.initiatedBy,
      requestId: opts.requestId,
      token: opts.token,
      input: {
        openDiagnosticsSession: brief.openDiagnosticsSession,
        userReport: brief.userReport,
      },
    })
  );

  log.info(
    { runId, productId: brief.productId, a2aStep: 'pipeline.start' },
    'A2A pipeline started'
  );

  try {
    // ── Step 1: Dispatcher ────────────────────────────────────────────────
    currentStep = { stepName: 'dispatcher', order: 1 };
    const dispatcherStep = currentStep;
    await safeTrack(() =>
      trackStepStarted({
        runId,
        productId: brief.productId,
        stepName: dispatcherStep.stepName,
        order: dispatcherStep.order,
        token: opts.token,
        requestId: opts.requestId,
        input: {
          userReport: brief.userReport,
        },
      })
    );

    const decision = dispatch(brief, {
      runId,
      requestId: opts.requestId,
      initiatedBy: opts.initiatedBy,
    });

    await safeTrack(() =>
      trackStepCompleted({
        runId,
        productId: brief.productId,
        stepName: dispatcherStep.stepName,
        order: dispatcherStep.order,
        token: opts.token,
        requestId: opts.requestId,
        output: {
          steps: decision.steps,
          resolvedTimeWindow: decision.resolvedTimeWindow,
        },
      })
    );

    log.info(
      {
        runId,
        stepId: decision.runContext.stepId,
        a2aStep: 'dispatcher.complete',
        steps: decision.steps,
        resolvedTimeWindow: decision.resolvedTimeWindow,
      },
      'Dispatcher decided pipeline steps'
    );

    // ── Step 2: Telemetry Analyst ─────────────────────────────────────────
    currentStep = { stepName: 'telemetry_analyst', order: 2 };
    const analystStep = currentStep;
    await safeTrack(() =>
      trackStepStarted({
        runId,
        productId: brief.productId,
        stepName: analystStep.stepName,
        order: analystStep.order,
        token: opts.token,
        requestId: opts.requestId,
      })
    );

    const findings = await analyze(decision, { token: opts.token });

    await safeTrack(() =>
      trackStepCompleted({
        runId,
        productId: brief.productId,
        stepName: analystStep.stepName,
        order: analystStep.order,
        token: opts.token,
        requestId: opts.requestId,
        output: {
          clusterCount: findings.clusters.length,
          hypothesisCount: findings.topHypotheses.length,
          queryError: findings.queryError,
        },
      })
    );

    log.info(
      {
        runId,
        stepId: findings.runContext.stepId,
        a2aStep: 'telemetry_analyst.complete',
        clusterCount: findings.clusters.length,
        hypothesisCount: findings.topHypotheses.length,
        queryError: findings.queryError,
      },
      'Telemetry analyst completed'
    );

    // ── Step 3: Diagnostics Orchestrator (conditional) ───────────────────
    let diagResult: DiagnosticsSessionResult | null = null;
    if (decision.steps.includes('diagnostics_orchestrator')) {
      currentStep = { stepName: 'diagnostics_orchestrator', order: 3 };
      const diagnosticsStep = currentStep;
      await safeTrack(() =>
        trackStepStarted({
          runId,
          productId: brief.productId,
          stepName: diagnosticsStep.stepName,
          order: diagnosticsStep.order,
          token: opts.token,
          requestId: opts.requestId,
        })
      );

      const diagnosticsResult = await orchestrate(decision, findings, { token: opts.token });
      diagResult = diagnosticsResult;

      await safeTrack(() =>
        trackStepCompleted({
          runId,
          productId: brief.productId,
          stepName: diagnosticsStep.stepName,
          order: diagnosticsStep.order,
          token: opts.token,
          requestId: opts.requestId,
          output: {
            skipped: diagnosticsResult.skipped,
            sessionId: diagnosticsResult.session?.id,
            sessionError: diagnosticsResult.sessionError,
          },
        })
      );

      log.info(
        {
          runId,
          stepId: diagnosticsResult.runContext.stepId,
          a2aStep: 'diagnostics_orchestrator.complete',
          skipped: diagnosticsResult.skipped,
          sessionId: diagnosticsResult.session?.id,
          sessionError: diagnosticsResult.sessionError,
        },
        'Diagnostics orchestrator completed'
      );
    }

    // ── Step 4: Report Writer ─────────────────────────────────────────────
    currentStep = { stepName: 'report_writer', order: 4 };
    const reportStep = currentStep;
    await safeTrack(() =>
      trackStepStarted({
        runId,
        productId: brief.productId,
        stepName: reportStep.stepName,
        order: reportStep.order,
        token: opts.token,
        requestId: opts.requestId,
      })
    );

    const report = writeReport(decision, findings, diagResult);

    await safeTrack(() =>
      trackStepCompleted({
        runId,
        productId: brief.productId,
        stepName: reportStep.stepName,
        order: reportStep.order,
        token: opts.token,
        requestId: opts.requestId,
        output: {
          clusterCount: report.clusterCount,
          hasSession: !!report.diagnosticsSession,
        },
      })
    );

    await safeTrack(() =>
      trackRunCompleted({
        runId,
        productId: brief.productId,
        name: 'incident-pipeline',
        initiatedBy: opts.initiatedBy,
        requestId: opts.requestId,
        token: opts.token,
        output: {
          clusterCount: report.clusterCount,
          hasSession: !!report.diagnosticsSession,
        },
      })
    );

    log.info(
      {
        runId,
        a2aStep: 'pipeline.complete',
        productId: brief.productId,
        clusterCount: report.clusterCount,
        hasSession: !!report.diagnosticsSession,
      },
      'A2A pipeline completed'
    );

    return report;
  } catch (error) {
    const message = error instanceof Error ? error.message : String(error);
    if (currentStep) {
      const failedStep = currentStep;
      await safeTrack(() =>
        trackStepFailed({
          runId,
          productId: brief.productId,
          stepName: failedStep.stepName,
          order: failedStep.order,
          token: opts.token,
          requestId: opts.requestId,
          error: message,
        })
      );
    }
    await safeTrack(() =>
      trackRunFailed({
        runId,
        productId: brief.productId,
        name: 'incident-pipeline',
        initiatedBy: opts.initiatedBy,
        requestId: opts.requestId,
        token: opts.token,
        error: message,
      })
    );
    throw error;
  }
}

async function safeTrack(fn: () => Promise<void>): Promise<void> {
  try {
    await fn();
  } catch {
    // Tracking must never fail the pipeline itself.
  }
}
