/**
 * STTFallbackMonitorAgent — A2A pipeline for LysnrAI offline STT fallback monitoring.
 *
 * Agent roster (3 steps):
 *   1. STTEventScannerAgent     — query telemetry for stt_completed events, compute online/offline split
 *   2. DiagnosticsLaunchAgent   — if offline rate exceeds threshold, open a diagnostics session
 *   3. FallbackReportAgent      — assemble rate analysis + diagnostics session ID + recommendations
 *
 * MCP tools:
 *   lysnrai.stt.monitorFallback(from?, to?, offlineThreshold?) — run pipeline
 */

import { randomUUID } from 'node:crypto';
import { z } from 'zod';
import { registerTool } from '../tools/registry.js';
import type { McpToolRequest } from '../tools/types.js';
import {
  telemetryQuery,
  diagnosticsCreateSession,
  diagnosticsUpdateSession,
  type DebugSession,
} from '../../lib/platform-client.js';
import { lysnraiSttGetBackendStatus } from '../../lib/lysnrai-client.js';

// ── Types ──────────────────────────────────────────────────────────────────────

interface SttEventScan {
  totalEvents: number;
  azureCount: number;
  whisperCount: number;
  unknownCount: number;
  offlineRate: number;
  exceedsThreshold: boolean;
  threshold: number;
  currentBackend: string | null;
}

export interface STTFallbackReport {
  runId: string;
  productId: 'lysnrai';
  from: string;
  to: string;
  offlineThreshold: number;
  totalEvents: number;
  azureCount: number;
  whisperCount: number;
  offlineRate: number;
  thresholdExceeded: boolean;
  diagnosticsSessionId: string | null;
  currentBackend: string | null;
  recommendation: string;
  generatedAt: string;
}

// ── Step 1: STTEventScannerAgent ──────────────────────────────────────────────

async function scanSttEvents(
  fromTime: string,
  toTime: string,
  offlineThreshold: number,
  opts: { token?: string; requestId?: string }
): Promise<SttEventScan> {
  let totalEvents = 0;
  let azureCount = 0;
  let whisperCount = 0;
  let unknownCount = 0;
  let currentBackend: string | null = null;

  try {
    const result = await telemetryQuery(
      {
        productId: 'lysnrai',
        eventType: 'stt_completed',
        from: fromTime,
        to: toTime,
        limit: 500,
      },
      { ...opts, productId: 'lysnrai' }
    );

    const events = result.events as Array<Record<string, unknown>>;
    totalEvents = events.length;

    for (const event of events) {
      const props = (event['properties'] as Record<string, unknown>) ?? {};
      const provider = String(props['stt_provider'] ?? event['stt_provider'] ?? '').toLowerCase();
      if (provider === 'azure') azureCount++;
      else if (provider === 'whisper') whisperCount++;
      else unknownCount++;
    }
  } catch {
    // best-effort
  }

  try {
    const status = await lysnraiSttGetBackendStatus(opts);
    currentBackend = ((status as unknown as Record<string, unknown>)['provider'] as string) ?? null;
  } catch {
    // best-effort
  }

  const offlineRate = totalEvents > 0 ? whisperCount / totalEvents : 0;
  const exceedsThreshold = offlineRate >= offlineThreshold;

  return {
    totalEvents,
    azureCount,
    whisperCount,
    unknownCount,
    offlineRate,
    exceedsThreshold,
    threshold: offlineThreshold,
    currentBackend,
  };
}

// ── Step 2: DiagnosticsLaunchAgent ────────────────────────────────────────────

async function launchDiagnosticsIfNeeded(
  scan: SttEventScan,
  opts: { token?: string; requestId?: string }
): Promise<DebugSession | null> {
  if (!scan.exceedsThreshold) return null;

  try {
    const session = await diagnosticsCreateSession(
      {
        productId: 'lysnrai',
        collectionLevel: 'debug',
      },
      opts
    );
    return session;
  } catch {
    return null;
  }
}

// ── Step 3: FallbackReportAgent ───────────────────────────────────────────────

