import { getLocationName, type GeoConvergenceAlert } from './geo-convergence';
import type { CountryScore } from './country-instability';
import { getLatestSanctionsPressure, type SanctionsPressureResult } from './sanctions-pressure';
import { getLatestRadiationWatch, type RadiationObservation } from './radiation';
import type { CascadeResult, CascadeImpactLevel } from '@/types';
import { calculateCII, isInLearningMode } from './country-instability';
import { getCountryNameByCode } from './country-geometry';
import { t } from '@/services/i18n';
import type { TheaterPostureSummary } from '@/services/military-surge';

export type AlertPriority = 'critical' | 'high' | 'medium' | 'low';
export type AlertType = 'convergence' | 'cii_spike' | 'cascade' | 'sanctions' | 'radiation' | 'composite';

export interface UnifiedAlert {
  id: string;
  type: AlertType;
  priority: AlertPriority;
  title: string;
  summary: string;
  components: {
    convergence?: GeoConvergenceAlert;
    ciiChange?: CIIChangeAlert;
    cascade?: CascadeAlert;
    sanctions?: SanctionsAlert;
    radiation?: RadiationAlert;
  };
  location?: { lat: number; lon: number };
  countries: string[];
  timestamp: Date;
}

export interface CIIChangeAlert {
  country: string;
  countryName: string;
  previousScore: number;
  currentScore: number;
  change: number;
  level: CountryScore['level'];
  driver: string;
}

export interface CascadeAlert {
  sourceId: string;
  sourceName: string;
  sourceType: string;
  countriesAffected: number;
  highestImpact: CascadeImpactLevel;
}


export interface SanctionsAlert {
  countryCode: string;
  countryName: string;
  entryCount: number;
  newEntryCount: number;
  topProgram: string;
  topProgramCount: number;
  vesselCount: number;
  aircraftCount: number;
  totalCount: number;
  datasetDate: number | null;
}

export interface RadiationAlert {
  siteId: string;
  siteName: string;
  country: string;
  value: number;
  unit: string;
  baselineValue: number;
  delta: number;
  zScore: number;
  severity: 'elevated' | 'spike';
  confidence: RadiationObservation['confidence'];
  corroborated: boolean;
  conflictingSources: boolean;
  convertedFromCpm: boolean;
  sourceCount: number;
  contributingSources: RadiationObservation['contributingSources'];
  anomalyCount: number;
  elevatedCount: number;
  spikeCount: number;
  corroboratedCount: number;
  lowConfidenceCount: number;
  conflictingCount: number;
}

export interface StrategicRiskOverview {
  convergenceAlerts: number;
  avgCIIDeviation: number;
  infrastructureIncidents: number;
  compositeScore: number;
  trend: 'escalating' | 'stable' | 'de-escalating';
  topRisks: string[];
  topConvergenceZones: { cellId: string; lat: number; lon: number; score: number }[];
  unstableCountries: CountryScore[];
  timestamp: Date;
}

const alerts: UnifiedAlert[] = [];
const previousCIIScores = new Map<string, number>();
const ALERT_MERGE_WINDOW_MS = 2 * 60 * 60 * 1000;
const ALERT_MERGE_DISTANCE_KM = 200;

let alertIdCounter = 0;
function generateAlertId(): string {
  return `alert-${Date.now()}-${++alertIdCounter}`;
}

function haversineDistance(
  lat1: number,
  lon1: number,
  lat2: number,
  lon2: number
): number {
  const R = 6371;
  const dLat = ((lat2 - lat1) * Math.PI) / 180;
  const dLon = ((lon2 - lon1) * Math.PI) / 180;
  const a =
    Math.sin(dLat / 2) * Math.sin(dLat / 2) +
    Math.cos((lat1 * Math.PI) / 180) *
      Math.cos((lat2 * Math.PI) / 180) *
      Math.sin(dLon / 2) *
      Math.sin(dLon / 2);
  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
  return R * c;
}

