/**
 * RouteSafetyAssessmentAgent — A2A pipeline for PeakPulse session safety assessment.
 *
 * Agent roster (4 steps):
 *   1. SessionDataAgent      — fetch session document (GPS track, weather, metrics)
 *   2. RouteProfileAgent     — fetch GPS track points + haptic events, build route summary
 *   3. SafetyAnalysisAgent   — call extraction service with route + weather context
 *   4. SafetyReportAgent     — assemble structured safety brief with risk level + recommendations
 *
 * MCP tools:
 *   peakpulse.sessions.assessSafety(sessionId) — 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 { peakpulseSessionGet, peakpulseRouteGet } from '../../lib/peakpulse-client.js';
import { extractionRun, type ExtractionItem } from '../../lib/extraction-client.js';

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

type RiskLevel = 'low' | 'moderate' | 'high' | 'critical';
type ActivityType = 'hiking' | 'skiing' | 'unknown';

interface SessionSnapshot {
  sessionId: string;
  activityType: ActivityType;
  locationName: string | null;
  durationMs: number | null;
  distanceM: number | null;
  elevationGainM: number | null;
  maxSpeedKmh: number | null;
  weather: {
    tempC: number | null;
    windSpeedKmh: number | null;
    uvIndex: number | null;
    condition: string | null;
  } | null;
}

interface RouteProfile {
  trackPointCount: number;
  elevationProfileSummary: string;
  maxElevationM: number | null;
  minElevationM: number | null;
  totalElevationGainM: number;
  steepestGradePercent: number | null;
  hapticEventCount: number;
}

interface SafetyAnalysis {
  riskLevel: RiskLevel;
  riskFactors: string[];
  extractedEntities: ExtractionItem[];
  extractionSkipped: boolean;
}

export interface RouteSafetyReport {
  runId: string;
  productId: 'peakpulse';
  sessionId: string;
  activityType: ActivityType;
  locationName: string | null;
  riskLevel: RiskLevel;
  riskFactors: string[];
  weatherSummary: string;
  routeSummary: string;
  recommendations: string[];
  extractedEntities: ExtractionItem[];
  generatedAt: string;
}

// ── Step 1: SessionDataAgent ───────────────────────────────────────────────────

async function fetchSessionData(
  sessionId: string,
  opts: { token?: string; requestId?: string }
): Promise<SessionSnapshot> {
  const session = await peakpulseSessionGet(sessionId, opts);
  const s = session as unknown as Record<string, unknown>;

  const metrics = (s['metrics'] as Record<string, unknown>) ?? {};
  const weather = s['weather'] as Record<string, unknown> | null | undefined;

  return {
    sessionId,
    activityType: (s['activityType'] as ActivityType) ?? 'unknown',
    locationName: (s['locationName'] as string) ?? null,
    durationMs: (metrics['durationMs'] as number) ?? null,
    distanceM: (metrics['distanceM'] as number) ?? null,
    elevationGainM: (metrics['elevationGainM'] as number) ?? null,
    maxSpeedKmh: (metrics['maxSpeedKmh'] as number) ?? null,
    weather: weather
      ? {
          tempC: (weather['tempC'] as number) ?? null,
          windSpeedKmh: (weather['windSpeedKmh'] as number) ?? null,
          uvIndex: (weather['uvIndex'] as number) ?? null,
          condition: (weather['condition'] as string) ?? null,
        }
      : null,
  };
}

// ── Step 2: RouteProfileAgent ─────────────────────────────────────────────────

async function buildRouteProfile(
  sessionId: string,
  opts: { token?: string; requestId?: string }
): Promise<RouteProfile> {
  try {
    const route = await peakpulseRouteGet(sessionId, opts);
    const r = route as unknown as Record<string, unknown>;
    const trackPoints = (r['trackPoints'] as Array<Record<string, unknown>>) ?? [];
    const hapticEvents = (r['hapticEvents'] as unknown[]) ?? [];

    const altitudes = trackPoints
      .map(p => p['altitude'] as number)
      .filter((a): a is number => typeof a === 'number');

    const maxElevationM = altitudes.length > 0 ? Math.max(...altitudes) : null;
    const minElevationM = altitudes.length > 0 ? Math.min(...altitudes) : null;

    let totalGain = 0;
    for (let i = 1; i < altitudes.length; i++) {
      const diff = altitudes[i]! - altitudes[i - 1]!;
      if (diff > 0) totalGain += diff;
    }

    let steepestGradePercent: number | null = null;
    for (let i = 1; i < trackPoints.length; i++) {
      const altDiff = Math.abs(
        ((trackPoints[i]!['altitude'] as number) ?? 0) -
          ((trackPoints[i - 1]!['altitude'] as number) ?? 0)
      );
      const distM = (trackPoints[i]!['distanceFromPrevM'] as number) ?? 10;
      if (distM > 0) {
        const grade = (altDiff / distM) * 100;
        if (steepestGradePercent === null || grade > steepestGradePercent) {
          steepestGradePercent = Math.round(grade * 10) / 10;
        }
      }
    }

    const elevationRange =
      maxElevationM !== null && minElevationM !== null
        ? `${minElevationM.toFixed(0)}m–${maxElevationM.toFixed(0)}m`
        : 'unknown';

    return {
      trackPointCount: trackPoints.length,
      elevationProfileSummary: `Elevation range: ${elevationRange}, total gain: ${totalGain.toFixed(0)}m`,
      maxElevationM,
      minElevationM,
      totalElevationGainM: Math.round(totalGain),
      steepestGradePercent,
      hapticEventCount: hapticEvents.length,
    };
  } catch {
    return {
      trackPointCount: 0,
      elevationProfileSummary: 'Route data unavailable',
      maxElevationM: null,
      minElevationM: null,
      totalElevationGainM: 0,
      steepestGradePercent: null,
      hapticEventCount: 0,
    };
  }
}

// ── Step 3: SafetyAnalysisAgent ───────────────────────────────────────────────

async function analyseSafety(
  snapshot: SessionSnapshot,
  profile: RouteProfile,
  opts: { token?: string; requestId?: string }
): Promise<SafetyAnalysis> {
  const riskFactors: string[] = [];

  // Rule-based risk scoring
  if (snapshot.weather?.uvIndex !== null && (snapshot.weather?.uvIndex ?? 0) >= 8) {
    riskFactors.push(`Very high UV index (${snapshot.weather!.uvIndex}) — sun protection critical`);
  }
  if (snapshot.weather?.windSpeedKmh !== null && (snapshot.weather?.windSpeedKmh ?? 0) >= 50) {
    riskFactors.push(
      `High wind speed (${snapshot.weather!.windSpeedKmh} km/h) — avalanche and fall risk`
    );
  }
  if (snapshot.weather?.tempC !== null && (snapshot.weather?.tempC ?? 20) <= -10) {
    riskFactors.push(`Extreme cold (${snapshot.weather!.tempC}°C) — hypothermia risk`);
  }
  if (profile.maxElevationM !== null && profile.maxElevationM >= 3000) {
    riskFactors.push(
      `High altitude (${profile.maxElevationM.toFixed(0)}m) — altitude sickness risk above 3000m`
    );
  }
  if (profile.totalElevationGainM >= 1200) {
    riskFactors.push(
      `Significant elevation gain (${profile.totalElevationGainM}m) — fatigue and overexertion risk`
    );
  }
  if (profile.steepestGradePercent !== null && profile.steepestGradePercent >= 40) {
    riskFactors.push(
      `Steep terrain detected (${profile.steepestGradePercent}% grade) — slip/fall risk`
    );
  }
  if (snapshot.activityType === 'skiing' && (snapshot.maxSpeedKmh ?? 0) >= 80) {
    riskFactors.push(
      `Very high ski speed recorded (${snapshot.maxSpeedKmh} km/h) — collision risk`
    );
  }

  const riskLevel: RiskLevel = (() => {
    if (riskFactors.length === 0) return 'low';
    if (riskFactors.length === 1) return 'moderate';
    if (riskFactors.length <= 3) return 'high';
    return 'critical';
  })();

  // Best-effort extraction for entity enrichment
  let extractedEntities: ExtractionItem[] = [];
  let extractionSkipped = false;

  if (riskLevel !== 'low' && profile.elevationProfileSummary !== 'Route data unavailable') {
    const context = [
      `Activity: ${snapshot.activityType}`,
      `Location: ${snapshot.locationName ?? 'unknown'}`,
      profile.elevationProfileSummary,
      snapshot.weather
        ? `Weather: ${snapshot.weather.condition ?? 'unknown'}, ${snapshot.weather.tempC ?? 'N/A'}°C, wind ${snapshot.weather.windSpeedKmh ?? 'N/A'} km/h, UV ${snapshot.weather.uvIndex ?? 'N/A'}`
        : 'Weather data unavailable',
      `Risk factors: ${riskFactors.join('; ')}`,
    ].join('. ');

    try {
      const result = await extractionRun({ text: context, taskId: 'triage' }, opts);
      extractedEntities = result.extractions ?? [];
    } catch {
      extractionSkipped = true;
    }
  } else {
    extractionSkipped = true;
  }

  return { riskLevel, riskFactors, extractedEntities, extractionSkipped };
}

// ── Step 4: SafetyReportAgent ─────────────────────────────────────────────────

function buildSafetyReport(
  runId: string,
  snapshot: SessionSnapshot,
  profile: RouteProfile,
  analysis: SafetyAnalysis
): RouteSafetyReport {
  const now = new Date().toISOString();

  const weatherSummary = snapshot.weather
    ? [
        snapshot.weather.condition ?? 'unknown conditions',
        snapshot.weather.tempC !== null ? `${snapshot.weather.tempC}°C` : null,
        snapshot.weather.windSpeedKmh !== null
          ? `wind ${snapshot.weather.windSpeedKmh} km/h`
          : null,
        snapshot.weather.uvIndex !== null ? `UV ${snapshot.weather.uvIndex}` : null,
      ]
        .filter(Boolean)
        .join(', ')
    : 'No weather data captured';

  const routeSummary = [
    profile.trackPointCount > 0 ? `${profile.trackPointCount} GPS points` : null,
    profile.maxElevationM !== null ? `max elevation ${profile.maxElevationM.toFixed(0)}m` : null,
    `${profile.totalElevationGainM}m gain`,
    profile.steepestGradePercent !== null
      ? `steepest grade ${profile.steepestGradePercent}%`
      : null,
  ]
    .filter(Boolean)
    .join(', ');

  const recommendations: string[] = [];
  if (analysis.riskLevel === 'low') {
    recommendations.push('Session appears safe. No action required.');
  } else {
    if (analysis.riskFactors.some(f => f.includes('UV'))) {
      recommendations.push('Apply SPF 50+ sunscreen; wear UV-blocking eyewear on future sessions.');
    }
    if (analysis.riskFactors.some(f => f.includes('wind') || f.includes('avalanche'))) {
      recommendations.push(
        'Check avalanche forecast before high-wind sessions. Carry beacon + probe + shovel.'
      );
    }
    if (analysis.riskFactors.some(f => f.includes('altitude'))) {
      recommendations.push(
        'Allow 24–48h acclimatisation above 3000m. Descend if headache or nausea develops.'
      );
    }
    if (analysis.riskFactors.some(f => f.includes('elevation gain'))) {
      recommendations.push(
        'Consider splitting high-gain routes across multiple days. Monitor heart rate.'
      );
    }
    if (analysis.riskFactors.some(f => f.includes('steep terrain'))) {
      recommendations.push(
        'Use trekking poles on steep descents. Consider crampons on icy gradients.'
      );
    }
    if (analysis.riskFactors.some(f => f.includes('ski speed'))) {
      recommendations.push(
        'Review speed zones and terrain for skill level. Consider guided assessment.'
      );
    }
    if (analysis.riskFactors.some(f => f.includes('cold') || f.includes('hypothermia'))) {
      recommendations.push(
        'Layer appropriately; carry emergency bivouac gear. Do not underestimate wind chill.'
      );
    }
  }

  return {
    runId,
    productId: 'peakpulse',
    sessionId: snapshot.sessionId,
    activityType: snapshot.activityType,
    locationName: snapshot.locationName,
    riskLevel: analysis.riskLevel,
    riskFactors: analysis.riskFactors,
    weatherSummary,
    routeSummary,
    recommendations,
    extractedEntities: analysis.extractedEntities,
    generatedAt: now,
  };
}

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

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

  req.log.info({ runId, stepId: 'session', sessionId }, 'SessionDataAgent start');
  const snapshot = await fetchSessionData(sessionId, opts);
  req.log.info(
    {
      runId,
      stepId: 'session',
      activityType: snapshot.activityType,
      hasWeather: !!snapshot.weather,
    },
    'SessionDataAgent done'
  );

  req.log.info({ runId, stepId: 'route', sessionId }, 'RouteProfileAgent start');
  const profile = await buildRouteProfile(sessionId, opts);
  req.log.info(
    {
      runId,
      stepId: 'route',
      trackPoints: profile.trackPointCount,
      elevGain: profile.totalElevationGainM,
    },
    'RouteProfileAgent done'
  );

  req.log.info({ runId, stepId: 'safety' }, 'SafetyAnalysisAgent start');
  const analysis = await analyseSafety(snapshot, profile, opts);
  req.log.info(
    {
      runId,
      stepId: 'safety',
      riskLevel: analysis.riskLevel,
      factors: analysis.riskFactors.length,
    },
    'SafetyAnalysisAgent done'
  );

  req.log.info({ runId, stepId: 'report' }, 'SafetyReportAgent start');
  const report = buildSafetyReport(runId, snapshot, profile, analysis);
  req.log.info({ runId, stepId: 'report', riskLevel: report.riskLevel }, 'SafetyReportAgent done');

  return report;
}

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

registerTool({
  name: 'peakpulse.sessions.assessSafety',
  description:
    'A2A pipeline: fetches GPS track + weather snapshot for a completed PeakPulse session, evaluates elevation, temperature, UV, wind, and speed risk factors, optionally enriches with extraction entities, and returns a structured safety brief with risk level and recommendations. Requires admin role.',
  requiredRole: 'admin',
  inputSchema: z.object({
    sessionId: z.string().min(1).describe('Completed session ID to assess'),
  }),
  async execute(args, req) {
    return runRouteSafetyPipeline(args.sessionId, req);
  },
});