async function buildFallbackReport(
  runId: string,
  scan: SttEventScan,
  diagnosticsSession: DebugSession | null,
  fromTime: string,
  toTime: string,
  opts: { requestId?: string }
): Promise<STTFallbackReport> {
  if (diagnosticsSession) {
    try {
      await diagnosticsUpdateSession(diagnosticsSession.id, { status: 'completed' }, opts);
    } catch {
      // best-effort
    }
  }

  let recommendation: string;
  if (scan.totalEvents === 0) {
    recommendation =
      'No STT events found in the specified window. Expand the time range or verify telemetry is flowing.';
  } else if (!scan.exceedsThreshold) {
    recommendation = `Offline fallback rate ${(scan.offlineRate * 100).toFixed(1)}% is within acceptable threshold (${(scan.threshold * 100).toFixed(0)}%). Azure Speech SDK is functioning normally.`;
  } else {
    recommendation = `ALERT: ${(scan.offlineRate * 100).toFixed(1)}% of STT events used Whisper (offline fallback), exceeding ${(scan.threshold * 100).toFixed(0)}% threshold. Check Azure Speech service connectivity, subscription key validity, and network routing from LysnrAI desktop app.`;
  }

  return {
    runId,
    productId: 'lysnrai',
    from: fromTime,
    to: toTime,
    offlineThreshold: scan.threshold,
    totalEvents: scan.totalEvents,
    azureCount: scan.azureCount,
    whisperCount: scan.whisperCount,
    offlineRate: scan.offlineRate,
    thresholdExceeded: scan.exceedsThreshold,
    diagnosticsSessionId: diagnosticsSession?.id ?? null,
    currentBackend: scan.currentBackend,
    recommendation,
    generatedAt: new Date().toISOString(),
  };
}

// ── Pipeline runner ────────────────────────────────────────────────────────────

async function runSTTFallbackMonitorPipeline(
  fromTime: string,
  toTime: string,
  offlineThreshold: number,
  req: McpToolRequest
): Promise<STTFallbackReport> {
  const runId = randomUUID();
  const opts = { token: req.headers.authorization?.slice(7), requestId: req.id };

  req.log.info(
    { runId, stepId: 'scan', fromTime, toTime, offlineThreshold },
    'STTEventScannerAgent start'
  );
  const scan = await scanSttEvents(fromTime, toTime, offlineThreshold, opts);
  req.log.info(
    { runId, stepId: 'scan', totalEvents: scan.totalEvents, offlineRate: scan.offlineRate },
    'STTEventScannerAgent done'
  );

  req.log.info(
    { runId, stepId: 'diagnostics', exceedsThreshold: scan.exceedsThreshold },
    'DiagnosticsLaunchAgent start'
  );
  const diagnosticsSession = await launchDiagnosticsIfNeeded(scan, opts);
  req.log.info(
    { runId, stepId: 'diagnostics', sessionId: diagnosticsSession?.id ?? null },
    'DiagnosticsLaunchAgent done'
  );

  req.log.info({ runId, stepId: 'report' }, 'FallbackReportAgent start');
  const report = await buildFallbackReport(runId, scan, diagnosticsSession, fromTime, toTime, {
    requestId: req.id,
  });
  req.log.info(
    { runId, stepId: 'report', thresholdExceeded: report.thresholdExceeded },
    'FallbackReportAgent done'
  );

  return report;
}

// ── MCP tool registration ─────────────────────────────────────────────────────

registerTool({
  name: 'lysnrai.stt.monitorFallback',
  description:
    'A2A pipeline: scans LysnrAI STT telemetry events to measure the offline Whisper fallback rate vs Azure Speech SDK. If the offline rate exceeds the threshold, opens a diagnostics session. Returns an analysis report with provider split, current backend status, and actionable recommendations. Requires admin role.',
  requiredRole: 'admin',
  inputSchema: z.object({
    from: z.string().datetime().optional().describe('Start of telemetry window (default: 6h ago)'),
    to: z.string().datetime().optional().describe('End of telemetry window (default: now)'),
    offlineThreshold: z.coerce
      .number()
      .min(0)
      .max(1)
      .default(0.2)
      .describe('Fraction of events using offline Whisper that triggers an alert (default 0.20)'),
  }),
  async execute(args, req) {
    const now = new Date();
    const toTime = args.to ?? now.toISOString();
    const fromTime = args.from ?? new Date(now.getTime() - 6 * 60 * 60 * 1000).toISOString();
    return runSTTFallbackMonitorPipeline(fromTime, toTime, args.offlineThreshold, req);
  },
});