function getPriorityFromCIIChange(change: number, level: CountryScore['level']): AlertPriority {
  const absChange = Math.abs(change);
  // Match CII thresholds: critical at 81+, high at 66+
  if (level === 'critical') return 'critical';
  if (level === 'high' || absChange >= 30) return 'high';
  if (level === 'elevated' || absChange >= 15) return 'medium';
  return 'low';
}

function getPriorityFromCascadeImpact(impact: CascadeImpactLevel, count: number): AlertPriority {
  if (impact === 'critical' || (impact === 'high' && count >= 3)) return 'critical';
  if (impact === 'high' || count >= 5) return 'high';
  if (impact === 'medium' || count >= 3) return 'medium';
  return 'low';
}

function getPriorityFromConvergence(score: number, typeCount: number): AlertPriority {
  // Convergence: 4+ event types or score 90+ = critical, 3 types or 70+ = high
  if (typeCount >= 4 || score >= 90) return 'critical';
  if (typeCount >= 3 || score >= 70) return 'high';
  if (score >= 50) return 'medium';
  return 'low';
}


function getPriorityFromSanctions(data: SanctionsPressureResult): AlertPriority {
  const leadEntryCount = data.countries[0]?.entryCount ?? 0;
  if (data.newEntryCount >= 10) return 'critical';
  if (data.newEntryCount >= 3 || leadEntryCount >= 60) return 'high';
  if (data.newEntryCount >= 1 || leadEntryCount >= 25) return 'medium';
  return 'low';
}

function getPriorityFromRadiation(observation: RadiationObservation, spikeCount: number): AlertPriority {
  let score = 0;
  if (observation.severity === 'spike') score += 4;
  else if (observation.severity === 'elevated') score += 2;
  if (observation.corroborated) score += 2;
  if (observation.confidence === 'high') score += 2;
  else if (observation.confidence === 'medium') score += 1;
  if (observation.conflictingSources) score -= 2;
  if (observation.convertedFromCpm) score -= 1;
  if (spikeCount > 1 && observation.corroborated) score += 1;
  if (score >= 7) return 'critical';
  if (score >= 4) return 'high';
  if (score >= 2) return 'medium';
  return 'low';
}

function buildConvergenceAlert(convergence: GeoConvergenceAlert, alertId: string): UnifiedAlert {
  const location = getCountriesNearLocation(convergence.lat, convergence.lon).join(', ') || 'Unknown';
  return {
    id: alertId,
    type: 'convergence',
    priority: getPriorityFromConvergence(convergence.score, convergence.types.length),
    title: t('alerts.geoAlert', { location }),
    summary: t('alerts.eventsDetected', { count: convergence.totalEvents, lat: convergence.lat.toFixed(1), lon: convergence.lon.toFixed(1) }),
    components: { convergence },
    location: { lat: convergence.lat, lon: convergence.lon },
    countries: getCountriesNearLocation(convergence.lat, convergence.lon),
    timestamp: new Date(),
  };
}

export function createConvergenceAlert(convergence: GeoConvergenceAlert): UnifiedAlert {
  const alertId = `conv-${convergence.cellId}`;
  const alert = buildConvergenceAlert(convergence, alertId);
  return addAndMergeAlert(alert);
}

export function createCIIAlert(
  country: string,
  countryName: string,
  previousScore: number,
  currentScore: number,
  level: CountryScore['level'],
  driver: string
): UnifiedAlert | null {
  const change = currentScore - previousScore;
  if (Math.abs(change) < 10) return null;

  const ciiChange: CIIChangeAlert = {
    country,
    countryName,
    previousScore,
    currentScore,
    change,
    level,
    driver,
  };

  const changeStr = change > 0 ? `+${change}` : String(change);
  const summaryKey = change > 0 ? 'alerts.indexRose' : 'alerts.indexFell';

  const alert: UnifiedAlert = {
    id: `cii-${country}`, // Stable ID for deduplication by country
    type: 'cii_spike',
    priority: getPriorityFromCIIChange(change, level),
    title: t(change > 0 ? 'alerts.instabilityRising' : 'alerts.instabilityFalling', { country: countryName }),
    summary: t(summaryKey, { from: previousScore, to: currentScore, change: changeStr, driver }),
    components: { ciiChange },
    countries: [country],
    timestamp: new Date(),
  };

  return addAndMergeAlert(alert);
}

