/**
 * ProtocolTuningAgent — A2A pipeline for NomGap fasting protocol optimization.
 *
 * Agent roster (3 steps):
 *   1. ProtocolStatsCollectorAgent — fetch all protocols + aggregate completion/abandonment stats
 *   2. AbandonmentAnalysisAgent    — identify protocols with high abandonment; run extraction for pattern analysis
 *   3. TuningReportAgent           — propose protocol parameter adjustments based on findings
 *
 * MCP tools:
 *   nomgap.protocols.tune() — run protocol tuning analysis
 */

import { randomUUID } from 'node:crypto';
import { z } from 'zod';
import { registerTool } from '../tools/registry.js';
import type { McpToolRequest } from '../tools/types.js';
import { nomgapProtocolsList, nomgapFastingGetStats } from '../../lib/nomgap-client.js';
import { extractionRun, type ExtractionItem } from '../../lib/extraction-client.js';

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

interface ProtocolStat {
  id: string;
  name: string;
  targetHours: number | null;
  completionRate: number | null;
  abandonmentRate: number | null;
  avgCompletedHours: number | null;
}

interface TuningRecommendation {
  protocolId: string;
  protocolName: string;
  issue: string;
  suggestion: string;
  priority: 'high' | 'medium' | 'low';
}

export interface ProtocolTuningReport {
  runId: string;
  productId: 'nomgap';
  protocolsAnalyzed: number;
  highAbandonmentCount: number;
  recommendations: TuningRecommendation[];
  extractedPatterns: ExtractionItem[];
  overallCompletionRate: number | null;
  summary: string;
  generatedAt: string;
}

// ── Step 1: ProtocolStatsCollectorAgent ───────────────────────────────────────

async function collectProtocolStats(opts: {
  token?: string;
  requestId?: string;
}): Promise<{ protocols: ProtocolStat[]; overallStats: Record<string, unknown> }> {
  try {
    const [protocolsResult, overallStats] = await Promise.all([
      nomgapProtocolsList(opts),
      nomgapFastingGetStats(opts).catch(() => ({}) as Record<string, unknown>),
    ]);

    const protocols: ProtocolStat[] = (
      protocolsResult.protocols as Array<Record<string, unknown>>
    ).map(p => ({
      id: String(p['id'] ?? ''),
      name: String(p['name'] ?? 'Unknown'),
      targetHours: typeof p['targetHours'] === 'number' ? p['targetHours'] : null,
      completionRate: typeof p['completionRate'] === 'number' ? p['completionRate'] : null,
      abandonmentRate: typeof p['abandonmentRate'] === 'number' ? p['abandonmentRate'] : null,
      avgCompletedHours: typeof p['avgCompletedHours'] === 'number' ? p['avgCompletedHours'] : null,
    }));

    return { protocols, overallStats: overallStats as Record<string, unknown> };
  } catch {
    return { protocols: [], overallStats: {} };
  }
}

// ── Step 2: AbandonmentAnalysisAgent ─────────────────────────────────────────

async function analyzeAbandonment(
  protocols: ProtocolStat[],
  opts: { token?: string; requestId?: string }
): Promise<{ highAbandonment: ProtocolStat[]; extractedPatterns: ExtractionItem[] }> {
  const highAbandonment = protocols.filter(
    p => p.abandonmentRate !== null && p.abandonmentRate > 0.4
  );

  let extractedPatterns: ExtractionItem[] = [];

  if (highAbandonment.length > 0) {
    const text = highAbandonment
      .map(
        p =>
          `Protocol "${p.name}": target ${p.targetHours ?? '?'}h, abandonment rate ${
            p.abandonmentRate !== null ? (p.abandonmentRate * 100).toFixed(0) : '?'
          }%, avg completed ${p.avgCompletedHours?.toFixed(1) ?? '?'}h.`
      )
      .join('\n');

    try {
      const result = await extractionRun({ text, taskId: 'memory-insight' }, opts);
      extractedPatterns = result.extractions;
    } catch {
      // best-effort
    }
  }

  return { highAbandonment, extractedPatterns };
}

// ── Step 3: TuningReportAgent ─────────────────────────────────────────────────