export function createCascadeAlert(cascade: CascadeResult): UnifiedAlert | null {
  if (cascade.countriesAffected.length === 0) return null;

  const highestImpact = cascade.countriesAffected[0]?.impactLevel || 'low';
  const cascadeAlert: CascadeAlert = {
    sourceId: cascade.source.id,
    sourceName: cascade.source.name,
    sourceType: cascade.source.type,
    countriesAffected: cascade.countriesAffected.length,
    highestImpact,
  };

  const alert: UnifiedAlert = {
    id: generateAlertId(),
    type: 'cascade',
    priority: getPriorityFromCascadeImpact(highestImpact, cascade.countriesAffected.length),
    title: t('alerts.infraAlert', { name: cascade.source.name }),
    summary: t('alerts.countriesAffected', { count: cascade.countriesAffected.length, impact: highestImpact }),
    components: { cascade: cascadeAlert },
    location: cascade.source.coordinates
      ? { lat: cascade.source.coordinates[1], lon: cascade.source.coordinates[0] }
      : undefined,
    countries: cascade.countriesAffected.map(c => c.country),
    timestamp: new Date(),
  };

  return addAndMergeAlert(alert);
}

function createSanctionsAlert(): UnifiedAlert | null {
  const pressure = getLatestSanctionsPressure();
  if (!pressure || pressure.totalCount === 0) {
    for (let i = alerts.length - 1; i >= 0; i--) {
      if (alerts[i]?.type === 'sanctions') alerts.splice(i, 1);
    }
    return null;
  }

  const leadCountry = [...pressure.countries]
    .sort((a, b) => b.newEntryCount - a.newEntryCount || b.entryCount - a.entryCount)[0];
  if (!leadCountry) return null;
  if (pressure.newEntryCount === 0 && leadCountry.entryCount < 25) return null;

  const leadProgram = [...pressure.programs]
    .sort((a, b) => b.newEntryCount - a.newEntryCount || b.entryCount - a.entryCount)[0];

  const sanctions: SanctionsAlert = {
    countryCode: leadCountry.countryCode,
    countryName: leadCountry.countryName,
    entryCount: leadCountry.entryCount,
    newEntryCount: leadCountry.newEntryCount,
    topProgram: leadProgram?.program || 'Unspecified',
    topProgramCount: leadProgram?.entryCount || 0,
    vesselCount: leadCountry.vesselCount,
    aircraftCount: leadCountry.aircraftCount,
    totalCount: pressure.totalCount,
    datasetDate: pressure.datasetDate?.getTime() ?? null,
  };

  const summary = pressure.newEntryCount > 0
    ? `${pressure.newEntryCount} new OFAC designation${pressure.newEntryCount === 1 ? '' : 's'} detected. Pressure is highest around ${leadCountry.countryName} (${leadCountry.entryCount}), with ${leadProgram?.program || 'unspecified'} leading program activity.`
    : `${leadCountry.countryName} has ${leadCountry.entryCount} OFAC-linked designations in the current dataset, led by ${leadProgram?.program || 'unspecified'} activity.`;

  return addAndMergeAlert({
    id: 'sanctions-pressure',
    type: 'sanctions',
    priority: getPriorityFromSanctions(pressure),
    title: pressure.newEntryCount > 0
      ? `Sanctions pressure rising around ${leadCountry.countryName}`
      : `Persistent sanctions pressure around ${leadCountry.countryName}`,
    summary,
    components: { sanctions },
    countries: [leadCountry.countryCode],
    timestamp: pressure.fetchedAt,
  });
}

function getRadiationRank(observation: RadiationObservation): number {
  const severityRank = observation.severity === 'spike' ? 2 : observation.severity === 'elevated' ? 1 : 0;
  const confidenceRank = observation.confidence === 'high' ? 2 : observation.confidence === 'medium' ? 1 : 0;
  const corroborationBonus = observation.corroborated ? 300 : 0;
  const conflictPenalty = observation.conflictingSources ? 250 : 0;
  return severityRank * 1000 + confidenceRank * 200 + corroborationBonus + observation.zScore * 100 + observation.delta - conflictPenalty;
}

function createRadiationAlert(): UnifiedAlert | null {
  const watch = getLatestRadiationWatch();
  if (!watch || watch.summary.anomalyCount === 0) {
    for (let i = alerts.length - 1; i >= 0; i--) {
      if (alerts[i]?.type === 'radiation') alerts.splice(i, 1);
    }
    return null;
  }

  const anomalies = watch.observations.filter(o => o.severity !== 'normal');
  if (anomalies.length === 0) return null;

  const strongest = [...anomalies].sort((a, b) => getRadiationRank(b) - getRadiationRank(a))[0];
  if (!strongest) return null;

  const countries = strongest.country ? [strongest.country] : getCountriesNearLocation(strongest.lat, strongest.lon);
  const radiation: RadiationAlert = {
    siteId: strongest.id,
    siteName: strongest.location,
    country: strongest.country,
    value: strongest.value,
    unit: strongest.unit,
    baselineValue: strongest.baselineValue,
    delta: strongest.delta,
    zScore: strongest.zScore,
    severity: strongest.severity === 'spike' ? 'spike' : 'elevated',
    confidence: strongest.confidence,
    corroborated: strongest.corroborated,
    conflictingSources: strongest.conflictingSources,
    convertedFromCpm: strongest.convertedFromCpm,
    sourceCount: strongest.sourceCount,
    contributingSources: strongest.contributingSources,
    anomalyCount: watch.summary.anomalyCount,
    elevatedCount: watch.summary.elevatedCount,
    spikeCount: watch.summary.spikeCount,
    corroboratedCount: watch.summary.corroboratedCount,
    lowConfidenceCount: watch.summary.lowConfidenceCount,
    conflictingCount: watch.summary.conflictingCount,
  };

  const qualifier = strongest.corroborated
    ? 'Confirmed'
    : strongest.conflictingSources
      ? 'Conflicting'
      : strongest.confidence === 'low'
        ? 'Potential'
        : 'Elevated';
  const title = strongest.severity === 'spike'
    ? `${qualifier} radiation spike at ${strongest.location}`
    : `${qualifier} radiation anomaly at ${strongest.location}`;
  const confidenceClause = strongest.corroborated
    ? `Confirmed by ${strongest.contributingSources.join(' + ')}.`
    : strongest.conflictingSources
      ? `Sources disagree across ${strongest.contributingSources.join(' + ')}.`
      : `Confidence is ${strongest.confidence}.`;
  const summary = watch.summary.spikeCount > 0
    ? `${watch.summary.spikeCount} spike and ${watch.summary.elevatedCount} elevated reading${watch.summary.anomalyCount === 1 ? '' : 's'} detected, with ${watch.summary.corroboratedCount} confirmed anomaly${watch.summary.corroboratedCount === 1 ? '' : 'ies'}. Highest site is ${strongest.location} (${strongest.value.toFixed(1)} ${strongest.unit}, +${strongest.delta.toFixed(1)} vs baseline). ${confidenceClause}`
    : `${watch.summary.elevatedCount} elevated radiation reading${watch.summary.elevatedCount === 1 ? '' : 's'} detected, with ${watch.summary.corroboratedCount} confirmed anomaly${watch.summary.corroboratedCount === 1 ? '' : 'ies'}. Highest site is ${strongest.location} (${strongest.value.toFixed(1)} ${strongest.unit}, +${strongest.delta.toFixed(1)} vs baseline). ${confidenceClause}`;

  return addAndMergeAlert({
    id: 'radiation-watch',
    type: 'radiation',
    priority: getPriorityFromRadiation(strongest, watch.summary.spikeCount),
    title,
    summary,
    components: { radiation },
    location: { lat: strongest.lat, lon: strongest.lon },
    countries,
    timestamp: strongest.observedAt,
  });
}