function buildTuningRecommendations(
  protocols: ProtocolStat[],
  highAbandonment: ProtocolStat[]
): TuningRecommendation[] {
  const recommendations: TuningRecommendation[] = [];

  for (const p of highAbandonment) {
    const abandonPct = p.abandonmentRate !== null ? (p.abandonmentRate * 100).toFixed(0) : '?';

    if (
      p.avgCompletedHours !== null &&
      p.targetHours !== null &&
      p.avgCompletedHours < p.targetHours * 0.6
    ) {
      recommendations.push({
        protocolId: p.id,
        protocolName: p.name,
        issue: `${abandonPct}% abandonment rate; users average only ${p.avgCompletedHours.toFixed(1)}h of ${p.targetHours}h target.`,
        suggestion: `Consider reducing target to ${Math.ceil(p.avgCompletedHours * 1.2)}h for a "stepped" version, or add milestone notifications at ${Math.round(p.avgCompletedHours)}h to encourage completion.`,
        priority: p.abandonmentRate! > 0.6 ? 'high' : 'medium',
      });
    } else {
      recommendations.push({
        protocolId: p.id,
        protocolName: p.name,
        issue: `${abandonPct}% abandonment rate.`,
        suggestion:
          'Review safety warnings frequency and check if users receive adequate pre-fast coaching. Consider adding a "Modified" variant with a lower target.',
        priority: 'medium',
      });
    }
  }

  const healthyProtocols = protocols.filter(
    p =>
      p.completionRate !== null &&
      p.completionRate < 0.3 &&
      !highAbandonment.find(h => h.id === p.id)
  );
  for (const p of healthyProtocols) {
    recommendations.push({
      protocolId: p.id,
      protocolName: p.name,
      issue: `Low completion rate (${p.completionRate !== null ? (p.completionRate * 100).toFixed(0) : '?'}%) with no abandonment data — may not be discoverable.`,
      suggestion:
        'Improve protocol description and add use-case tags to increase discoverability in the protocol selection UI.',
      priority: 'low',
    });
  }

  return recommendations;
}

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

async function runProtocolTuningPipeline(req: McpToolRequest): Promise<ProtocolTuningReport> {
  const runId = randomUUID();
  const opts = { token: req.headers.authorization?.slice(7), requestId: req.id };

  req.log.info({ runId, stepId: 'collect' }, 'ProtocolStatsCollectorAgent start');
  const { protocols, overallStats } = await collectProtocolStats(opts);
  req.log.info(
    { runId, stepId: 'collect', protocolCount: protocols.length },
    'ProtocolStatsCollectorAgent done'
  );

  req.log.info(
    { runId, stepId: 'analyze', highAbandonmentCheck: protocols.length },
    'AbandonmentAnalysisAgent start'
  );
  const { highAbandonment, extractedPatterns } = await analyzeAbandonment(protocols, opts);
  req.log.info(
    {
      runId,
      stepId: 'analyze',
      highAbandonment: highAbandonment.length,
      patterns: extractedPatterns.length,
    },
    'AbandonmentAnalysisAgent done'
  );

  req.log.info({ runId, stepId: 'tune' }, 'TuningReportAgent start');
  const recommendations = buildTuningRecommendations(protocols, highAbandonment);
  const overallCompletionRate =
    typeof overallStats['completionRate'] === 'number'
      ? (overallStats['completionRate'] as number)
      : null;

  const summary =
    protocols.length === 0
      ? 'No protocols found to analyze.'
      : `Analyzed ${protocols.length} protocols. ${highAbandonment.length} with high abandonment (>40%). ${recommendations.length} tuning recommendation(s) generated.`;

  req.log.info({ runId, stepId: 'tune', summary }, 'TuningReportAgent done');

  return {
    runId,
    productId: 'nomgap',
    protocolsAnalyzed: protocols.length,
    highAbandonmentCount: highAbandonment.length,
    recommendations,
    extractedPatterns,
    overallCompletionRate,
    summary,
    generatedAt: new Date().toISOString(),
  };
}

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

registerTool({
  name: 'nomgap.protocols.tune',
  description:
    'A2A pipeline: analyzes NomGap fasting protocols for high abandonment rates, uses extraction to identify patterns in abandonment data, and proposes concrete parameter adjustments (target duration reduction, milestone notifications, variant creation). Returns prioritized tuning recommendations. Requires admin role.',
  requiredRole: 'admin',
  inputSchema: z.object({}),
  async execute(_args, req) {
    return runProtocolTuningPipeline(req);
  },
});