function shouldMergeAlerts(a: UnifiedAlert, b: UnifiedAlert): boolean {
  const sameCountry = a.countries.some(c => b.countries.includes(c));
  const sameTime =
    Math.abs(a.timestamp.getTime() - b.timestamp.getTime()) < ALERT_MERGE_WINDOW_MS;
  const sameLocation = !!(
    a.location &&
    b.location &&
    haversineDistance(a.location.lat, a.location.lon, b.location.lat, b.location.lon) <
      ALERT_MERGE_DISTANCE_KM
  );

  return (sameCountry || sameLocation) && sameTime;
}

function mergeAlerts(existing: UnifiedAlert, incoming: UnifiedAlert): UnifiedAlert {
  const merged: UnifiedAlert = {
    id: existing.id,
    type: 'composite',
    priority: getHigherPriority(existing.priority, incoming.priority),
    title: generateCompositeTitle(existing, incoming),
    summary: generateCompositeSummary(existing, incoming),
    components: {
      ...existing.components,
      ...incoming.components,
    },
    location: existing.location || incoming.location,
    countries: [...new Set([...existing.countries, ...incoming.countries])],
    timestamp: new Date(Math.max(existing.timestamp.getTime(), incoming.timestamp.getTime())),
  };

  return merged;
}

function getHigherPriority(a: AlertPriority, b: AlertPriority): AlertPriority {
  const order: AlertPriority[] = ['critical', 'high', 'medium', 'low'];
  return order.indexOf(a) <= order.indexOf(b) ? a : b;
}

function getCountryDisplayName(code: string): string {
  return getCountryNameByCode(code) || code;
}

function generateCompositeTitle(a: UnifiedAlert, b: UnifiedAlert): string {
  const ciiChange = a.components.ciiChange || b.components.ciiChange;
  if (ciiChange) {
    return t(ciiChange.change > 0 ? 'alerts.instabilityRising' : 'alerts.instabilityFalling', { country: ciiChange.countryName });
  }

  if (a.components.convergence || b.components.convergence) {
    if (a.components.sanctions || b.components.sanctions) {
    const sanctions = a.components.sanctions || b.components.sanctions;
    if (sanctions) return `Sanctions pressure: ${sanctions.countryName}`;
  }

  const countryCode = a.countries[0] || b.countries[0];
    const location = countryCode ? getCountryDisplayName(countryCode) : t('alerts.multipleRegions');
    return t('alerts.geoAlert', { location });
  }

  if (a.components.cascade || b.components.cascade) {
    return t('alerts.cascadeAlert');
  }

  if (a.components.sanctions || b.components.sanctions) {
    const sanctions = a.components.sanctions || b.components.sanctions;
    if (sanctions) return `Sanctions pressure: ${sanctions.countryName}`;
  }

  const countryCode = a.countries[0] || b.countries[0];
  const location = countryCode ? getCountryDisplayName(countryCode) : t('alerts.multipleRegions');
  return t('alerts.alert', { location });
}

function generateCompositeSummary(a: UnifiedAlert, b: UnifiedAlert): string {
  // For CII alerts, combine into a single narrative
  const ciiA = a.components.ciiChange;
  const ciiB = b.components.ciiChange;

  if (ciiA && ciiB && ciiA.country === ciiB.country) {
    // Same country, multiple updates - show the progression
    const latest = ciiB.currentScore > ciiA.currentScore ? ciiB : ciiA;
    const earliest = ciiB.currentScore > ciiA.currentScore ? ciiA : ciiB;
    const totalChange = latest.currentScore - earliest.previousScore;
    const changeStr = totalChange > 0 ? `+${totalChange}` : `${totalChange}`;
    const summaryKey = totalChange > 0 ? 'alerts.indexRose' : 'alerts.indexFell';
    return t(summaryKey, { from: earliest.previousScore, to: latest.currentScore, change: changeStr, driver: latest.driver });
  }

  // Otherwise combine summaries — limit to avoid unbounded growth
  // Extract unique bullet segments from both summaries (they may already contain ' • ' from prior merges)
  const seen = new Set<string>();
  const parts: string[] = [];
  for (const s of [a.summary, b.summary]) {
    if (!s) continue;
    for (const seg of s.split(' • ')) {
      const trimmed = seg.trim();
      if (trimmed && !seen.has(trimmed)) {
        seen.add(trimmed);
        parts.push(trimmed);
      }
    }
  }
  // Cap at 3 evidence items to prevent wall-of-text
  if (parts.length > 3) {
    const extra = parts.length - 3;
    return parts.slice(0, 3).join(' • ') + ` (+${extra} more)`;
  }
  return parts.join(' • ');
}

function addAndMergeAlert(alert: UnifiedAlert): UnifiedAlert {
  // First check for existing alert with same ID (stable deduplication)
  const existingByIdIndex = alerts.findIndex(a => a.id === alert.id);
  if (existingByIdIndex !== -1) {
    const existing = alerts[existingByIdIndex]!;
    // Update existing alert with new data, keeping higher priority
    const updated: UnifiedAlert = {
      ...alert,
      priority: getHigherPriority(existing.priority, alert.priority),
      timestamp: new Date(Math.max(existing.timestamp.getTime(), alert.timestamp.getTime())),
    };
    alerts[existingByIdIndex] = updated;
    return updated;
  }

  // Then check for merge candidates based on location/country
  for (let i = 0; i < alerts.length; i++) {
    const existing = alerts[i];
    if (existing && shouldMergeAlerts(existing, alert)) {
      const merged = mergeAlerts(existing, alert);
      alerts[i] = merged;
      return merged;
    }
  }

  alerts.unshift(alert);
  if (alerts.length > 50) alerts.pop();
  document.dispatchEvent(new CustomEvent('wm:intelligence-updated'));
  return alert;
}

function getCountriesNearLocation(lat: number, lon: number): string[] {
  const countries: string[] = [];

  const regionCountries = {
    europe: ['DE', 'FR', 'GB', 'PL', 'UA'],
    middle_east: ['IR', 'IL', 'SA', 'TR', 'SY', 'YE'],
    east_asia: ['CN', 'TW', 'KP'],
    south_asia: ['IN', 'PK', 'MM'],
    americas: ['US', 'VE'],
  } as const;

  if (lat > 35 && lat < 70 && lon > -10 && lon < 40) {
    countries.push(...regionCountries.europe);
  } else if (lat > 15 && lat < 45 && lon > 25 && lon < 65) {
    countries.push(...regionCountries.middle_east);
  } else if (lat > 15 && lat < 55 && lon > 100 && lon < 145) {
    countries.push(...regionCountries.east_asia);
  } else if (lat > 5 && lat < 40 && lon > 65 && lon < 100) {
    countries.push(...regionCountries.south_asia);
  } else if (lat > -60 && lat < 70 && lon > -130 && lon < -30) {
    countries.push(...regionCountries.americas);
  }

  return countries;
}

export function checkCIIChanges(): UnifiedAlert[] {
  const newAlerts: UnifiedAlert[] = [];
  const scores = calculateCII();

  // Skip alerting during learning mode - data not yet reliable
  const inLearning = isInLearningMode();

  for (const score of scores) {
    const previous = previousCIIScores.get(score.code) ?? score.score;
    const change = score.score - previous;

    // Only emit alerts after learning period completes
    if (!inLearning && Math.abs(change) >= 10) {
      const driver = getHighestComponent(score);
      const alert = createCIIAlert(
        score.code,
        score.name,
        previous,
        score.score,
        score.level,
        driver
      );
      if (alert) newAlerts.push(alert);
    }

    previousCIIScores.set(score.code, score.score);
  }

  return newAlerts;
}

function getHighestComponent(score: CountryScore): string {
  const { unrest, security, information } = score.components;
  if (unrest >= security && unrest >= information) return 'Civil Unrest';
  if (security >= information) return 'Security Activity';
  return 'Information Velocity';
}

// Populate alerts from convergence and CII data
function updateAlerts(convergenceAlerts: GeoConvergenceAlert[]): void {
  // Prune old alerts (older than 24 hours)
  const cutoff = Date.now() - 24 * 60 * 60 * 1000;
  while (alerts.length > 0 && alerts[0]!.timestamp.getTime() < cutoff) {
    alerts.shift();
  }

  // Add convergence alerts (addAndMergeAlert handles deduplication by stable ID)
  for (const conv of convergenceAlerts) {
    createConvergenceAlert(conv);
  }

  // Check for CII changes (alerts are added internally via addAndMergeAlert)
  checkCIIChanges();
  createSanctionsAlert();
  createRadiationAlert();

  // Sort by timestamp (newest first) and limit to 100
  alerts.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
  if (alerts.length > 100) {
    alerts.length = 100;
  }
}

export function calculateStrategicRiskOverview(
  convergenceAlerts: GeoConvergenceAlert[],
  theaterPostures?: TheaterPostureSummary[],
  breakingAlertScore?: number,
  theaterStaleFactor?: number
): StrategicRiskOverview {
  const ciiScores = calculateCII();

  // Update the alerts array with current data
  updateAlerts(convergenceAlerts);

  const ciiRiskScore = calculateCIIRiskScore(ciiScores);
  const sanctionsPressure = getLatestSanctionsPressure();

  const sanctionsScore = sanctionsPressure
    ? Math.min(
        10,
        sanctionsPressure.newEntryCount * 2 +
        Math.min(4, (sanctionsPressure.countries[0]?.entryCount ?? 0) / 20) +
        sanctionsPressure.vesselCount * 0.3 +
        sanctionsPressure.aircraftCount * 0.3
      )
    : 0;

  const radiationWatch = getLatestRadiationWatch();
  const radiationScore = radiationWatch
    ? Math.min(
        12,
        radiationWatch.summary.spikeCount * 4 +
        radiationWatch.summary.elevatedCount * 2 +
        radiationWatch.summary.corroboratedCount * 3 -
        radiationWatch.summary.lowConfidenceCount -
        radiationWatch.summary.conflictingCount
      )
    : 0;

  // Weights for composite score
  const convergenceWeight = 0.3;  // Geo convergence of multiple event types
  const ciiWeight = 0.5;          // Country instability (main driver)
  const infraWeight = 0.2;        // Infrastructure incidents

  const convergenceScore = Math.min(100, convergenceAlerts.length * 25);
  const infraScore = Math.min(100, countInfrastructureIncidents() * 25);

  // Theater posture boost from raw asset counts (avoids CII double-count)
  let theaterBoost = 0;
  if (theaterPostures && theaterPostures.length > 0) {
    for (const p of theaterPostures) {
      if (p.totalAircraft + p.totalVessels === 0) continue;
      const assetScore = Math.min(10, Math.floor((p.totalAircraft + p.totalVessels) / 5));
      theaterBoost += p.strikeCapable ? assetScore + 5 : assetScore;
    }
    theaterBoost = Math.min(25, theaterBoost);
  }
  theaterBoost = Math.round(theaterBoost * (theaterStaleFactor ?? 1));

  // Breaking news severity boost (pre-computed by panel)
  const breakingBoost = Math.min(15, breakingAlertScore ?? 0);

  const composite = Math.min(100, Math.round(
    convergenceScore * convergenceWeight +
    ciiRiskScore * ciiWeight +
    infraScore * infraWeight +
    theaterBoost +
    breakingBoost +
    sanctionsScore +
    radiationScore
  ));

  const trend = determineTrend(composite);

  // Top country score for display
  const topCountry = ciiScores[0];
  const topCIIScore = topCountry ? topCountry.score : 0;

  return {
    convergenceAlerts: convergenceAlerts.length,
    avgCIIDeviation: topCIIScore,  // Now shows top country score
    infrastructureIncidents: countInfrastructureIncidents(),
    compositeScore: composite,
    trend,
    topRisks: identifyTopRisks(convergenceAlerts, ciiScores, sanctionsPressure, radiationWatch?.observations ?? []),
    topConvergenceZones: convergenceAlerts
      .slice(0, 3)
      .map(a => ({ cellId: a.cellId, lat: a.lat, lon: a.lon, score: a.score })),
    unstableCountries: ciiScores.filter(s => s.score >= 50).slice(0, 5),
    timestamp: new Date(),
  };
}

function calculateCIIRiskScore(scores: CountryScore[]): number {
  if (scores.length === 0) return 0;

  // Use top 5 highest-scoring countries to determine risk
  // Don't dilute with stable countries
  const sorted = [...scores].sort((a, b) => b.score - a.score);
  const top5 = sorted.slice(0, 5);

  // Weighted: highest country contributes most
  // Top country: 40%, 2nd: 25%, 3rd: 20%, 4th: 10%, 5th: 5%
  const weights = [0.4, 0.25, 0.2, 0.1, 0.05];
  let weightedScore = 0;

  for (let i = 0; i < top5.length; i++) {
    const country = top5[i];
    const weight = weights[i];
    if (country && weight !== undefined) {
      weightedScore += country.score * weight;
    }
  }

  // Count of elevated countries (score >= 50) adds bonus
  const elevatedCount = scores.filter(s => s.score >= 50).length;
  const elevatedBonus = Math.min(20, elevatedCount * 5);

  return Math.min(100, weightedScore + elevatedBonus);
}

let previousCompositeScore: number | null = null;
function determineTrend(current: number): 'escalating' | 'stable' | 'de-escalating' {
  if (previousCompositeScore === null) {
    previousCompositeScore = current;
    return 'stable';
  }
  const diff = current - previousCompositeScore;
  previousCompositeScore = current;
  if (diff >= 3) return 'escalating';
  if (diff <= -3) return 'de-escalating';
  return 'stable';
}

function countInfrastructureIncidents(): number {
  return alerts.filter(a => a.components.cascade).length;
}

function identifyTopRisks(
  convergence: GeoConvergenceAlert[],
  cii: CountryScore[],
  sanctions: SanctionsPressureResult | null,
  radiation: RadiationObservation[]
): string[] {
  const risks: string[] = [];

  const top = convergence[0];
  if (top) {
    const location = getLocationName(top.lat, top.lon);
    risks.push(`Convergence: ${location} (score: ${top.score})`);
  }

  const leadSanctions = sanctions?.countries[0];
  if (leadSanctions && (sanctions.newEntryCount > 0 || leadSanctions.entryCount >= 25)) {
    const label = sanctions.newEntryCount > 0 ? 'Sanctions burst' : 'Sanctions pressure';
    risks.push(`${label}: ${leadSanctions.countryName} (${leadSanctions.entryCount}, +${leadSanctions.newEntryCount} new)`);
  }

  const strongestRadiation = radiation
    .filter(observation => observation.severity !== 'normal')
    .sort((a, b) => getRadiationRank(b) - getRadiationRank(a))[0];
  if (strongestRadiation) {
    const status = strongestRadiation.corroborated
      ? strongestRadiation.severity === 'spike' ? 'Confirmed radiation spike' : 'Confirmed radiation anomaly'
      : strongestRadiation.conflictingSources
        ? 'Conflicting radiation signal'
        : strongestRadiation.severity === 'spike'
          ? 'Potential radiation spike'
          : 'Elevated radiation';
    risks.push(`${status}: ${strongestRadiation.location} (+${strongestRadiation.delta.toFixed(1)} ${strongestRadiation.unit})`);
  }

  const critical = cii.filter(s => s.level === 'critical' || s.level === 'high');
  for (const c of critical.slice(0, 2)) {
    risks.push(`${c.name} instability: ${c.score} (${c.level})`);
  }

  return risks.slice(0, 3);
}

export function getAlerts(): UnifiedAlert[] {
  return [...alerts];
}

export function getRecentAlerts(hours: number = 24): UnifiedAlert[] {
  const cutoff = Date.now() - hours * 60 * 60 * 1000;
  return alerts.filter(a => a.timestamp.getTime() > cutoff);
}

export function clearAlerts(): void {
  alerts.length = 0;
}

export function getAlertCount(): { critical: number; high: number; medium: number; low: number } {
  return {
    critical: alerts.filter(a => a.priority === 'critical').length,
    high: alerts.filter(a => a.priority === 'high').length,
    medium: alerts.filter(a => a.priority === 'medium').length,
    low: alerts.filter(a => a.priority === 'low').length,
  };
}
