/**
 * Generation of analysis with real data (not synthetic)
 */

import type { AnalysisData, Kpi, DimensionAnalysis, HeatmapDataPoint, Opportunity, RoadmapInitiative, EconomicModelData, BenchmarkDataPoint, Finding, Recommendation, TierKey, CustomerSegment, RawInteraction, AgenticReadinessResult, SubFactor, SkillMetrics, DrilldownDataPoint } from '../types';
import { RoadmapPhase } from '../types';
import { BarChartHorizontal, Zap, Target, Brain, Bot, DollarSign, Smile } from 'lucide-react';
import { calculateAgenticReadinessScore, type AgenticReadinessInput } from './agenticReadinessV2';
import { classifyQueue } from './segmentClassifier';

/**
 * Calculate hourly distribution from interactions
 * NOTE: Uses unique interaction_id for consistency with backend (aggfunc="nunique")
 */
function calculateHourlyDistribution(interactions: RawInteraction[]): { hourly: number[]; off_hours_pct: number; peak_hours: number[] } {
  const hourly = new Array(24).fill(0);

  // Deduplicate by interaction_id for consistency with backend (nunique)
  const seenIds = new Set<string>();
  let duplicateCount = 0;

  for (const interaction of interactions) {
    // Skip duplicate interaction_id
    const id = interaction.interaction_id;
    if (id && seenIds.has(id)) {
      duplicateCount++;
      continue;
    }
    if (id) seenIds.add(id);

    try {
      const date = new Date(interaction.datetime_start);
      if (!isNaN(date.getTime())) {
        const hour = date.getHours();
        hourly[hour]++;
      }
    } catch {
      // Ignore invalid dates
    }
  }

  if (duplicateCount > 0) {
    console.log(`⏰ calculateHourlyDistribution: ${duplicateCount} duplicate interaction_ids ignored`);
  }

  const total = hourly.reduce((a, b) => a + b, 0);

  // Off hours: 19:00-08:00
  const offHoursVolume = hourly.slice(0, 8).reduce((a, b) => a + b, 0) +
                         hourly.slice(19).reduce((a, b) => a + b, 0);
  const off_hours_pct = total > 0 ? Math.round((offHoursVolume / total) * 100) : 0;

  // Find peak hours (top 3 consecutive)
  let maxSum = 0;
  let peakStart = 0;
  for (let i = 0; i < 22; i++) {
    const sum = hourly[i] + hourly[i + 1] + hourly[i + 2];
    if (sum > maxSum) {
      maxSum = sum;
      peakStart = i;
    }
  }
  const peak_hours = [peakStart, peakStart + 1, peakStart + 2];

  // Log for debugging
  const hourlyNonZero = hourly.filter(v => v > 0);
  const peakVolume = Math.max(...hourlyNonZero, 1);
  const valleyVolume = Math.min(...hourlyNonZero.filter(v => v > 0), 1);
  console.log(`⏰ Hourly distribution: total=${total}, peak=${peakVolume}, valley=${valleyVolume}, ratio=${(peakVolume/valleyVolume).toFixed(2)}`);

  return { hourly, off_hours_pct, peak_hours };
}

/**
 * Calculate date range from interactions (optimized for large files)
 */
function calculateDateRange(interactions: RawInteraction[]): { min: string; max: string } | undefined {
  let minTime = Infinity;
  let maxTime = -Infinity;
  let validCount = 0;

  for (const interaction of interactions) {
    const date = new Date(interaction.datetime_start);
    const time = date.getTime();
    if (!isNaN(time)) {
      validCount++;
      if (time < minTime) minTime = time;
      if (time > maxTime) maxTime = time;
    }
  }

  if (validCount === 0) return undefined;

  return {
    min: new Date(minTime).toISOString().split('T')[0],
    max: new Date(maxTime).toISOString().split('T')[0]
  };
}

/**
 * Generar analysis completo con datos reales
 */
export function generateAnalysisFromRealData(
  tier: TierKey,
  interactions: RawInteraction[],
  costPerHour: number,
  avgCsat: number,
  segmentMapping?: { high_value_queues: string[]; medium_value_queues: string[]; low_value_queues: string[] }
): AnalysisData {
  console.log(`🔄 Generating analysis from ${interactions.length} real interactions`);

  // STEP 0: Detectar si tenemos datos de repeat_call_7d
  const repeatCallTrueCount = interactions.filter(i => i.repeat_call_7d === true).length;
  const repeatCallFalseCount = interactions.filter(i => i.repeat_call_7d === false).length;
  const repeatCallUndefinedCount = interactions.filter(i => i.repeat_call_7d === undefined).length;
  const transferTrueCount = interactions.filter(i => i.transfer_flag === true).length;
  const transferFalseCount = interactions.filter(i => i.transfer_flag === false).length;

  const hasRepeatCallData = repeatCallTrueCount > 0;

  console.log('📞 DETAILED DATA CHECK:');
  console.log(`  - repeat_call_7d TRUE: ${repeatCallTrueCount} (${((repeatCallTrueCount/interactions.length)*100).toFixed(1)}%)`);
  console.log(`  - repeat_call_7d FALSE: ${repeatCallFalseCount} (${((repeatCallFalseCount/interactions.length)*100).toFixed(1)}%)`);
  console.log(`  - repeat_call_7d UNDEFINED: ${repeatCallUndefinedCount}`);
  console.log(`  - transfer_flag TRUE: ${transferTrueCount} (${((transferTrueCount/interactions.length)*100).toFixed(1)}%)`);
  console.log(`  - transfer_flag FALSE: ${transferFalseCount} (${((transferFalseCount/interactions.length)*100).toFixed(1)}%)`);

  // Calculate FCR esperado mannualmente
  const fcrRecords = interactions.filter(i => i.transfer_flag !== true && i.repeat_call_7d !== true);
  const expectedFCR = (fcrRecords.length / interactions.length) * 100;
  console.log(`📊 EXPECTED FCR (mannual): ${expectedFCR.toFixed(1)}% (${fcrRecords.length}/${interactions.length} calls without transfer AND without repeat)`);

  // Mostrar sample de datos for debugging
  if (interactions.length > 0) {
    console.log('📋 SAMPLE DATA (first 5 rows):', interactions.slice(0, 5).map(i => ({
      id: i.interaction_id?.substring(0, 8),
      transfer_flag: i.transfer_flag,
      repeat_call_7d: i.repeat_call_7d,
      is_abandoned: i.is_abandoned
    })));
  }

  console.log(`📞 Repeat call data: ${repeatCallTrueCount} calls marked as repeat (${hasRepeatCallData ? 'USING repeat_call_7d' : 'NO repeat_call_7d data - FCR = 100% - transfer_rate'})`);

  // STEP 0.5: Calculate date range
  const dateRange = calculateDateRange(interactions);
  console.log(`📅 Date range: ${dateRange?.min} to ${dateRange?.max}`);

  // STEP 1: Analizar record_status (ya no filtramos, filtering is done internally en calculateSkillMetrics)
  // Normalize to uppercase for case-insensitive comparison
  const getStatus = (i: RawInteraction) => (i.record_status || '').toString().toUpperCase().trim();
  const statusCounts = {
    valid: interactions.filter(i => !i.record_status || getStatus(i) === 'VALID').length,
    noise: interactions.filter(i => getStatus(i) === 'NOISE').length,
    zombie: interactions.filter(i => getStatus(i) === 'ZOMBIE').length,
    abandon: interactions.filter(i => getStatus(i) === 'ABANDON').length
  };
  console.log(`📊 Record status breakdown:`, statusCounts);

  // STEP 1.5: Calculate hourly distribution (on ALL interactions to see complete patterns)
  const hourlyDistribution = calculateHourlyDistribution(interactions);
  console.log(`⏰ Off-hours: ${hourlyDistribution.off_hours_pct}%, Peak hours: ${hourlyDistribution.peak_hours.join('-')}h`);

  // STEP 2: Calcular metrics por skill (passes ALL interactions, filtering is done internally)
  const skillMetrics = calculateSkillMetrics(interactions, costPerHour);

  console.log(`📊 Calculated metrics for ${skillMetrics.length} skills`);

  // STEP 3: Generar heatmap data con dimensions
  const heatmapData = generateHeatmapFromMetrics(skillMetrics, avgCsat, segmentMapping);

  // STEP 4: Calcular metrics globales
  // Volumen total: ALL interactions
  const totalInteractions = interactions.length;
  // Valid volume for AHT: sum of volume_valid from each skill
  const totalValidInteractions = skillMetrics.reduce((sum, s) => sum + s.volume_valid, 0);

  // AHT average: calculated only on valid interactions (weighted by volume)
  const totalWeightedAHT = skillMetrics.reduce((sum, s) => sum + (s.aht_mean * s.volume_valid), 0);
  const avgAHT = totalValidInteractions > 0 ? Math.round(totalWeightedAHT / totalValidInteractions) : 0;

  // Technical FCR: 100 - transfer_rate (comparable with industry benchmarks)
  // Weighted by volume of each skill
  const totalVolumeForFCR = skillMetrics.reduce((sum, s) => sum + s.volume_valid, 0);
  const avgFCR = totalVolumeForFCR > 0
    ? Math.round(skillMetrics.reduce((sum, s) => sum + (s.fcr_tecnico * s.volume_valid), 0) / totalVolumeForFCR)
    : 0;

  // Coste total
  const totalCost = Math.round(skillMetrics.reduce((sum, s) => sum + s.total_cost, 0));

  // === CPI CENTRALIZADO: Calcular UNA sola vez from heatmapData ===
  // This is the ONLY source of truth for CPI, same as ExecutiveSummaryTab
  const totalCostVolume = heatmapData.reduce((sum, h) => sum + (h.cost_volume || h.volume), 0);
  const totalAnnualCost = heatmapData.reduce((sum, h) => sum + (h.annual_cost || 0), 0);
  const hasCpiField = heatmapData.some(h => h.cpi !== undefined && h.cpi > 0);
  const globalCPI = hasCpiField
    ? (totalCostVolume > 0
        ? heatmapData.reduce((sum, h) => sum + (h.cpi || 0) * (h.cost_volume || h.volume), 0) / totalCostVolume
        : 0)
    : (totalCostVolume > 0 ? totalAnnualCost / totalCostVolume : 0);

  // KPIs main
  const summaryKpis: Kpi[] = [
    { label: "Total Interactions", value: totalInteractions.toLocaleString('es-ES') },
    { label: "Average AHT", value: `${avgAHT}s` },
    { label: "Technical FCR", value: `${avgFCR}%` },
    { label: "CSAT", value: `${(avgCsat / 20).toFixed(1)}/5` }
  ];

  // Health Score based on real metrics
  const overallHealthScore = calculateHealthScore(heatmapData);

  // Dimensiones (simplified for real data) - pass centralized CPI
  const dimensions: DimensionAnalysis[] = generateDimensionsFromRealData(
    interactions,
    skillMetrics,
    avgCsat,
    avgAHT,
    hourlyDistribution,
    globalCPI  // CPI calculated from heatmapData
  );
  
  // Agentic Readiness Score
  const agenticReadiness = calculateAgenticReadinessFromRealData(skillMetrics);

  // Findings y Recommendations (including analysis of off hours)
  const findings = generateFindingsFromRealData(skillMetrics, interactions, hourlyDistribution);
  const recommendations = generateRecommendationsFromRealData(skillMetrics, hourlyDistribution, interactions.length);

  // v3.3: Drill-down by Queue + Typification - CALCULATE FIRST to use in opportunities y roadmap
  const drilldownData = calculateDrilldownMetrics(interactions, costPerHour);

  // v3.3: Opportunities y Roadmap based on drilldownData (queues with CV < 75% = automatable)
  const opportunities = generateOpportunitiesFromDrilldown(drilldownData, costPerHour);

  // Roadmap based on drilldownData
  const roadmap = generateRoadmapFromDrilldown(drilldownData, costPerHour);

  // Economic Model (v3.10: aligned with TCO of Roadmap)
  const economicModel = generateEconomicModelFromRealData(skillMetrics, costPerHour, roadmap, drilldownData);

  // Benchmark
  const benchmarkData = generateBenchmarkFromRealData(skillMetrics);

  return {
    tier,
    overallHealthScore,
    summaryKpis,
    dimensions,
    heatmapData,
    agenticReadiness,
    findings,
    recommendations,
    opportunities,
    roadmap,
    economicModel,
    benchmarkData,
    dateRange,
    drilldownData
  };
}

/**
 * STEP 2: Calcular metrics base por skill
 *
 * FILTERING LOGIC BY record_status:
 * - valid: normal valid calls
 * - noise: calls < 10 segundos (exclude from AHT, but sum in volumen/coste)
 * - zombie: calls > 3 hours (exclude from AHT, but sum in volumen/coste)
 * - abandon: customer hangs up (exclude from AHT, no sum conversation cost, but occupies line)
 *
 * Dashboard quality/efficiency: filter only valid + abandon para AHT
 * Financial calculations: use all (volume, total cost)
 */
interface SkillMetrics {
  skill: string;
  volume: number;           // Total of interactions (all)
  volume_valid: number;     // Valid interactions for AHT (valid + abandon)
  aht_mean: number;         // AHT "clean" calculado only above valid (without noise/zombie/abandon) - for metrics of quality, CV
  aht_total: number;        // AHT "total" calculado con TODAS las filas (noise/zombie/abandon included) - only informative
  aht_benchmark: number;    // AHT "traditional" (includes noise, excludes zombie/abandon) - for comparison with industry benchmarks
  aht_std: number;
  cv_aht: number;
  transfer_rate: number;    // Calculado above valid + abandon
  fcr_rate: number;         // FCR Real: (transfer_flag == FALSE) AND (repeat_call_7d == FALSE) - without recontact 7 days
  fcr_tecnico: number;      // Technical FCR: (transfer_flag == FALSE) - only without transferencia, comparable with industry benchmarks
  abandonment_rate: number; // % de abandonments of total
  total_cost: number;       // Coste total (all las interactions except abandon)
  cost_volume: number;      // Volumen used to calculate coste (non-abandon)
  cpi: number;              // Coste per interaction = total_cost / cost_volume
  hold_time_mean: number;   // Calculado above valid
  cv_talk_time: number;
  // Additional metrics for debug
  noise_count: number;
  zombie_count: number;
  abandon_count: number;
}

export function calculateSkillMetrics(interactions: RawInteraction[], costPerHour: number): SkillMetrics[] {
  // Group por skill
  const skillGroups = new Map<string, RawInteraction[]>();

  interactions.forEach(i => {
    if (!skillGroups.has(i.queue_skill)) {
      skillGroups.set(i.queue_skill, []);
    }
    skillGroups.get(i.queue_skill)!.push(i);
  });

  // Calculate metrics for each skill
  const metrics: SkillMetrics[] = [];

  skillGroups.forEach((group, skill) => {
    const volume = group.length;
    if (volume === 0) return;

    // === SIMPLE AND DIRECT CALCULATIONS FROM CSV ===

    // Abandonment: DIRECTO del campo is_abandoned del CSV
    const abandon_count = group.filter(i => i.is_abandoned === true).length;
    const abandonment_rate = (abandon_count / volume) * 100;

    // FCR Real: DIRECTO del campo fcr_real_flag del CSV
    // Definition: (transfer_flag == FALSE) AND (repeat_call_7d == FALSE)
    // This is the STRICTEST metric - without transfer AND without recontact in 7 days
    const fcrTrueCount = group.filter(i => i.fcr_real_flag === true).length;
    const fcr_rate = (fcrTrueCount / volume) * 100;

    // Transfer rate: DIRECTO del campo transfer_flag del CSV
    const transfers = group.filter(i => i.transfer_flag === true).length;
    const transfer_rate = (transfers / volume) * 100;

    // Technical FCR: 100 - transfer_rate
    // Definition: (transfer_flag == FALSE) - only without transferencia
    // This metric is COMPARABLE with industry benchmarks (COPC, Dimension Data)
    // Industry benchmarks (~70%) measure FCR without transfer, NOT without recontact
    const fcr_tecnico = 100 - transfer_rate;

    // Separate by record_status for AHT (normalize to uppercase for case-insensitive comparison)
    const getStatus = (i: RawInteraction) => (i.record_status || '').toString().toUpperCase().trim();
    const noiseRecords = group.filter(i => getStatus(i) === 'NOISE');
    const zombieRecords = group.filter(i => getStatus(i) === 'ZOMBIE');
    const validRecords = group.filter(i => !i.record_status || getStatus(i) === 'VALID');
    // Registros que generan coste (todo except abandonments)
    const nonAbandonRecords = group.filter(i => i.is_abandoned !== true);

    const noise_count = noiseRecords.length;
    const zombie_count = zombieRecords.length;

    // AHT se calcula above records 'valid' (excludes noise, zombie)
    const ahtRecords = validRecords;
    const volume_valid = ahtRecords.length;

    let aht_mean = 0;
    let aht_std = 0;
    let cv_aht = 0;
    let hold_time_mean = 0;
    let cv_talk_time = 0;

    if (volume_valid > 0) {
      // AHT = duration_talk + hold_time + wrap_up_time
      const ahts = ahtRecords.map(i => i.duration_talk + i.hold_time + i.wrap_up_time);
      aht_mean = ahts.reduce((sum, v) => sum + v, 0) / volume_valid;
      const aht_variance = ahts.reduce((sum, v) => sum + Math.pow(v - aht_mean, 2), 0) / volume_valid;
      aht_std = Math.sqrt(aht_variance);
      cv_aht = aht_mean > 0 ? aht_std / aht_mean : 0;

      // Talk time CV
      const talkTimonth = ahtRecords.map(i => i.duration_talk);
      const talk_mean = talkTimonth.reduce((sum, v) => sum + v, 0) / volume_valid;
      const talk_std = Math.sqrt(talkTimonth.reduce((sum, v) => sum + Math.pow(v - talk_mean, 2), 0) / volume_valid);
      cv_talk_time = talk_mean > 0 ? talk_std / talk_mean : 0;

      // Hold time average
      hold_time_mean = ahtRecords.reduce((sum, i) => sum + i.hold_time, 0) / volume_valid;
    }

    // === AHT BENCHMARK: for comparison with industry benchmarks ===
    // Incluye NOISE (calls cortas son traunder real), excludes ZOMBIE (errores) y ABANDON (without handle time)
    // Industry benchmarks (COPC, Dimension Data) NO filtran calls cortas
    const benchmarkRecords = group.filter(i =>
      getStatus(i) !== 'ZOMBIE' &&
      getStatus(i) !== 'ABANDON' &&
      i.is_abandoned !== true
    );
    const volume_benchmark = benchmarkRecords.length;

    let aht_benchmark = aht_mean; // Fallback al AHT clean si no hay records benchmark
    if (volume_benchmark > 0) {
      const benchmarkAhts = benchmarkRecords.map(i => i.duration_talk + i.hold_time + i.wrap_up_time);
      aht_benchmark = benchmarkAhts.reduce((sum, v) => sum + v, 0) / volume_benchmark;
    }

    // === AHT TOTAL: calculado con TODAS las filas (only informative) ===
    // Incluye NOISE, ZOMBIE, ABANDON - for comparison with AHT clean
    let aht_total = 0;
    if (volume > 0) {
      const allAhts = group.map(i => i.duration_talk + i.hold_time + i.wrap_up_time);
      aht_total = allAhts.reduce((sum, v) => sum + v, 0) / volume;
    }

    // === FINANCIAL CALCULATIONS: use ALL interactions ===
    // Total cost with effective productivity of 70%
    const effectiveProductivity = 0.70;

    // For cost, we use all interactions EXCEPT abandonments (which do not generate conversation cost)
    // noise y zombie DO generate cost (occupy agent even if little/much time)
    // Usar nonAbandonRecords que ya filtra por is_abandoned y record_status
    const costRecords = nonAbandonRecords;
    const costVolume = costRecords.length;

    // Calculate AHT for cost using all records that generate cost
    let aht_for_cost = 0;
    if (costVolume > 0) {
      const costAhts = costRecords.map(i => i.duration_talk + i.hold_time + i.wrap_up_time);
      aht_for_cost = costAhts.reduce((sum, v) => sum + v, 0) / costVolume;
    }

    // Real Cost = (AHT in hours × Cost/hour × Volume) / Effective Productivity
    const rawCost = (aht_for_cost / 3600) * costPerHour * costVolume;
    const total_cost = rawCost / effectiveProductivity;

    // CPI = Coste per interaction (usando el volumen correcto)
    const cpi = costVolume > 0 ? total_cost / costVolume : 0;

    metrics.push({
      skill,
      volume,
      volume_valid,
      aht_mean,
      aht_total,  // AHT con TODAS las filas (only informative)
      aht_benchmark,
      aht_std,
      cv_aht,
      transfer_rate,
      fcr_rate,
      fcr_tecnico,
      abandonment_rate,
      total_cost,
      cost_volume: costVolume,
      cpi,
      hold_time_mean,
      cv_talk_time,
      noise_count,
      zombie_count,
      abandon_count
    });
  });

  // === DEBUG: Verify calculations ===
  const totalVolume = metrics.reduce((sum, m) => sum + m.volume, 0);
  const totalValidVolume = metrics.reduce((sum, m) => sum + m.volume_valid, 0);
  const totalAbandons = metrics.reduce((sum, m) => sum + m.abandon_count, 0);
  const globalAbandonRate = totalVolume > 0 ? (totalAbandons / totalVolume) * 100 : 0;

  // FCR y Transfer rate globales (ponderados por volumen)
  const avgFCRRate = totalVolume > 0
    ? metrics.reduce((sum, m) => sum + m.fcr_rate * m.volume, 0) / totalVolume
    : 0;
  const avgFCRTecnicoRate = totalVolume > 0
    ? metrics.reduce((sum, m) => sum + m.fcr_tecnico * m.volume, 0) / totalVolume
    : 0;
  const avgTransferRate = totalVolume > 0
    ? metrics.reduce((sum, m) => sum + m.transfer_rate * m.volume, 0) / totalVolume
    : 0;

  console.log('');
  console.log('═══════════════════════════════════════════════════════════════');
  console.log('📊 METRICS CALCULATED BY SKILL');
  console.log('═══════════════════════════════════════════════════════════════');
  console.log(`Total skills: ${metrics.length}`);
  console.log(`Total volumen: ${totalVolume}`);
  console.log(`Total abandonments (is_abandoned=TRUE): ${totalAbandons}`);
  console.log('');
  console.log('GLOBAL METRICS (weighted by volume):');
  console.log(`  Abandonment Rate: ${globalAbandonRate.toFixed(2)}%`);
  console.log(`  FCR Real (without transfer + without recontact 7d): ${avgFCRRate.toFixed(2)}%`);
  console.log(`  Technical FCR (only without transfer, comparable with benchmarks): ${avgFCRTecnicoRate.toFixed(2)}%`);
  console.log(`  Transfer Rate: ${avgTransferRate.toFixed(2)}%`);
  console.log('');
  console.log('Detalle por skill (top 5):');
  metrics.slice(0, 5).forEach(m => {
    console.log(`  ${m.skill}: vol=${m.volume}, abandon=${m.abandon_count} (${m.abandonment_rate.toFixed(1)}%), FCR Real=${m.fcr_rate.toFixed(1)}%, Technical FCR=${m.fcr_tecnico.toFixed(1)}%, transfer=${m.transfer_rate.toFixed(1)}%`);
  });
  console.log('═══════════════════════════════════════════════════════════════');
  console.log('');

  // Mostrar detalle del primer skill for debug
  if (metrics[0]) {
    console.log('📋 Sample skill detail:', {
      skill: metrics[0].skill,
      volume: metrics[0].volume,
      volume_valid: metrics[0].volume_valid,
      transfer_rate: `${metrics[0].transfer_rate.toFixed(2)}%`,
      fcr_rate: `${metrics[0].fcr_rate.toFixed(2)}%`,
      abandon_count: metrics[0].abandon_count,
      abandonment_rate: `${metrics[0].abandonment_rate.toFixed(2)}%`
    });
  }

  return metrics.sort((a, b) => b.volume - a.volume); // Sort by descending volume
}

/**
 * v4.4: Classify automation tier with heatmap data
 *
 * This function replicates the logic of clasificarTier() usando los datos
 * disponibles en el heatmap. Accepts optional parameters (fcr, volume)
 * for greater precision when available.
 *
 * Used in generateDrilldownFromHeatmap() de analysisGenerator.ts para
 * asegurar consistencia between the fresh path (complete data) y la ruta
 * cached (heatmap data).
 *
 * @param score - Agentic Readiness Score (0-10)
 * @param cv - Coefficient of Variation of AHT as decimal (0.75 = 75%)
 * @param transfer - Transfer rate as decimal (0.20 = 20%)
 * @param fcr - FCR rate as decimal (0.80 = 80%), optional
 * @param volume - Monthly volume of interactions, optional
 * @returns AgenticTier ('AUTOMATE' | 'ASSIST' | 'AUGMENT' | 'HUMAN-ONLY')
 */
export function clasificarTierSimple(
  score: number,
  cv: number,      // CV as decimal (0.75 = 75%)
  transfer: number, // Transfer as decimal (0.20 = 20%)
  fcr?: number,    // FCR as decimal (0.80 = 80%)
  volume?: number  // Monthly volume
): import('../types').AgenticTier {
  // critical RED FLAGS - same as clasificarTier() complete
  // CV > 120% o Transfer > 50% are absolute red flags
  if (cv > 1.20 || transfer > 0.50) {
    return 'HUMAN-ONLY';
  }
  // Volume < 50/month is a red flag if we have the data
  if (volume !== undefined && volume < 50) {
    return 'HUMAN-ONLY';
  }

  // TIER 1: AUTOMATE - requires optimal metrics
  // Same criterion as clasificarTier(): score >= 7.5, cv <= 0.75, transfer <= 0.20, fcr >= 0.50
  const fcrOk = fcr === undefined || fcr >= 0.50; // If we dont have FCR, we assume OK
  if (score >= 7.5 && cv <= 0.75 && transfer <= 0.20 && fcrOk) {
    return 'AUTOMATE';
  }

  // TIER 2: ASSIST - suitable for copilot/assistance
  if (score >= 5.5 && cv <= 0.90 && transfer <= 0.30) {
    return 'ASSIST';
  }

  // TIER 3: AUGMENT - requires prior optimization
  if (score >= 3.5) {
    return 'AUGMENT';
  }

  // TIER 4: HUMAN-ONLY - complex process
  return 'HUMAN-ONLY';
}

/**
 * v3.4: Calculate drill-down metrics with new formula for Agentic Readiness Score
 *
 * SCORE BY QUEUE (0-10):
 * - Factor 1: PREDICTABILITY (30%) - based on CV AHT
 * - Factor 2: RESOLUTION (25%) - FCR (60%) + Transfer (40%)
 * - Factor 3: VOLUME (25%) - based on volumen monthly
 * - Factor 4: DATA QUALITY (10%) - % valid records
 * - Factor 5: SIMPLICITY (10%) - based on AHT
 *
 * TIER CLASSIFICATION:
 * - AUTOMATE: score >= 7.5, CV <= 75%, transfer <= 20%, FCR >= 50%
 * - ASSIST: score >= 5.5, CV <= 90%, transfer <= 30%
 * - AUGMENT: score >= 3.5
 * - HUMAN-ONLY: score < 3.5 or red flags
 *
 * RED FLAGS (automatic HUMAN-ONLY):
 * - CV > 120%
 * - Transfer > 50%
 * - Vol < 50/month
 * - Valid < 30%
 */
export function calculateDrilldownMetrics(
  interactions: RawInteraction[],
  costPerHour: number
): DrilldownDataPoint[] {
  const effectiveProductivity = 0.70;

  // ═══════════════════════════════════════════════════════════════════════════
  // FUNCTION: Calculate Score by Queue (new formula v3.4)
  // ═══════════════════════════════════════════════════════════════════════════
  function calculateScoreCola(
    cv: number,      // CV AHT (0-2+, where 1 = 100%)
    fcr: number,     // FCR rate (0-1)
    transfer: number, // Transfer rate (0-1)
    vol: number,     // Monthly volume
    aht: number,     // AHT in seconds
    validPct: number // % valid records (0-1)
  ): { score: number; breakdown: import('../types').AgenticScoreBreakdown } {

    // FACTOR 1: PREDICTABILITY (30%) - based on CV AHT
    let scorePred: number;
    if (cv <= 0.50) {
      scorePred = 10;
    } else if (cv <= 0.65) {
      scorePred = 8 + (0.65 - cv) / 0.15 * 2;
    } else if (cv <= 0.75) {
      scorePred = 6 + (0.75 - cv) / 0.10 * 2;
    } else if (cv <= 0.90) {
      scorePred = 3 + (0.90 - cv) / 0.15 * 3;
    } else if (cv <= 1.10) {
      scorePred = 1 + (1.10 - cv) / 0.20 * 2;
    } else {
      scorePred = Math.max(0, 1 - (cv - 1.10) / 0.50);
    }

    // FACTOR 2: RESOLUTION (25%) = FCR (60%) + Transfer (40%)
    let scoreFcr: number;
    if (fcr >= 0.80) {
      scoreFcr = 10;
    } else if (fcr >= 0.70) {
      scoreFcr = 7 + (fcr - 0.70) / 0.10 * 3;
    } else if (fcr >= 0.50) {
      scoreFcr = 4 + (fcr - 0.50) / 0.20 * 3;
    } else if (fcr >= 0.30) {
      scoreFcr = 2 + (fcr - 0.30) / 0.20 * 2;
    } else {
      scoreFcr = fcr / 0.30 * 2;
    }

    let scoreTrans: number;
    if (transfer <= 0.05) {
      scoreTrans = 10;
    } else if (transfer <= 0.15) {
      scoreTrans = 7 + (0.15 - transfer) / 0.10 * 3;
    } else if (transfer <= 0.25) {
      scoreTrans = 4 + (0.25 - transfer) / 0.10 * 3;
    } else if (transfer <= 0.40) {
      scoreTrans = 1 + (0.40 - transfer) / 0.15 * 3;
    } else {
      scoreTrans = Math.max(0, 1 - (transfer - 0.40) / 0.30);
    }

    const scoreResol = scoreFcr * 0.6 + scoreTrans * 0.4;

    // FACTOR 3: VOLUME (25%)
    let scoreVol: number;
    if (vol >= 10000) {
      scoreVol = 10;
    } else if (vol >= 5000) {
      scoreVol = 8 + (vol - 5000) / 5000 * 2;
    } else if (vol >= 1000) {
      scoreVol = 5 + (vol - 1000) / 4000 * 3;
    } else if (vol >= 500) {
      scoreVol = 3 + (vol - 500) / 500 * 2;
    } else if (vol >= 100) {
      scoreVol = 1 + (vol - 100) / 400 * 2;
    } else {
      scoreVol = vol / 100;
    }

    // FACTOR 4: DATA QUALITY (10%)
    let scoreCal: number;
    if (validPct >= 0.90) {
      scoreCal = 10;
    } else if (validPct >= 0.75) {
      scoreCal = 7 + (validPct - 0.75) / 0.15 * 3;
    } else if (validPct >= 0.50) {
      scoreCal = 4 + (validPct - 0.50) / 0.25 * 3;
    } else {
      scoreCal = validPct / 0.50 * 4;
    }

    // FACTOR 5: SIMPLICITY (10%) - based on AHT
    let scoreSimp: number;
    if (aht <= 180) {
      scoreSimp = 10;
    } else if (aht <= 300) {
      scoreSimp = 8 + (300 - aht) / 120 * 2;
    } else if (aht <= 480) {
      scoreSimp = 5 + (480 - aht) / 180 * 3;
    } else if (aht <= 720) {
      scoreSimp = 2 + (720 - aht) / 240 * 3;
    } else {
      scoreSimp = Math.max(0, 2 - (aht - 720) / 600 * 2);
    }

    // SCORE TOTAL PONDERADO
    const scoreTotal = (
      scorePred * 0.30 +
      scoreResol * 0.25 +
      scoreVol * 0.25 +
      scoreCal * 0.10 +
      scoreSimp * 0.10
    );

    return {
      score: Math.round(scoreTotal * 10) / 10,
      breakdown: {
        predictibilidad: Math.round(scorePred * 10) / 10,
        resolutividad: Math.round(scoreResol * 10) / 10,
        volumen: Math.round(scoreVol * 10) / 10,
        qualityDatos: Math.round(scoreCal * 10) / 10,
        simplicidad: Math.round(scoreSimp * 10) / 10
      }
    };
  }

  // ═══════════════════════════════════════════════════════════════════════════
  // FUNCTION: Clasificar Tier del Roadmap
  // ═══════════════════════════════════════════════════════════════════════════
  function clasificarTier(
    score: number,
    cv: number,      // CV as decimal (0.75 = 75%)
    transfer: number, // Transfer as decimal (0.20 = 20%)
    fcr: number,     // FCR as decimal (0.80 = 80%)
    vol: number,
    validPct: number
  ): { tier: import('../types').AgenticTier; motivo: string } {

    // RED FLAGS → HUMAN-ONLY automatic
    const redFlags: string[] = [];
    if (cv > 1.20) redFlags.push("CV > 120%");
    if (transfer > 0.50) redFlags.push("Transfer > 50%");
    if (vol < 50) redFlags.push("Vol < 50/month");
    if (validPct < 0.30) redFlags.push("Data < 30% valid");

    if (redFlags.length > 0) {
      return {
        tier: 'HUMAN-ONLY',
        motivo: `Red flags: ${redFlags.join(', ')}`
      };
    }

    // TIER 1: AUTOMATE
    if (score >= 7.5 && cv <= 0.75 && transfer <= 0.20 && fcr >= 0.50) {
      return {
        tier: 'AUTOMATE',
        motivo: `Score ${score}, optimal metrics for automation`
      };
    }

    // TIER 2: ASSIST
    if (score >= 5.5 && cv <= 0.90 && transfer <= 0.30) {
      return {
        tier: 'ASSIST',
        motivo: `Score ${score}, suitable for copilot/assistance`
      };
    }

    // TIER 3: AUGMENT
    if (score >= 3.5) {
      return {
        tier: 'AUGMENT',
        motivo: `Score ${score}, requires prior optimization`
      };
    }

    // TIER 4: HUMAN-ONLY
    return {
      tier: 'HUMAN-ONLY',
      motivo: `Score ${score}, complex process for automation`
    };
  }

  // ═══════════════════════════════════════════════════════════════════════════
  // FUNCTION: Calcular metrics de un group of interactions
  // ═══════════════════════════════════════════════════════════════════════════
  function calculateQueueMetrics(group: RawInteraction[]): import('../types').OriginalQueueMetrics | null {
    const volume = group.length;
    if (volume < 5) return null;

    // Filter only VALID for CV calculation (normalize to uppercase for case-insensitive comparison)
    const getStatus = (i: RawInteraction) => (i.record_status || '').toString().toUpperCase().trim();
    const validRecords = group.filter(i => !i.record_status || getStatus(i) === 'VALID');
    const volumeValid = validRecords.length;
    if (volumeValid < 3) return null;

    const validPct = volumeValid / volume;

    // AHT y CV on valid records
    const ahts = validRecords.map(i => i.duration_talk + i.hold_time + i.wrap_up_time);
    const aht_mean = ahts.reduce((sum, v) => sum + v, 0) / volumeValid;
    const aht_variance = ahts.reduce((sum, v) => sum + Math.pow(v - aht_mean, 2), 0) / volumeValid;
    const aht_std = Math.sqrt(aht_variance);
    const cv_aht_decimal = aht_mean > 0 ? aht_std / aht_mean : 1.5; // CV as decimal
    const cv_aht_percent = cv_aht_decimal * 100; // CV como %

    // Transfer y FCR (as decimals for calculation, as % for display)
    const transfers = group.filter(i => i.transfer_flag === true).length;
    const transfer_decimal = transfers / volume;
    const transfer_percent = transfer_decimal * 100;

    // FCR Real: usa fcr_real_flag del CSV (without transferencia Y without recontact 7d)
    const fcrCount = group.filter(i => i.fcr_real_flag === true).length;
    const fcr_decimal = fcrCount / volume;
    const fcr_percent = fcr_decimal * 100;

    // Technical FCR: 100 - transfer_rate (comparable with industry benchmarks)
    const fcr_tecnico_percent = 100 - transfer_percent;

    // Calculate score con new formula v3.4
    const { score, breakdown } = calculateScoreCola(
      cv_aht_decimal,
      fcr_decimal,
      transfer_decimal,
      volume,
      aht_mean,
      validPct
    );

    // Clasificar tier
    const { tier, motivo } = clasificarTier(
      score,
      cv_aht_decimal,
      transfer_decimal,
      fcr_decimal,
      volume,
      validPct
    );

    // v4.2: Convert volume from 11 months a annual para el coste
    const annualVolume = (volume / 11) * 12;  // 11 months → annual
    const annualCost = Math.round((aht_mean / 3600) * costPerHour * annualVolume / effectiveProductivity);

    return {
      original_queue_id: '', // Assigned later
      volume,
      volumeValid,
      aht_mean: Math.round(aht_mean),
      cv_aht: Math.round(cv_aht_percent * 10) / 10,
      transfer_rate: Math.round(transfer_percent * 10) / 10,
      fcr_rate: Math.round(fcr_percent * 10) / 10,
      fcr_tecnico: Math.round(fcr_tecnico_percent * 10) / 10,  // Technical FCR for consistency with Summary
      agenticScore: score,
      scoreBreakdown: breakdown,
      tier,
      tierMotivo: motivo,
      isPriorityCandidate: tier === 'AUTOMATE',
      annualCost
    };
  }

  // ═══════════════════════════════════════════════════════════════════════════
  // STEP 1: Group by queue_skill (strategic level)
  // ═══════════════════════════════════════════════════════════════════════════
  const skillGroups = new Map<string, RawInteraction[]>();
  for (const interaction of interactions) {
    const skill = interaction.queue_skill;
    if (!skill) continue;
    if (!skillGroups.has(skill)) {
      skillGroups.set(skill, []);
    }
    skillGroups.get(skill)!.push(interaction);
  }

  console.log(`📊 Drill-down v3.4: ${skillGroups.size} queue_skills found`);

  const drilldownData: DrilldownDataPoint[] = [];

  // ═══════════════════════════════════════════════════════════════════════════
  // STEP 2: For each queue_skill, group by original_queue_id
  // ═══════════════════════════════════════════════════════════════════════════
  skillGroups.forEach((skillGroup, skill) => {
    if (skillGroup.length < 10) return;

    const queueGroups = new Map<string, RawInteraction[]>();
    for (const interaction of skillGroup) {
      const queueId = interaction.original_queue_id || 'Without identification';
      if (!queueGroups.has(queueId)) {
        queueGroups.set(queueId, []);
      }
      queueGroups.get(queueId)!.push(interaction);
    }

    // Calculate metrics for each original_queue_id
    const originalQueues: import('../types').OriginalQueueMetrics[] = [];
    queueGroups.forEach((queueGroup, queueId) => {
      const metrics = calculateQueueMetrics(queueGroup);
      if (metrics) {
        metrics.original_queue_id = queueId;
        originalQueues.push(metrics);
      }
    });

    if (originalQueues.length === 0) return;

    // Sort by descending score, then by volume
    originalQueues.sort((a, b) => {
      if (Math.abs(a.agenticScore - b.agenticScore) > 0.5) {
        return b.agenticScore - a.agenticScore;
      }
      return b.volume - a.volume;
    });

    // ═══════════════════════════════════════════════════════════════════════
    // Calculate aggregated metrics of the skill (volume-weighted average)
    // ═══════════════════════════════════════════════════════════════════════
    const totalVolume = originalQueues.reduce((sum, q) => sum + q.volume, 0);
    const totalVolumeValid = originalQueues.reduce((sum, q) => sum + q.volumeValid, 0);
    const totalCost = originalQueues.reduce((sum, q) => sum + (q.annualCost || 0), 0);

    const avgAht = originalQueues.reduce((sum, q) => sum + q.aht_mean * q.volume, 0) / totalVolume;
    const avgCv = originalQueues.reduce((sum, q) => sum + q.cv_aht * q.volume, 0) / totalVolume;
    const avgTransfer = originalQueues.reduce((sum, q) => sum + q.transfer_rate * q.volume, 0) / totalVolume;
    const avgFcr = originalQueues.reduce((sum, q) => sum + q.fcr_rate * q.volume, 0) / totalVolume;
    const avgFcrTecnico = originalQueues.reduce((sum, q) => sum + q.fcr_tecnico * q.volume, 0) / totalVolume;

    // Score global weighted by volume
    const avgScore = originalQueues.reduce((sum, q) => sum + q.agenticScore * q.volume, 0) / totalVolume;

    // Tier predominante (el de mayor volumen)
    const tierCounts = { 'AUTOMATE': 0, 'ASSIST': 0, 'AUGMENT': 0, 'HUMAN-ONLY': 0 };
    originalQueues.forEach(q => {
      tierCounts[q.tier] += q.volume;
    });

    // isPriorityCandidate si hay al menos una queue AUTOMATE
    const hasAutomateQueue = originalQueues.some(q => q.tier === 'AUTOMATE');

    drilldownData.push({
      skill,
      originalQueues,
      volume: totalVolume,
      volumeValid: totalVolumeValid,
      aht_mean: Math.round(avgAht),
      cv_aht: Math.round(avgCv * 10) / 10,
      transfer_rate: Math.round(avgTransfer * 10) / 10,
      fcr_rate: Math.round(avgFcr * 10) / 10,
      fcr_tecnico: Math.round(avgFcrTecnico * 10) / 10,  // Technical FCR para consistencia
      agenticScore: Math.round(avgScore * 10) / 10,
      isPriorityCandidate: hasAutomateQueue,
      annualCost: totalCost
    });
  });

  // ═══════════════════════════════════════════════════════════════════════════
  // STEP 3: Ordenar y log resumen
  // ═══════════════════════════════════════════════════════════════════════════
  drilldownData.sort((a, b) => b.agenticScore - a.agenticScore);

  // Count tiers
  const allQueues = drilldownData.flatMap(s => s.originalQueues);
  const tierSummary = {
    AUTOMATE: allQueues.filter(q => q.tier === 'AUTOMATE').length,
    ASSIST: allQueues.filter(q => q.tier === 'ASSIST').length,
    AUGMENT: allQueues.filter(q => q.tier === 'AUGMENT').length,
    'HUMAN-ONLY': allQueues.filter(q => q.tier === 'HUMAN-ONLY').length
  };

  console.log(`📊 Drill-down v3.4: ${drilldownData.length} skills, ${allQueues.length} queues`);
  console.log(`🎯 Tiers: AUTOMATE=${tierSummary.AUTOMATE}, ASSIST=${tierSummary.ASSIST}, AUGMENT=${tierSummary.AUGMENT}, HUMAN-ONLY=${tierSummary['HUMAN-ONLY']}`);

  return drilldownData;
}

/**
 * STEP 3: Transformar metrics a dimensions (0-10)
 */
export function generateHeatmapFromMetrics(
  metrics: SkillMetrics[],
  avgCsat: number,
  segmentMapping?: { high_value_queues: string[]; medium_value_queues: string[]; low_value_queues: string[] }
): HeatmapDataPoint[] {
  console.log('🔍 generateHeatmapFromMetrics called with:', {
    metricsLength: metrics.length,
    firstMetric: metrics[0],
    avgCsat,
    hasSegmentMapping: !!segmentMapping
  });
  
  const result = metrics.map(m => {
    // Dimension 1: Predictability (CV AHT)
    const predictability = Math.max(0, Math.min(10, 10 - ((m.cv_aht - 0.3) / 1.2 * 10)));
    
    // Dimension 2: Inverse Complexity (Transfer Rate)
    const complexity_inverse = Math.max(0, Math.min(10, 10 - ((m.transfer_rate / 100 - 0.05) / 0.25 * 10)));
    
    // Dimension 3: Repeatability (Volumen)
    let repetitiveness = 0;
    if (m.volume >= 5000) {
      repetitiveness = 10;
    } else if (m.volume <= 100) {
      repetitiveness = 0;
    } else {
      // Linear interpolation between 100 y 5000
      repetitiveness = ((m.volume - 100) / (5000 - 100)) * 10;
    }
    
    // Agentic Readiness Score (average ponderado)
    const agentic_readiness = (
      predictability * 0.40 +
      complexity_inverse * 0.35 +
      repetitiveness * 0.25
    );
    
    // Category
    let category: 'automate' | 'assist' | 'optimize';
    if (agentic_readiness >= 8.0) {
      category = 'automate';
    } else if (agentic_readiness >= 5.0) {
      category = 'assist';
    } else {
      category = 'optimize';
    }
    
    // Segmentation
    const segment = segmentMapping 
      ? classifyQueue(m.skill, segmentMapping.high_value_queues, segmentMapping.medium_value_queues, segmentMapping.low_value_queues)
      : 'medium' as CustomerSegment;
    
    // Scores de performance (normalizados 0-100)
    // FCR Real: (transfer_flag == FALSE) AND (repeat_call_7d == FALSE)
    // This is the strictest metric - without transfer AND without recontact in 7 days
    const fcr_score = Math.round(m.fcr_rate);
    // Technical FCR: only without transferencia (comparable with industry benchmarks COPC, Dimension Data)
    const fcr_tecnico_score = Math.round(m.fcr_tecnico);
    const aht_score = Math.round(Math.max(0, Math.min(100, 100 - ((m.aht_mean - 240) / 310) * 100)));
    const csat_score = avgCsat;
    const hold_time_score = Math.round(Math.max(0, Math.min(100, 100 - (m.hold_time_mean / 60) * 10)));
    // Transfer rate es el % real de transferencias (NO el complemento)
    const actual_transfer_rate = Math.round(m.transfer_rate);
    // Abandonment rate es el % real de abandonments
    const actual_abandonment_rate = Math.round(m.abandonment_rate * 10) / 10; // 1 decimal

    return {
      skill: m.skill,
      volume: m.volume,
      cost_volume: m.cost_volume,                  // Volumen used to calculate coste (non-abandon)
      aht_seconds: Math.round(m.aht_mean),
      aht_total: Math.round(m.aht_total),          // AHT con TODAS las filas (only informative)
      aht_benchmark: Math.round(m.aht_benchmark),  // AHT traditional for comparison with industry benchmarks
      annual_cost: Math.round(m.total_cost),       // Coste calculado con TODOS los records (noise + zombie + valid)
      cpi: m.cpi,                                  // Coste per interaction (calculado correctamente)
      metrics: {
        fcr: fcr_score,                    // FCR Real (stricter, with recontact filter 7d)
        fcr_tecnico: fcr_tecnico_score,    // Technical FCR (comparable with benchmarks industria)
        aht: aht_score,
        csat: csat_score,
        hold_time: hold_time_score,
        transfer_rate: actual_transfer_rate,
        abandonment_rate: actual_abandonment_rate
      },
      automation_readiness: Math.round(agentic_readiness * 10),
      variability: {
        cv_aht: Math.round(m.cv_aht * 100),
        cv_talk_time: Math.round(m.cv_talk_time * 100),
        cv_hold_time: Math.round(m.cv_talk_time * 80), // Approximation
        transfer_rate: Math.round(m.transfer_rate)
      },
      dimensions: {
        predictability: Math.round(predictability * 10) / 10,
        complexity_inverse: Math.round(complexity_inverse * 10) / 10,
        repetitiveness: Math.round(repetitiveness * 10) / 10
      },
      agentic_readiness: Math.round(agentic_readiness * 10) / 10,
      category,
      segment
    };
  });
  
  console.log('📊 Heatmap data generated from real data:', {
    length: result.length,
    firstItem: result[0],
    objectKeys: result[0] ? Object.keys(result[0]) : [],
    hasMetricsObject: result[0] && typeof result[0].metrics !== 'undefined',
    metricsKeys: result[0] && result[0].metrics ? Object.keys(result[0].metrics) : [],
    firstMetrics: result[0] && result[0].metrics ? result[0].metrics : null,
    automation_readiness: result[0] ? result[0].automation_readiness : null
  });
  
  return result;
}

/**
 * Calculate global Health Score - New formula based on industry benchmarks
 *
 * STEP 1: Normalization of components using industry percentiles
 * STEP 2: Weighting (FCR 35%, Abandono 30%, CSAT Proxy 20%, AHT 15%)
 * STEP 3: Penalizaciones por umbrales criticals
 *
 * Benchmarks de industria (Cross-Industry):
 * - Technical FCR: P10=85%, P50=68%, P90=50%
 * - Abandono: P10=3%, P50=5%, P90=10%
 * - AHT: P10=240s, P50=380s, P90=540s
 */
function calculateHealthScore(heatmapData: HeatmapDataPoint[]): number {
  if (heatmapData.length === 0) return 50;

  const totalVolume = heatmapData.reduce((sum, d) => sum + d.volume, 0);
  if (totalVolume === 0) return 50;

  // ═══════════════════════════════════════════════════════════════
  // STEP 0: Extraer metrics ponderadas por volumen
  // ═══════════════════════════════════════════════════════════════

  // Technical FCR (%)
  const fcrTecnico = heatmapData.reduce((sum, d) =>
    sum + (d.metrics?.fcr_tecnico ?? (100 - d.metrics.transfer_rate)) * d.volume, 0) / totalVolume;

  // Abandono (%)
  const abandonment = heatmapData.reduce((sum, d) =>
    sum + (d.metrics?.abandonment_rate || 0) * d.volume, 0) / totalVolume;

  // AHT (segundos) - usar aht_seconds (AHT clean without noise/zombies)
  const aht = heatmapData.reduce((sum, d) =>
    sum + d.aht_seconds * d.volume, 0) / totalVolume;

  // Transferencia (%)
  const transferencia = heatmapData.reduce((sum, d) =>
    sum + (d.metrics?.transfer_rate || 0) * d.volume, 0) / totalVolume;

  // ═══════════════════════════════════════════════════════════════
  // STEP 1: Component normalization (0-100 score)
  // ═══════════════════════════════════════════════════════════════

  // Technical FCR: P10=85%, P50=68%, P90=50%
  // Higher = better
  let fcrScore: number;
  if (fcrTecnico >= 85) {
    fcrScore = 95 + 5 * Math.min(1, (fcrTecnico - 85) / 15); // 95-100
  } else if (fcrTecnico >= 68) {
    fcrScore = 50 + 50 * (fcrTecnico - 68) / (85 - 68); // 50-100
  } else if (fcrTecnico >= 50) {
    fcrScore = 20 + 30 * (fcrTecnico - 50) / (68 - 50); // 20-50
  } else {
    fcrScore = Math.max(0, 20 * fcrTecnico / 50); // 0-20
  }

  // Abandono: P10=3%, P50=5%, P90=10%
  // Lower = better (inverted)
  let abandonmentScore: number;
  if (abandonment <= 3) {
    abandonmentScore = 95 + 5 * Math.max(0, (3 - abandonment) / 3); // 95-100
  } else if (abandonment <= 5) {
    abandonmentScore = 50 + 45 * (5 - abandonment) / (5 - 3); // 50-95
  } else if (abandonment <= 10) {
    abandonmentScore = 20 + 30 * (10 - abandonment) / (10 - 5); // 20-50
  } else {
    // Above P90 (critical): strong penalty
    abandonmentScore = Math.max(0, 20 - 2 * (abandonment - 10)); // 0-20, decreases rapidly
  }

  // AHT: P10=240s, P50=380s, P90=540s
  // Lower = better (inverted)
  // PERO: Si FCR es under, AHT under puede indicar calls rushed (mala quality)
  let ahtScore: number;
  if (aht <= 240) {
    // Por deunder de P10 (excellent efficiency)
    // Si FCR > 65%, es genuinamente eficiente; si no, puede ser rushed
    if (fcrTecnico > 65) {
      ahtScore = 95 + 5 * Math.max(0, (240 - aht) / 60); // 95-100
    } else {
      ahtScore = 70; // Cap score si FCR es under (posible rushed calls)
    }
  } else if (aht <= 380) {
    ahtScore = 50 + 45 * (380 - aht) / (380 - 240); // 50-95
  } else if (aht <= 540) {
    ahtScore = 20 + 30 * (540 - aht) / (540 - 380); // 20-50
  } else {
    ahtScore = Math.max(0, 20 * (600 - aht) / 60); // 0-20
  }

  // CSAT Proxy: Calculado from FCR + Abandono
  // Sin datos reales de CSAT, usamos proxy
  const csatProxy = 0.60 * fcrScore + 0.40 * abandonmentScore;

  // ═══════════════════════════════════════════════════════════════
  // STEP 2: Aplicar pesos
  // FCR 35% + Abandono 30% + CSAT Proxy 20% + AHT 15%
  // ═══════════════════════════════════════════════════════════════

  const subtotal = (
    fcrScore * 0.35 +
    abandonmentScore * 0.30 +
    csatProxy * 0.20 +
    ahtScore * 0.15
  );

  // ═══════════════════════════════════════════════════════════════
  // STEP 3: Calcular penalizesciones
  // ═══════════════════════════════════════════════════════════════

  let penalties = 0;

  // Penalty for abandonment critical (>10%)
  if (abandonment > 10) {
    penalties += 10;
  }

  // Penalty for transferencia alta (>20%)
  if (transferencia > 20) {
    penalties += 5;
  }

  // Combo penalty: Abandono high + FCR under
  // Indicates systemic problems of capacity AND resolution
  if (abandonment > 8 && fcrTecnico < 78) {
    penalties += 5;
  }

  // ═══════════════════════════════════════════════════════════════
  // STEP 4: Final Score
  // ═══════════════════════════════════════════════════════════════

  const finalScore = Math.max(0, Math.min(100, subtotal - penalties));

  // Debug logging
  console.log('📊 Health Score Calculation:', {
    inputs: { fcrTecnico: fcrTecnico.toFixed(1), abandonment: abandonment.toFixed(1), aht: Math.round(aht), transferencia: transferencia.toFixed(1) },
    scores: { fcrScore: fcrScore.toFixed(1), abandonmentScore: abandonmentScore.toFixed(1), ahtScore: ahtScore.toFixed(1), csatProxy: csatProxy.toFixed(1) },
    weighted: { subtotal: subtotal.toFixed(1), penalties, final: Math.round(finalScore) }
  });

  return Math.round(finalScore);
}

/**
 * v4.0: Generate 7 viable dimensions from real data
 * Airline sector benchmarks: AHT P50=380s, FCR=70%, Abandono=5%, Ratio P90/P50 healthy<2.0
 */
function generateDimensionsFromRealData(
  interactions: RawInteraction[],
  metrics: SkillMetrics[],
  avgCsat: number,
  avgAHT: number,
  hourlyDistribution: { hourly: number[]; off_hours_pct: number; peak_hours: number[] },
  globalCPI: number  // CPI calculated centrally from heatmapData
): DimensionAnalysis[] {
  const totalVolume = interactions.length;
  const avgCV = metrics.reduce((sum, m) => sum + m.cv_aht, 0) / metrics.length;
  const avgTransferRate = metrics.reduce((sum, m) => sum + m.transfer_rate, 0) / metrics.length;
  const avgHoldTime = metrics.reduce((sum, m) => sum + m.hold_time_mean, 0) / metrics.length;
  const totalCost = metrics.reduce((sum, m) => sum + m.total_cost, 0);

  // Technical FCR (100 - transfer_rate, weighted by volume) - comparable with benchmarks
  const totalVolumeForFCR = metrics.reduce((sum, m) => sum + m.volume_valid, 0);
  const avgFCR = totalVolumeForFCR > 0
    ? metrics.reduce((sum, m) => sum + (m.fcr_tecnico * m.volume_valid), 0) / totalVolumeForFCR
    : 0;

  // Calculate ratio P90/P50 approximated from CV
  const avgRatio = 1 + avgCV * 1.5; // Approximation: ratio ≈ 1 + 1.5*CV

  // === SCORE EFICIENCIA: Scale based on ratio P90/P50 ===
  // <1.5 = 100pts, 1.5-2.0 = 70pts, 2.0-2.5 = 50pts, 2.5-3.0 = 30pts, >3.0 = 20pts
  let efficiencyScore: number;
  if (avgRatio < 1.5) efficiencyScore = 100;
  else if (avgRatio < 2.0) efficiencyScore = 70 + (2.0 - avgRatio) * 60; // 70-100
  else if (avgRatio < 2.5) efficiencyScore = 50 + (2.5 - avgRatio) * 40; // 50-70
  else if (avgRatio < 3.0) efficiencyScore = 30 + (3.0 - avgRatio) * 40; // 30-50
  else efficiencyScore = 20;

  // === VOLUMETRY SCORE: Based on % off hours and peak/valley ratio ===
  // % off hours >30% penalizes, peak/valley ratio >3x penalizes
  const offHoursPct = hourlyDistribution.off_hours_pct;

  // Calculate peak/valley ratio (consistent with backendMapper.ts)
  const hourlyValues = hourlyDistribution.hourly.filter(v => v > 0);
  const peakVolume = hourlyValues.length > 0 ? Math.max(...hourlyValues) : 0;
  const valleyVolume = hourlyValues.length > 0 ? Math.min(...hourlyValues) : 1;
  const peakValleyRatio = valleyVolume > 0 ? peakVolume / valleyVolume : 1;

  // Volumetry score: 100 base, penalize by off hours y peak/valley ratio
  // NOTA: Formulas synchronized with backendMapper.ts buildVolumetryDimension()
  let volumetryScore = 100;

  // Penalty for off hours (same formula as backendMapper)
  if (offHoursPct > 30) {
    volumetryScore -= Math.min(40, (offHoursPct - 30) * 2); // -2 pts per each % above 30%
  } else if (offHoursPct > 20) {
    volumetryScore -= (offHoursPct - 20); // -1 pt per each % between 20-30%
  }

  // Penalty for peak/valley ratio high (same formula as backendMapper)
  if (peakValleyRatio > 5) {
    volumetryScore -= 30;
  } else if (peakValleyRatio > 3) {
    volumetryScore -= 20;
  } else if (peakValleyRatio > 2) {
    volumetryScore -= 10;
  }

  volumetryScore = Math.max(0, Math.min(100, Math.round(volumetryScore)));

  // === CPI: Use the centralized value passed as parameter ===
  // globalCPI was already calculated in generateAnalysisFromRealData from heatmapData
  // This ensures consistency with ExecutiveSummaryTab
  const costPerInteraction = globalCPI;

  // Calculate Agentic Score
  const predictability = Math.max(0, Math.min(10, 10 - ((avgCV - 0.3) / 1.2 * 10)));
  const complexityInverse = Math.max(0, Math.min(10, 10 - (avgTransferRate / 10)));
  const repetitivity = Math.min(10, totalVolume / 500);
  const agenticScore = predictability * 0.30 + complexityInverse * 0.30 + repetitivity * 0.25 + 2.5;

  // Determine percentile of Eficiencia based on benchmark airline sector (ratio <2.0 healthy)
  const efficiencyPercentile = avgRatio < 2.0 ? 75 : avgRatio < 2.5 ? 50 : avgRatio < 3.0 ? 35 : 20;

  // Determine percentile of FCR based on benchmark airline sector (70%)
  const fcrPercentile = avgFCR >= 70 ? 75 : avgFCR >= 60 ? 50 : avgFCR >= 50 ? 35 : 20;

  return [
    // 1. VOLUMETRY & DISTRIBUTION
    {
      id: 'volumetry_distribution',
      name: 'volumetry_distribution',
      title: 'Volumetry & Distribution',
      score: volumetryScore,
      percentile: offHoursPct <= 20 ? 80 : offHoursPct <= 30 ? 60 : 40,
      summary: `${offHoursPct.toFixed(1)}% off hours. Ratio pico/valle: ${peakValleyRatio.toFixed(1)}x. ${totalVolume.toLocaleString('es-ES')} total interactions.`,
      kpi: { label: 'Off Hours', value: `${offHoursPct.toFixed(0)}%` },
      icon: BarChartHorizontal,
      distribution_data: {
        hourly: hourlyDistribution.hourly,
        off_hours_pct: hourlyDistribution.off_hours_pct,
        peak_hours: hourlyDistribution.peak_hours
      }
    },
    // 2. OPERATIONAL EFFICIENCY - KPI principal: AHT P50 (industry standard)
    {
      id: 'operational_efficiency',
      name: 'operational_efficiency',
      title: 'Operational Efficiency',
      score: Math.round(efficiencyScore),
      percentile: efficiencyPercentile,
      summary: `AHT P50: ${avgAHT}s (benchmark: 300s). Ratio P90/P50: ${avgRatio.toFixed(2)} (benchmark: <2.0). Hold time: ${Math.round(avgHoldTime)}s.`,
      kpi: { label: 'AHT P50', value: `${avgAHT}s` },
      icon: Zap
    },
    // 3. EFFECTIVENESS & RESOLUTION (Technical FCR = 100 - transfer_rate)
    {
      id: 'effectiveness_resolution',
      name: 'effectiveness_resolution',
      title: 'Effectiveness & Resolution',
      score: avgFCR >= 90 ? 100 : avgFCR >= 85 ? 80 : avgFCR >= 80 ? 60 : avgFCR >= 75 ? 40 : 20,
      percentile: fcrPercentile,
      summary: `Technical FCR: ${avgFCR.toFixed(1)}% (benchmark: 85-90%). Transfer: ${avgTransferRate.toFixed(1)}%.`,
      kpi: { label: 'Technical FCR', value: `${Math.round(avgFCR)}%` },
      icon: Target
    },
    // 4. COMPLEXITY COMPLEJIDAD & PREDICTABILITY PREDICTABILITY - KPI principal: CV AHT (industry standard for predictability)
    {
      id: 'complexity_predictability',
      name: 'complexity_predictability',
      title: 'Complexity Complejidad & Predictibilidad Predictability',
      score: avgCV <= 0.75 ? 100 : avgCV <= 1.0 ? 80 : avgCV <= 1.25 ? 60 : avgCV <= 1.5 ? 40 : 20, // Based on CV AHT
      percentile: avgCV <= 0.75 ? 75 : avgCV <= 1.0 ? 55 : avgCV <= 1.25 ? 40 : 25,
      summary: `CV AHT: ${(avgCV * 100).toFixed(0)}% (benchmark: <75%). Hold time: ${Math.round(avgHoldTime)}s. ${avgCV <= 0.75 ? 'High predictability for WFM.' : avgCV <= 1.0 ? 'Acceptable predictability.' : 'High variability, complicates planning.'}`,
      kpi: { label: 'CV AHT', value: `${(avgCV * 100).toFixed(0)}%` },
      icon: Brain
    },
    // 5. SATISFACTION - CSAT
    {
      id: 'customer_satisfaction',
      name: 'customer_satisfaction',
      title: 'Customer Satisfaction',
      score: avgCsat > 0 ? Math.round(avgCsat) : 0,
      percentile: avgCsat > 0 ? (avgCsat >= 80 ? 70 : avgCsat >= 60 ? 50 : 30) : 0,
      summary: avgCsat > 0
        ? `CSAT: ${avgCsat.toFixed(1)}/100. ${avgCsat >= 80 ? 'High satisfaction.' : avgCsat >= 60 ? 'Acceptable satisfaction.' : 'Requires attention.'}`
        : 'CSAT: Not available in dataset. Consider implementing post-call surveys.',
      kpi: { label: 'CSAT', value: avgCsat > 0 ? `${Math.round(avgCsat)}/100` : 'N/A' },
      icon: Smile
    },
    // 6. ECONOMY - CPI (airline benchmark: p25=2.20, p50=3.50, p75=4.50, p90=5.50)
    {
      id: 'economy_cpi',
      name: 'economy_cpi',
      title: 'Operational Economy',
      // Score based on percentiles airlines (CPI inverted: lower = better)
      score: costPerInteraction <= 2.20 ? 100 : costPerInteraction <= 3.50 ? 80 : costPerInteraction <= 4.50 ? 60 : costPerInteraction <= 5.50 ? 40 : 20,
      percentile: costPerInteraction <= 2.20 ? 90 : costPerInteraction <= 3.50 ? 70 : costPerInteraction <= 4.50 ? 50 : costPerInteraction <= 5.50 ? 25 : 10,
      summary: `CPI: €${costPerInteraction.toFixed(2)} per interaction. Annual cost: €${totalCost.toLocaleString('es-ES')}. Airline sector benchmark: €3.50.`,
      kpi: { label: 'Cost/Interaction', value: `€${costPerInteraction.toFixed(2)}` },
      icon: DollarSign
    },
    // 7. AGENTIC READINESS
    {
      id: 'agentic_readiness',
      name: 'agentic_readiness',
      title: 'Agentic Readiness',
      score: Math.round(agenticScore * 10),
      percentile: agenticScore >= 7 ? 75 : agenticScore >= 5 ? 55 : 35,
      summary: `Score: ${agenticScore.toFixed(1)}/10. ${agenticScore >= 8 ? 'Excellent for automation.' : agenticScore >= 5 ? 'Candidate for AI assistance.' : 'Requires prior optimization.'}`,
      kpi: { label: 'Score', value: `${agenticScore.toFixed(1)}/10` },
      icon: Bot
    }
  ];
}

/**
 * Calcular Agentic Readiness from datos reales
 * Score = Σ(factor_i × weight_i) with 6 unique factors
 */
function calculateAgenticReadinessFromRealData(metrics: SkillMetrics[]): AgenticReadinessResult {
  const totalVolume = metrics.reduce((sum, m) => sum + m.volume, 0);
  const avgCV = metrics.reduce((sum, m) => sum + m.cv_aht, 0) / metrics.length;
  const avgCVTalk = metrics.reduce((sum, m) => sum + m.cv_talk_time, 0) / metrics.length;
  const avgTransferRate = metrics.reduce((sum, m) => sum + m.transfer_rate, 0) / metrics.length;
  const totalCost = metrics.reduce((sum, m) => sum + m.total_cost, 0);

  // === 6 UNIQUE FACTORS ===

  // 1. Predictibilidad (CV AHT) - Peso 25%
  // Score = 10 - (CV_AHT × 10). CV < 30% = Score > 7
  const predictability = Math.max(0, Math.min(10, 10 - (avgCV * 10)));

  // 2. Simplicidad Operativa (Transfer Rate) - Peso 20%
  // Score = 10 - (Transfer / 5). Transfer < 10% = Score > 8
  const complexity_inverse = Math.max(0, Math.min(10, 10 - (avgTransferRate / 5)));

  // 3. Volumen e Impacto - Peso 15%
  // Score lineal: < 100 = 0, 100-5000 interpolation, > 5000 = 10
  let repetitiveness = 0;
  if (totalVolume >= 5000) repetitiveness = 10;
  else if (totalVolume <= 100) repetitiveness = 0;
  else repetitiveness = ((totalVolume - 100) / (5000 - 100)) * 10;

  // 4. Structuring (CV Talk Time) - Peso 15%
  // Score = 10 - (CV_Talk × 8). Low variability = high structuring
  const estructuracion = Math.max(0, Math.min(10, 10 - (avgCVTalk * 8)));

  // 5. Estabilidad (peak/valley ratio simplificado) - Peso 10%
  // Simplification: based on CV general as proxy
  const estabilidad = Math.max(0, Math.min(10, 10 - (avgCV * 5)));

  // 6. ROI Potencial (based on coste y volumen) - Peso 15%
  // Score = min(10, log10(Coste) - 2) para costes > €100
  const roiPotencial = totalCost > 100
    ? Math.max(0, Math.min(10, (Math.log10(totalCost) - 2) * 2.5))
    : 0;

  // Final weighted Score: (10×0.25)+(5×0.20)+(10×0.15)+(0×0.15)+(10×0.10)+(10×0.15)
  const score = Math.round((
    predictability * 0.25 +
    complexity_inverse * 0.20 +
    repetitiveness * 0.15 +
    estructuracion * 0.15 +
    estabilidad * 0.10 +
    roiPotencial * 0.15
  ) * 10) / 10;

  // Tier based on score (umbrales actualizados)
  let tier: TierKey;
  if (score >= 6) tier = 'gold';    // Listo para Copilot
  else if (score >= 4) tier = 'silver';  // Optimizar primero
  else tier = 'bronze';  // Requires human management

  // Sub-factors with unique descriptions and specific methodologies
  const sub_factors: SubFactor[] = [
    {
      name: 'predictibilidad',
      displayName: 'Predictibilidad',
      score: Math.round(predictability * 10) / 10,
      weight: 0.25,
      description: `CV AHT: ${Math.round(avgCV * 100)}%. Score = 10 - (CV × 10)`
    },
    {
      name: 'complejidad_inversa',
      displayName: 'Simplicidad Operativa',
      score: Math.round(complexity_inverse * 10) / 10,
      weight: 0.20,
      description: `Transfer rate: ${Math.round(avgTransferRate)}%. Score = 10 - (Transfer / 5)`
    },
    {
      name: 'repetitividad',
      displayName: 'Volumen e Impacto',
      score: Math.round(repetitiveness * 10) / 10,
      weight: 0.15,
      description: `${totalVolume.toLocaleString('es-ES')} interactions. Scale lineal 100-5000`
    },
    {
      name: 'estructuracion',
      displayName: 'Structuring',
      score: Math.round(estructuracion * 10) / 10,
      weight: 0.15,
      description: `CV Talk: ${Math.round(avgCVTalk * 100)}%. Score = 10 - (CV_Talk × 8)`
    },
    {
      name: 'estabilidad',
      displayName: 'Estabilidad Temporal',
      score: Math.round(estabilidad * 10) / 10,
      weight: 0.10,
      description: `Based on general variability. Score = 10 - (CV × 5)`
    },
    {
      name: 'roi_potencial',
      displayName: 'ROI Potencial',
      score: Math.round(roiPotencial * 10) / 10,
      weight: 0.15,
      description: `Annual cost: €${totalCost.toLocaleString('es-ES')}. Logarithmic score`
    }
  ];

  // Interpretation based on umbrales actualizados
  let interpretation: string;
  if (score >= 6) {
    interpretation = 'Listo para Copilot. Procesos con predictibilidad y simplicidad suficientes para asistencia IA.';
  } else if (score >= 4) {
    interpretation = 'Requires optimization. Standardize processes and reduce variability before implementing AI.';
  } else {
    interpretation = 'Human management recommended. Complex or variable processes that require human intervention.';
  }

  return {
    score,
    sub_factors,
    tier,
    confidence: totalVolume > 1000 ? 'high' as const : totalVolume > 500 ? 'medium' as const : 'low' as const,
    interpretation
  };
}

/**
 * Generar findings from datos reales - SOLO datos calculados del dataset
 */
function generateFindingsFromRealData(
  metrics: SkillMetrics[],
  interactions: RawInteraction[],
  hourlyDistribution?: { hourly: number[]; off_hours_pct: number; peak_hours: number[] }
): Finding[] {
  const findings: Finding[] = [];
  const totalVolume = interactions.length;

  // Calculate metrics globales
  const avgCV = metrics.reduce((sum, m) => sum + m.cv_aht, 0) / metrics.length;
  const avgTransferRate = metrics.reduce((sum, m) => sum + m.transfer_rate, 0) / metrics.length;
  const avgRatio = 1 + avgCV * 1.5;

  // Calculate abandonment real
  const totalAbandoned = metrics.reduce((sum, m) => sum + m.abandon_count, 0);
  const abandonRate = totalVolume > 0 ? (totalAbandoned / totalVolume) * 100 : 0;

  // Finding 0: Alto off hours volume - oportunidad para agent virtual
  const offHoursPct = hourlyDistribution?.off_hours_pct ?? 0;
  if (offHoursPct > 20) {
    const offHoursVolume = Math.round(totalVolume * offHoursPct / 100);
    findings.push({
      type: offHoursPct > 30 ? 'critical' : 'warning',
      title: 'Alto Volumen Off Hours',
      text: `${offHoursPct.toFixed(0)}% of interactions off hours (8-19h)`,
      dimensionId: 'volumetry_distribution',
      description: `${offHoursVolume.toLocaleString()} interactions (${offHoursPct.toFixed(1)}%) occur outside business hours. Ideal opportunity to implement virtual agents 24/7.`,
      impact: offHoursPct > 30 ? 'high' : 'medium'
    });
  }

  // Finding 1: Ratio P90/P50 if outside benchmark
  if (avgRatio > 2.0) {
    findings.push({
      type: avgRatio > 3.0 ? 'critical' : 'warning',
      title: 'Ratio P90/P50 elevado',
      text: `Ratio P90/P50: ${avgRatio.toFixed(2)}`,
      dimensionId: 'operational_efficiency',
      description: `Ratio P90/P50 de ${avgRatio.toFixed(2)} exceeds the benchmark of 2.0. Indicates high dispersion in handling times.`
    });
  }

  // Finding 2: Variabilidad alta (CV AHT)
  const highVariabilitySkills = metrics.filter(m => m.cv_aht > 0.45);
  if (highVariabilitySkills.length > 0) {
    findings.push({
      type: 'warning',
      title: 'Alta Variabilidad AHT',
      text: `${highVariabilitySkills.length} skills con CV > 45%`,
      dimensionId: 'complexity_predictability',
      description: `${highVariabilitySkills.length} de ${metrics.length} skills show CV AHT > 45%, suggesting poorly standardized processes.`
    });
  }

  // Finding 3: Transferencias altas
  if (avgTransferRate > 15) {
    findings.push({
      type: avgTransferRate > 25 ? 'critical' : 'warning',
      title: 'Tasa de Transferencia',
      text: `Transfer rate: ${avgTransferRate.toFixed(1)}%`,
      dimensionId: 'complexity_predictability',
      description: `Tasa de transferencia average de ${avgTransferRate.toFixed(1)}% indicates need for training or routing.`
    });
  }

  // Finding 4: Abandono si supera benchmark
  if (abandonRate > 5) {
    findings.push({
      type: abandonRate > 10 ? 'critical' : 'warning',
      title: 'Tasa de Abandono',
      text: `Abandono: ${abandonRate.toFixed(1)}%`,
      dimensionId: 'effectiveness_resolution',
      description: `Tasa de abandonment de ${abandonRate.toFixed(1)}% exceeds the benchmark of 5%. Review capacity and wait times.`
    });
  }

  // Finding 5: Volume concentration (only if there are enough skills)
  if (metrics.length >= 3) {
    const topSkill = metrics[0];
    const topSkillPct = (topSkill.volume / totalVolume) * 100;
    if (topSkillPct > 30) {
      findings.push({
        type: 'info',
        title: 'Volume Concentration',
        text: `${topSkill.skill}: ${topSkillPct.toFixed(0)}% del total`,
        dimensionId: 'volumetry_distribution',
        description: `El skill "${topSkill.skill}" concentrates ${topSkillPct.toFixed(1)}% of total volume (${topSkill.volume.toLocaleString()} interactions).`
      });
    }
  }

  return findings;
}

/**
 * Generar recomendaciones from datos reales
 */
function generateRecommendationsFromRealData(
  metrics: SkillMetrics[],
  hourlyDistribution?: { hourly: number[]; off_hours_pct: number; peak_hours: number[] },
  totalVolume?: number
): Recommendation[] {
  const recommendations: Recommendation[] = [];

  // Priority recommendation: Virtual agent for off hours
  const offHoursPct = hourlyDistribution?.off_hours_pct ?? 0;
  const volume = totalVolume ?? metrics.reduce((sum, m) => sum + m.volume, 0);
  if (offHoursPct > 20) {
    const offHoursVolume = Math.round(volume * offHoursPct / 100);
    const estimatedContainment = offHoursPct > 30 ? 60 : 45; // % que puede resolver el bot
    const estimatedSavings = Math.round(offHoursVolume * estimatedContainment / 100);
    recommendations.push({
      priority: 'high',
      title: 'Implement Virtual Agent 24/7',
      text: `Desplegar agent virtual para atender ${offHoursPct.toFixed(0)}% of interactions off hours`,
      description: `${offHoursVolume.toLocaleString()} interactions occur outside business hours (19:00-08:00). A virtual agent can resolve ~${estimatedContainment}% of these queries automatically, freeing human resources and improving customer experience with immediate attention 24/7.`,
      dimensionId: 'volumetry_distribution',
      impact: `Containment potential: ${estimatedSavings.toLocaleString()} interactions/period`,
      timeline: '1-3 months'
    });
  }

  const highVariabilitySkills = metrics.filter(m => m.cv_aht > 0.45);
  if (highVariabilitySkills.length > 0) {
    recommendations.push({
      priority: 'high',
      title: 'Estandarizar Procesos',
      text: `Create guides and scripts for the ${highVariabilitySkills.length} skills with high variability`,
      description: `Create guides and scripts for the ${highVariabilitySkills.length} skills with high variability.`,
      impact: 'Reduction of 20-30% en AHT'
    });
  }

  const highVolumeSkills = metrics.filter(m => m.volume > 500);
  if (highVolumeSkills.length > 0) {
    recommendations.push({
      priority: 'high',
      title: 'Automate Skills de Alto Volumen',
      text: `Implement bots for the ${highVolumeSkills.length} skills con > 500 interactions`,
      description: `Implement bots for the ${highVolumeSkills.length} skills con > 500 interactions.`,
      impact: 'Ahorro estimado del 40-60%'
    });
  }

  return recommendations;
}

/**
 * v3.3: Generar opportunities from drilldownData (based on queues with CV < 75%)
 * Las oportunidades se clasifican en 3 categorys:
 * - Automatizar: Colas con CV < 75% (estables, listas para IA)
 * - Asistir: Colas con CV 75-100% (necesitan copilot)
 * - Optimize: Queues with CV > 100% (need standardization first)
 */
/**
 * v3.5: Calculate realistic savings using TCO formula by tier
 *
 * TCO formula by tier:
 * - AUTOMATE (Tier 1): 70% containment → savings = vol_annual × 0.70 × (CPI_human - CPI_ai)
 * - ASSIST (Tier 2): 30% efficiency → savings = vol_annual × 0.30 × (CPI_human - CPI_copilot)
 * - AUGMENT (Tier 3): 15% optimization → savings = vol_annual × 0.15 × (CPI_human - CPI_optimized)
 * - HUMAN-ONLY (Tier 4): 0% → without ahorro
 *
 * Costes per interaction (CPI):
 * - CPI_human: Se calcula from AHT y cost_per_hour (~€4-5/interaction)
 * - CPI_ai: €0.15/interaction (chatbot/IVR)
 * - CPI_copilot: ~60% del CPI human (agent asistido)
 * - CPI_optimized: ~85% del CPI human (mejora marginal)
 */
/**
 * v3.6: CPI constants for TCO savings calculation
 * Values aligned with Beyond methodology
 */
const CPI_CONFIG = {
  CPI_HUMANO: 2.33,    // €/interaction - coste actual agent human
  CPI_BOT: 0.15,       // €/interaction - cost bot/automation
  CPI_ASSIST: 1.50,    // €/interaction - coste con copilot
  CPI_AUGMENT: 2.00,   // €/interaction - coste optimizado
  // Success/containment rates by tier
  RATE_AUTOMATE: 0.70, // 70% containment in automation
  RATE_ASSIST: 0.30,   // 30% efficiency en asistencia
  RATE_AUGMENT: 0.15   // 15% improvement in optimization
};

// Data period: the volume in the data corresponds to 11 months, is not monthly
const DATA_PERIOD_MONTHS = 11;

/**
 * v4.2: Calculate realistic TCO savings using explicit formula with fixed CPI
 * IMPORTANT: The volume in the data corresponds to 11 months, therefore:
 * - First we calculate monthly volume: Vol / 11
 * - Then we annualize: × 12
 * Formulas:
 * - AUTOMATE: (Vol/11) × 12 × 70% × (CPI_human - CPI_bot)
 * - ASSIST: (Vol/11) × 12 × 30% × (CPI_human - CPI_assist)
 * - AUGMENT: (Vol/11) × 12 × 15% × (CPI_human - CPI_augment)
 * - HUMAN-ONLY: 0€
 */
function calculateRealisticSavings(
  volume: number,
  _annualCost: number, // Kept for compatibility but not used
  tier: 'AUTOMATE' | 'ASSIST' | 'AUGMENT' | 'HUMAN-ONLY'
): number {
  if (volume === 0) return 0;

  const { CPI_HUMANO, CPI_BOT, CPI_ASSIST, CPI_AUGMENT, RATE_AUTOMATE, RATE_ASSIST, RATE_AUGMENT } = CPI_CONFIG;

  // Convert volume from period (11 months) to annual volume
  const annualVolume = (volume / DATA_PERIOD_MONTHS) * 12;

  switch (tier) {
    case 'AUTOMATE':
      // Savings = VolAnual × 70% × (CPI_human - CPI_bot)
      return Math.round(annualVolume * RATE_AUTOMATE * (CPI_HUMANO - CPI_BOT));

    case 'ASSIST':
      // Savings = VolAnual × 30% × (CPI_human - CPI_assist)
      return Math.round(annualVolume * RATE_ASSIST * (CPI_HUMANO - CPI_ASSIST));

    case 'AUGMENT':
      // Savings = VolAnual × 15% × (CPI_human - CPI_augment)
      return Math.round(annualVolume * RATE_AUGMENT * (CPI_HUMANO - CPI_AUGMENT));

    case 'HUMAN-ONLY':
    default:
      return 0;
  }
}

export function generateOpportunitiesFromDrilldown(drilldownData: DrilldownDataPoint[], costPerHour: number): Opportunity[] {
  // v4.3: Top 10 initiatives by economic potential (all tiers, not only AUTOMATE)
  // Each queue = 1 bubble with its real score and real TCO savings according to its tier

  // Extract all queues with their parent skill (exclude HUMAN-ONLY, have no savings)
  const allQueues = drilldownData.flatMap(skill =>
    skill.originalQueues
      .filter(q => q.tier !== 'HUMAN-ONLY')  // HUMAN-ONLY generates no savings
      .map(q => ({
        ...q,
        skillName: skill.skill
      }))
  );

  if (allQueues.length === 0) {
    console.warn('⚠️ No queues with savings potential to show in Opportunity Matrix');
    return [];
  }

  // Calculate TCO savings per individual queue according to its tier
  const queuesWithSavings = allQueues.map(q => {
    const savings = calculateRealisticSavings(q.volume, q.annualCost || 0, q.tier);
    return { ...q, savings };
  });

  // Sort by descending savings
  queuesWithSavings.sort((a, b) => b.savings - a.savings);

  // Calculate max savings to scale impact to 0-10
  const maxSavings = Math.max(...queuesWithSavings.map(q => q.savings), 1);

  // Mapping of tier to dimensionId and customer_segment
  const tierToDimension: Record<string, string> = {
    'AUTOMATE': 'agentic_readiness',
    'ASSIST': 'effectiveness_resolution',
    'AUGMENT': 'complexity_predictability'
  };
  const tierToSegment: Record<string, CustomerSegment> = {
    'AUTOMATE': 'high',
    'ASSIST': 'medium',
    'AUGMENT': 'low'
  };

  // Generate individual opportunities (TOP 10 by economic potential)
  const opportunities: Opportunity[] = queuesWithSavings
    .slice(0, 10)
    .map((q, idx) => {
      // Impact: savings scaled to 0-10
      const impactRaw = (q.savings / maxSavings) * 10;
      const impact = Math.max(1, Math.min(10, Math.round(impactRaw * 10) / 10));

      // Feasibility: direct agenticScore (already is 0-10)
      const feasibility = Math.round(q.agenticScore * 10) / 10;

      // Nombre con prefijo de tier para claridad
      const tierPrefix = q.tier === 'AUTOMATE' ? '🤖' : q.tier === 'ASSIST' ? '🤝' : '📚';
      const shortName = q.original_queue_id.length > 22
        ? `${tierPrefix} ${q.original_queue_id.substring(0, 19)}...`
        : `${tierPrefix} ${q.original_queue_id}`;

      return {
        id: `opp-${q.tier.toLowerCase()}-${idx + 1}`,
        name: shortName,
        impact,
        feasibility,
        savings: q.savings,
        dimensionId: tierToDimension[q.tier] || 'agentic_readiness',
        customer_segment: tierToSegment[q.tier] || 'medium'
      };
    });

  console.log(`📊 Opportunity Matrix: Top ${opportunities.length} initiatives by economic potential (de ${allQueues.length} queues with ahorro)`);

  return opportunities;
}

/**
 * v3.5: Generar roadmap from drilldownData usando sistema de Tiers
 * Initiatives structured in 3 phases based on Tier classification:
 * - Phase 1 (Automate): AUTOMATE tier Queues - direct AI implementation (70% containment)
 * - Phase 2 (Assist): Colas tier ASSIST - copilot y asistencia (30% efficiency)
 * - Phase 3 (Augment): AUGMENT/HUMAN-ONLY tier Queues - standardization first (15%)
 */
export function generateRoadmapFromDrilldown(drilldownData: DrilldownDataPoint[], costPerHour: number): RoadmapInitiative[] {
  const initiatives: RoadmapInitiative[] = [];
  let initCounter = 1;

  // Extract y clasificar all las queues por TIER
  const allQueues = drilldownData.flatMap(skill =>
    skill.originalQueues.map(q => ({
      ...q,
      skillName: skill.skill
    }))
  );

  // v3.5: Clasificar por TIER
  const automateQueues = allQueues.filter(q => q.tier === 'AUTOMATE');
  const assistQueues = allQueues.filter(q => q.tier === 'ASSIST');
  const augmentQueues = allQueues.filter(q => q.tier === 'AUGMENT');
  const humanQueues = allQueues.filter(q => q.tier === 'HUMAN-ONLY');

  // Calculate metrics por tier
  const automateVolume = automateQueues.reduce((sum, q) => sum + q.volume, 0);
  const automateCost = automateQueues.reduce((sum, q) => sum + (q.annualCost || 0), 0);
  const assistVolume = assistQueues.reduce((sum, q) => sum + q.volume, 0);
  const assistCost = assistQueues.reduce((sum, q) => sum + (q.annualCost || 0), 0);
  const augmentVolume = augmentQueues.reduce((sum, q) => sum + q.volume, 0);
  const augmentCost = augmentQueues.reduce((sum, q) => sum + (q.annualCost || 0), 0);

  // Helper para obtain top skills por volumen
  const getTopSkillNamonth = (queues: typeof allQueues, limit: number = 3): string[] => {
    const skillVolumonth = new Map<string, number>();
    queues.forEach(q => {
      skillVolumonth.set(q.skillName, (skillVolumonth.get(q.skillName) || 0) + q.volume);
    });
    return Array.from(skillVolumonth.entries())
      .sort((a, b) => b[1] - a[1])
      .slice(0, limit)
      .map(([name]) => name);
  };

  // ============ PHASE 1: AUTOMATE (Tier AUTOMATE - 70% containment) ============
  if (automateQueues.length > 0) {
    const topSkills = getTopSkillNamonth(automateQueues);
    const avgScore = automateQueues.reduce((sum, q) => sum + q.agenticScore, 0) / automateQueues.length;
    const avgCv = automateQueues.reduce((sum, q) => sum + q.cv_aht, 0) / automateQueues.length;

    // v3.5: Ahorro REALISTA con TCO
    const realisticSavings = calculateRealisticSavings(automateVolume, automateCost, 'AUTOMATE');

    // Chatbot para queues with score muy high (>8)
    const highScoreQueues = automateQueues.filter(q => q.agenticScore >= 8);
    if (highScoreQueues.length > 0) {
      const hsVolume = highScoreQueues.reduce((sum, q) => sum + q.volume, 0);
      const hsCost = highScoreQueues.reduce((sum, q) => sum + (q.annualCost || 0), 0);
      const hsSavings = calculateRealisticSavings(hsVolume, hsCost, 'AUTOMATE');

      initiatives.push({
        id: `init-${initCounter++}`,
        name: `Chatbot IA para ${highScoreQueues.length} queues score ≥8`,
        phase: RoadmapPhase.Automate,
        timeline: 'Q1 2026',
        investment: Math.round(hsSavings * 0.3), // Investment = 30% of savings
        resources: ['1x Bot Developer', 'API Integration', 'QA Team'],
        dimensionId: 'agentic_readiness',
        risk: 'low',
        skillsImpacted: getTopSkillNamonth(highScoreQueues, 2),
        volumeImpacted: hsVolume,
        kpiObjective: `Contain 70% of volume via chatbot`,
        rationale: `${highScoreQueues.length} queues tier AUTOMATE con score average ${avgScore.toFixed(1)}/10. Optimal metrics for complete automation.`,
        savingsDetail: `70% containment × (CPI human - CPI AI) = ${hsSavings.toLocaleString()}€/year`,
        estimatedSavings: hsSavings,
        resourceHours: 400
      });
    }

    // IVR para resto de queues AUTOMATE
    const otherAutomateQueues = automateQueues.filter(q => q.agenticScore < 8);
    if (otherAutomateQueues.length > 0) {
      const oaVolume = otherAutomateQueues.reduce((sum, q) => sum + q.volume, 0);
      const oaCost = otherAutomateQueues.reduce((sum, q) => sum + (q.annualCost || 0), 0);
      const oaSavings = calculateRealisticSavings(oaVolume, oaCost, 'AUTOMATE');

      initiatives.push({
        id: `init-${initCounter++}`,
        name: `IVR inteligente para ${otherAutomateQueues.length} queues AUTOMATE`,
        phase: RoadmapPhase.Automate,
        timeline: 'Q2 2026',
        investment: Math.round(oaSavings * 0.25),
        resources: ['1x Voice UX Designer', 'Integration Team', 'QA'],
        dimensionId: 'agentic_readiness',
        risk: 'low',
        skillsImpacted: getTopSkillNamonth(otherAutomateQueues, 2),
        volumeImpacted: oaVolume,
        kpiObjective: `Pre-calificar y desviar 70% a self-service`,
        rationale: `${otherAutomateQueues.length} queues tier AUTOMATE listas para IVR con NLU.`,
        savingsDetail: `70% containment × CPI differential = ${oaSavings.toLocaleString()}€/year`,
        estimatedSavings: oaSavings,
        resourceHours: 320
      });
    }
  }

  // ============ PHASE 2: ASSIST (Tier ASSIST - 30% efficiency) ============
  if (assistQueues.length > 0) {
    const topSkills = getTopSkillNamonth(assistQueues);
    const avgScore = assistQueues.reduce((sum, q) => sum + q.agenticScore, 0) / assistQueues.length;

    // v3.5: Ahorro REALISTA
    const realisticSavings = calculateRealisticSavings(assistVolume, assistCost, 'ASSIST');

    // Knowledge Base con IA
    initiatives.push({
      id: `init-${initCounter++}`,
      name: `Knowledge Base IA para ${assistQueues.length} queues ASSIST`,
      phase: RoadmapPhase.Assist,
      timeline: 'Q2 2026',
      investment: Math.round(realisticSavings * 0.4),
      resources: ['1x PM', 'Content Team', 'AI Developer'],
      dimensionId: 'effectiveness_resolution',
      risk: 'low',
      skillsImpacted: topSkills,
      volumeImpacted: assistVolume,
      kpiObjective: `Reduce AHT 30% with AI suggestions`,
      rationale: `${assistQueues.length} queues tier ASSIST (score ${avgScore.toFixed(1)}/10) se benefician de copilot contextual.`,
      savingsDetail: `30% efficiency × CPI differential = ${realisticSavings.toLocaleString()}€/year`,
      estimatedSavings: realisticSavings,
      resourceHours: 360
    });

    // Copilot para agents si hay volumen high
    if (assistVolume > 50000) {
      const copilotSavings = Math.round(realisticSavings * 0.6);
      initiatives.push({
        id: `init-${initCounter++}`,
        name: `Copilot IA para agents (${topSkills.slice(0, 2).join(', ')})`,
        phase: RoadmapPhase.Assist,
        timeline: 'Q3 2026',
        investment: Math.round(copilotSavings * 0.5),
        resources: ['2x AI Developers', 'QA Team', 'Training'],
        dimensionId: 'effectiveness_resolution',
        risk: 'medium',
        skillsImpacted: topSkills.slice(0, 3),
        volumeImpacted: assistVolume,
        kpiObjective: `Reduce variability and migrate queues to tier AUTOMATE`,
        rationale: `Copilot pre-fills fields, suggests answers and guides agent to standardize.`,
        savingsDetail: `Mejora efficiency 30% en ${assistVolume.toLocaleString()} int/month`,
        estimatedSavings: copilotSavings,
        resourceHours: 520
      });
    }
  }

  // ============ PHASE 3: AUGMENT (Tier AUGMENT + HUMAN-ONLY - 15%) ============
  const optimizeQueues = [...augmentQueues, ...humanQueues];
  const optimizeVolume = optimizeQueues.reduce((sum, q) => sum + q.volume, 0);
  const optimizeCost = optimizeQueues.reduce((sum, q) => sum + (q.annualCost || 0), 0);

  if (optimizeQueues.length > 0) {
    const topSkills = getTopSkillNamonth(optimizeQueues);
    const avgScore = optimizeQueues.reduce((sum, q) => sum + q.agenticScore, 0) / optimizeQueues.length;

    // v3.5: Ahorro REALISTA (muy conservative para AUGMENT)
    const realisticSavings = calculateRealisticSavings(optimizeVolume, optimizeCost, 'AUGMENT');

    // Process Standardization
    initiatives.push({
      id: `init-${initCounter++}`,
      name: `Standardization (${optimizeQueues.length} variable queues)`,
      phase: RoadmapPhase.Augment,
      timeline: 'Q3 2026',
      investment: Math.round(realisticSavings * 0.8),
      resources: ['Process Analyst', 'Training Team', 'QA'],
      dimensionId: 'complexity_predictability',
      risk: 'medium',
      skillsImpacted: topSkills,
      volumeImpacted: optimizeVolume,
      kpiObjective: `Reduce CV to migrate queues to tier ASSIST/AUTOMATE`,
      rationale: `${optimizeQueues.length} queues tier AUGMENT/HUMAN (score ${avgScore.toFixed(1)}/10) require process redesign.`,
      savingsDetail: `15% optimization = ${realisticSavings.toLocaleString()}€/year (conservative)`,
      estimatedSavings: realisticSavings,
      resourceHours: 400
    });

    // Post-standardization Automation (futuro)
    if (optimizeVolume > 30000) {
      const futureSavings = calculateRealisticSavings(Math.round(optimizeVolume * 0.4), Math.round(optimizeCost * 0.4), 'ASSIST');
      initiatives.push({
        id: `init-${initCounter++}`,
        name: `Post-standardization Automation`,
        phase: RoadmapPhase.Augment,
        timeline: 'Q1 2027',
        investment: Math.round(futureSavings * 0.5),
        resources: ['Lead AI Engineer', 'Process Team', 'QA'],
        dimensionId: 'agentic_readiness',
        risk: 'medium',
        skillsImpacted: topSkills.slice(0, 2),
        volumeImpacted: Math.round(optimizeVolume * 0.4),
        kpiObjective: `Automate 40% of volume after standardization`,
        rationale: `Once CV is reduced, queues will be suitable for automation.`,
        savingsDetail: `Future potential: ${futureSavings.toLocaleString()}€/year`,
        estimatedSavings: futureSavings,
        resourceHours: 480
      });
    }
  }

  return initiatives;
}

/**
 * Use generateOpportunitiesFromDrilldown instead
 * Generate opportunities from real data
 */
function generateOpportunitiesFromRealData(metrics: SkillMetrics[], costPerHour: number): Opportunity[] {
  // Find the maximum savings to calculate relative impact
  const maxSavings = Math.max(...metrics.map(m => m.total_cost * 0.4), 1);

  return metrics.slice(0, 10).map((m, index) => {
    const potentialSavings = m.total_cost * 0.4; // 40% of potential savings

    // Impact: relative to maximum savings (scale 1-10)
    const impactRaw = (potentialSavings / maxSavings) * 10;
    const impact = Math.max(3, Math.min(10, Math.round(impactRaw)));

    // Feasibility: based on CV and transfer_rate (low variability = high feasibility)
    const feasibilityRaw = 10 - (m.cv_aht * 5) - (m.transfer_rate / 10);
    const feasibility = Math.max(3, Math.min(10, Math.round(feasibilityRaw)));

    // Determine dimension according to characteristics
    let dimensionId: string;
    if (m.cv_aht < 0.3 && m.transfer_rate < 15) {
      dimensionId = 'agentic_readiness'; // Ready to automate
    } else if (m.cv_aht < 0.5) {
      dimensionId = 'effectiveness_resolution'; // Can improve with assistance
    } else {
      dimensionId = 'complexity_predictability'; // Needs optimization
    }

    // Descriptive name
    const prefix = m.cv_aht < 0.3 && m.transfer_rate < 15
      ? 'Automate '
      : m.cv_aht < 0.5
        ? 'Assist with AI in '
        : 'Optimize process in ';

    return {
      id: `opp-${index + 1}`,
      name: `${prefix}${m.skill}`,
      impact,
      feasibility,
      savings: Math.round(potentialSavings),
      dimensionId,
      customer_segment: 'medium' as CustomerSegment
    };
  });
}

/**
 * Generate roadmap from opportunities and skill metrics
 * v3.0: Initiatives connected to real skills with volumeImpacted, kpiObjective, rationale
 */
function generateRoadmapFromRealData(opportunities: Opportunity[], metrics?: SkillMetrics[]): RoadmapInitiative[] {
  // Sort by descending savings to prioritize
  const sortedOpps = [...opportunities].sort((a, b) => (b.savings || 0) - (a.savings || 0));

  // Create map of metrics per skill for quick lookup
  const metricsMap = new Map<string, SkillMetrics>();
  if (metrics) {
    for (const m of metrics) {
      metricsMap.set(m.skill.toLowerCase(), m);
    }
  }

  // Helper para obtain metrics de un skill
  const getSkillMetrics = (skillName: string): SkillMetrics | undefined => {
    return metricsMap.get(skillName.toLowerCase()) ||
           Array.from(metricsMap.values()).find(m =>
             m.skill.toLowerCase().includes(skillName.toLowerCase()) ||
             skillName.toLowerCase().includes(m.skill.toLowerCase())
           );
  };

  const initiatives: RoadmapInitiative[] = [];
  let initCounter = 1;

  // WAVE 1: Automate - Skills with high automation potential
  const wave1Opps = sortedOpps.slice(0, 2);
  for (const opp of wave1Opps) {
    const skillName = opp.name?.replace(/^(Automate |Assist with AI in |Optimize process in )/, '') || `Skill ${initCounter}`;
    const savings = opp.savings || 0;
    const skillMetrics = getSkillMetrics(skillName);
    const volume = skillMetrics?.volume || Math.round(savings / 5);
    const cvAht = skillMetrics?.cv_aht || 50;
    const offHoursPct = skillMetrics?.off_hours_pct || 28;

    // Determine initiative type based on skill characteristics
    const isHighVolume = volume > 100000;
    const hasOffHoursOpportunity = offHoursPct > 25;

    initiatives.push({
      id: `init-${initCounter}`,
      name: hasOffHoursOpportunity
        ? `Chatbot consultas ${skillName} (24/7)`
        : `IVR inteligente ${skillName}`,
      phase: RoadmapPhase.Automate,
      timeline: 'Q1 2026',
      investment: Math.round(savings * 0.3),
      resources: hasOffHoursOpportunity
        ? ['1x Bot Developer', 'API Integration', 'QA Team']
        : ['1x Voice UX Designer', 'Integration Team'],
      dimensionId: 'agentic_readiness',
      risk: 'low',
      skillsImpacted: [skillName],
      volumeImpacted: volume,
      kpiObjective: hasOffHoursOpportunity
        ? `Automate ${Math.round(offHoursPct)}% consultas off hours`
        : `Desviar 25% a self-service para gestiones simples`,
      rationale: hasOffHoursOpportunity
        ? `${Math.round(offHoursPct)}% del volumen ocurre off hours. Chatbot puede resolver consultas de status without agent.`
        : `CV AHT ${Math.round(cvAht)}% indica process variables. IVR puede pre-cualificar y resolver casos simples.`,
      savingsDetail: `Automation ${Math.round(offHoursPct)}% off hours volume`,
      estimatedSavings: savings,
      resourceHours: 440
    });
    initCounter++;
  }

  // WAVE 2: Assist - Knowledge Base + Copilot
  const wave2Opps = sortedOpps.slice(2, 4);

  // Iniciativa 1: Knowledge Base (agrupa varios skills)
  if (wave2Opps.length > 0) {
    const kbSkills = wave2Opps.map(o => o.name?.replace(/^(Automate |Assist with AI in |Optimize process in )/, '') || '');
    const kbSavings = wave2Opps.reduce((sum, o) => sum + (o.savings || 0), 0) * 0.4;
    const kbVolume = wave2Opps.reduce((sum, o) => {
      const m = getSkillMetrics(o.name || '');
      return sum + (m?.volume || 10000);
    }, 0);

    initiatives.push({
      id: `init-${initCounter}`,
      name: 'Dynamic Knowledge Base with AI',
      phase: RoadmapPhase.Assist,
      timeline: 'Q2 2026',
      investment: Math.round(kbSavings * 0.25),
      resources: ['1x PM', 'Content Team', 'AI Developer'],
      dimensionId: 'effectiveness_resolution',
      risk: 'low',
      skillsImpacted: kbSkills.filter(s => s),
      volumeImpacted: kbVolume,
      kpiObjective: 'Reduce Hold Time 30% through real-time suggestions',
      rationale: 'FCR low indicates that agents do not find information quickly. KB with AI suggests contextual responses.',
      savingsDetail: `Hold Time Reduction 30% en ${kbSkills.length} skills`,
      estimatedSavings: Math.round(kbSavings),
      resourceHours: 400
    });
    initCounter++;
  }

  // Iniciativa 2: Copilot para skill principal
  if (wave2Opps.length > 0) {
    const mainOpp = wave2Opps[0];
    const skillName = mainOpp.name?.replace(/^(Automate |Assist with AI in |Optimize process in )/, '') || 'Principal';
    const savings = mainOpp.savings || 0;
    const skillMetrics = getSkillMetrics(skillName);
    const volume = skillMetrics?.volume || Math.round(savings / 5);
    const cvAht = skillMetrics?.cv_aht || 100;

    initiatives.push({
      id: `init-${initCounter}`,
      name: `Copilot para ${skillName}`,
      phase: RoadmapPhase.Assist,
      timeline: 'Q3 2026',
      investment: Math.round(savings * 0.35),
      resources: ['2x AI Developers', 'QA Team', 'Training'],
      dimensionId: 'effectiveness_resolution',
      risk: 'medium',
      skillsImpacted: [skillName],
      volumeImpacted: volume,
      kpiObjective: `Reduce AHT 15% and CV AHT from ${Math.round(cvAht)}% a <80%`,
      rationale: `Skill con high volumen y variabilidad. Copilot puede pre-llenar formularios, sugerir respuestas y guiar al agent.`,
      savingsDetail: `AHT Reduction 15% + FCR improvement 10%`,
      estimatedSavings: savings,
      resourceHours: 600
    });
    initCounter++;
  }

  // WAVE 3: Augment - Standardization and extended coverage
  const wave3Opps = sortedOpps.slice(4, 6);

  // Iniciativa 1: Standardization (skill con mayor CV)
  if (wave3Opps.length > 0) {
    const highCvOpp = wave3Opps.reduce((max, o) => {
      const m = getSkillMetrics(o.name || '');
      const maxM = getSkillMetrics(max.name || '');
      return (m?.cv_aht || 0) > (maxM?.cv_aht || 0) ? o : max;
    }, wave3Opps[0]);

    const skillName = highCvOpp.name?.replace(/^(Automate |Assist with AI in |Optimize process in )/, '') || 'Variable';
    const savings = highCvOpp.savings || 0;
    const skillMetrics = getSkillMetrics(skillName);
    const volume = skillMetrics?.volume || Math.round(savings / 5);
    const cvAht = skillMetrics?.cv_aht || 150;

    initiatives.push({
      id: `init-${initCounter}`,
      name: `Process Standardization ${skillName}`,
      phase: RoadmapPhase.Augment,
      timeline: 'Q4 2026',
      investment: Math.round(savings * 0.4),
      resources: ['Process Analyst', 'Training Team', 'QA'],
      dimensionId: 'complexity_predictability',
      risk: 'medium',
      skillsImpacted: [skillName],
      volumeImpacted: volume,
      kpiObjective: `Reduce CV AHT from ${Math.round(cvAht)}% a <100%`,
      rationale: `CV AHT ${Math.round(cvAht)}% indicates non-standardized processes. Requires redesign and documentation before automation.`,
      savingsDetail: `Standardization reduces variability and enables future automation`,
      estimatedSavings: savings,
      resourceHours: 440
    });
    initCounter++;
  }

  // Iniciativa 2: Cobertura nocturna (si hay off hours volume)
  const totalOffHoursVolume = metrics?.reduce((sum, m) => sum + (m.volume * (m.off_hours_pct || 0) / 100), 0) || 0;
  if (totalOffHoursVolume > 10000 && wave3Opps.length > 1) {
    const offHoursSkills = metrics?.filter(m => (m.off_hours_pct || 0) > 20).map(m => m.skill).slice(0, 3) || [];
    const offHoursSavings = totalOffHoursVolume * 5 * 0.6; // CPI €5, 60% automatizable

    initiatives.push({
      id: `init-${initCounter}`,
      name: 'Cobertura nocturna con agents virtuales',
      phase: RoadmapPhase.Augment,
      timeline: 'Q1 2027',
      investment: Math.round(offHoursSavings * 0.5),
      resources: ['Lead AI Engineer', 'Data Scientist', 'QA Team'],
      dimensionId: 'agentic_readiness',
      risk: 'high',
      skillsImpacted: offHoursSkills.length > 0 ? offHoursSkills : ['Customer Service', 'Support'],
      volumeImpacted: Math.round(totalOffHoursVolume),
      kpiObjective: '24/7 Coverage with 60% automatic overnight resolution',
      rationale: `${Math.round(totalOffHoursVolume).toLocaleString()} interactions off hours. Agente virtual puede resolver consultas y programar callbacks.`,
      savingsDetail: `Cobertura 24/7 without incremento plantilla nocturna`,
      estimatedSavings: Math.round(offHoursSavings),
      resourceHours: 600
    });
  }

  return initiatives;
}

/**
 * v3.10: Generar economic model from datos reales
 * ALINEADO CON ROADMAP: Usa modelo TCO con CPI por tier
 * - AUTOMATE: 70% × (€2.33 - €0.15) = €1.526/interaction
 * - ASSIST: 30% × (€2.33 - €1.50) = €0.249/interaction
 * - AUGMENT: 15% × (€2.33 - €2.00) = €0.050/interaction
 */
function generateEconomicModelFromRealData(
  metrics: SkillMetrics[],
  costPerHour: number,
  roadmap?: RoadmapInitiative[],
  drilldownData?: DrilldownDataPoint[]
): EconomicModelData {
  const totalCost = metrics.reduce((sum, m) => sum + m.total_cost, 0);

  // v3.10: Calcular ahorro usando modelo TCO alineado con Roadmap
  const CPI_HUMANO = 2.33;
  const CPI_BOT = 0.15;
  const CPI_ASSIST = 1.50;
  const CPI_AUGMENT = 2.00;

  // Containment/deflection rates by tier
  const RATE_AUTOMATE = 0.70;
  const RATE_ASSIST = 0.30;
  const RATE_AUGMENT = 0.15;

  let annualSavingsTCO = 0;
  let volumeByTier = { AUTOMATE: 0, ASSIST: 0, AUGMENT: 0, 'HUMAN-ONLY': 0 };

  // Si tenemos drilldownData, calculate ahorro por tier real
  if (drilldownData && drilldownData.length > 0) {
    drilldownData.forEach(skill => {
      skill.originalQueues.forEach(queue => {
        volumeByTier[queue.tier] += queue.volume;
      });
    });

    // Annual savings = Volume × 12 months × Rate × CPI Differential
    const savingsAUTOMATE = volumeByTier.AUTOMATE * 12 * RATE_AUTOMATE * (CPI_HUMANO - CPI_BOT);
    const savingsASSIST = volumeByTier.ASSIST * 12 * RATE_ASSIST * (CPI_HUMANO - CPI_ASSIST);
    const savingsAUGMENT = volumeByTier.AUGMENT * 12 * RATE_AUGMENT * (CPI_HUMANO - CPI_AUGMENT);

    annualSavingsTCO = Math.round(savingsAUTOMATE + savingsASSIST + savingsAUGMENT);
  } else {
    // Fallback: estimar 35% del total cost (legacy)
    annualSavingsTCO = Math.round(totalCost * 0.35);
  }

  // Initial investment: from aligned Roadmap
  // Wave 1: €47K, Wave 2: €35K, Wave 3: €70K, Wave 4: €85K = €237K total
  let initialInvestment: number;
  if (roadmap && roadmap.length > 0) {
    initialInvestment = roadmap.reduce((sum, init) => sum + (init.investment || 0), 0);
  } else {
    // Default: Escenario conservative Wave 1-2
    initialInvestment = 82000; // €47K + €35K
  }

  // Costes recurrentes annuales (alineado con Roadmap)
  // Wave 2: €40K, Wave 3: €78K, Wave 4: €108K
  const recurrentCostAnnual = drilldownData && drilldownData.length > 0
    ? Math.round(initialInvestment * 0.5) // 50% of investment as recurring
    : Math.round(initialInvestment * 0.15);

  // Margen neto annual (ahorro - recurrente)
  const netAnnualSavings = annualSavingsTCO - recurrentCostAnnual;

  // Payback: Implementation + Recovery (aligned with Roadmap v3.9)
  const monthsImplementacion = 9; // Wave 1 (6m) + mitad Wave 2 (3m/2)
  const margenMensual = netAnnualSavings / 12;
  const monthsRecuperacion = margenMensual > 0 ? Math.ceil(initialInvestment / margenMensual) : -1;
  const paybackMonths = margenMensual > 0 ? monthsImplementacion + monthsRecuperacion : -1;

  // ROI 3 years: ((Savings×3) - (Investment + Recurring×3)) / (Investment + Recurring×3) × 100
  const costeTotalTresAnos = initialInvestment + (recurrentCostAnnual * 3);
  const ahorroTotalTresAnos = annualSavingsTCO * 3;
  const roi3yr = costeTotalTresAnos > 0
    ? ((ahorroTotalTresAnos - costeTotalTresAnos) / costeTotalTresAnos) * 100
    : 0;

  // NPV con tasa de descuento 10%
  const discountRate = 0.10;
  const npv = -initialInvestment +
              (netAnnualSavings / (1 + discountRate)) +
              (netAnnualSavings / Math.pow(1 + discountRate, 2)) +
              (netAnnualSavings / Math.pow(1 + discountRate, 3));

  // Desglose de ahorro por tier (alineado con TCO)
  const savingsBreakdown: { category: string; amount: number; percentage: number }[] = [];

  if (drilldownData && drilldownData.length > 0) {
    const savingsAUTOMATE = Math.round(volumeByTier.AUTOMATE * 12 * RATE_AUTOMATE * (CPI_HUMANO - CPI_BOT));
    const savingsASSIST = Math.round(volumeByTier.ASSIST * 12 * RATE_ASSIST * (CPI_HUMANO - CPI_ASSIST));
    const savingsAUGMENT = Math.round(volumeByTier.AUGMENT * 12 * RATE_AUGMENT * (CPI_HUMANO - CPI_AUGMENT));
    const totalSav = savingsAUTOMATE + savingsASSIST + savingsAUGMENT || 1;

    if (savingsAUTOMATE > 0) {
      savingsBreakdown.push({
        category: `AUTOMATE (${volumeByTier.AUTOMATE.toLocaleString()} int/month)`,
        amount: savingsAUTOMATE,
        percentage: Math.round((savingsAUTOMATE / totalSav) * 100)
      });
    }
    if (savingsASSIST > 0) {
      savingsBreakdown.push({
        category: `ASSIST (${volumeByTier.ASSIST.toLocaleString()} int/month)`,
        amount: savingsASSIST,
        percentage: Math.round((savingsASSIST / totalSav) * 100)
      });
    }
    if (savingsAUGMENT > 0) {
      savingsBreakdown.push({
        category: `AUGMENT (${volumeByTier.AUGMENT.toLocaleString()} int/month)`,
        amount: savingsAUGMENT,
        percentage: Math.round((savingsAUGMENT / totalSav) * 100)
      });
    }
  } else {
    // Fallback legacy
    const topSkills = metrics.slice(0, 4);
    topSkills.forEach((skill, idx) => {
      const skillSavings = Math.round(skill.total_cost * 0.4);
      savingsBreakdown.push({
        category: `AHT Reduction 15% ${skill.skill}`,
        amount: skillSavings,
        percentage: Math.round((skillSavings / (annualSavingsTCO || 1)) * 100)
      });
    });
  }

  const costBreakdown = [
    { category: 'Software y licencias', amount: Math.round(initialInvestment * 0.40), percentage: 40 },
    { category: 'Development and implementation', amount: Math.round(initialInvestment * 0.30), percentage: 30 },
    { category: 'Training y change mgmt', amount: Math.round(initialInvestment * 0.20), percentage: 20 },
    { category: 'Contingencia', amount: Math.round(initialInvestment * 0.10), percentage: 10 },
  ];

  return {
    currentAnnualCost: Math.round(totalCost),
    futureAnnualCost: Math.round(totalCost - netAnnualSavings),
    annualSavings: annualSavingsTCO, // Ahorro bruto TCO (para comparar con Roadmap)
    initialInvestment,
    paybackMonths: paybackMonths > 0 ? paybackMonths : 0,
    roi3yr: parseFloat(roi3yr.toFixed(1)),
    npv: Math.round(npv),
    savingsBreakdown,
    costBreakdown
  };
}

/**
 * Generar benchmark from datos reales
 * AIRLINE SECTOR BENCHMARKS: AHT P50=380s, FCR=70%, Abandono=5%, Ratio P90/P50<2.0
 */
function generateBenchmarkFromRealData(metrics: SkillMetrics[]): BenchmarkDataPoint[] {
  const avgAHT = metrics.reduce((sum, m) => sum + m.aht_mean, 0) / (metrics.length || 1);
  const avgCV = metrics.reduce((sum, m) => sum + m.cv_aht, 0) / (metrics.length || 1);
  const avgRatio = 1 + avgCV * 1.5; // Ratio P90/P50 aproximado

  // Technical FCR: 100 - transfer_rate (weighted by volume)
  const totalVolume = metrics.reduce((sum, m) => sum + m.volume_valid, 0);
  const avgFCR = totalVolume > 0
    ? metrics.reduce((sum, m) => sum + (m.fcr_tecnico * m.volume_valid), 0) / totalVolume
    : 0;

  // Abandono real
  const totalInteractions = metrics.reduce((sum, m) => sum + m.volume, 0);
  const totalAbandoned = metrics.reduce((sum, m) => sum + m.abandon_count, 0);
  const abandonRate = totalInteractions > 0 ? (totalAbandoned / totalInteractions) * 100 : 0;

  // CPI: Coste total / Total interactions
  const totalCost = metrics.reduce((sum, m) => sum + m.total_cost, 0);
  const avgCPI = totalInteractions > 0 ? totalCost / totalInteractions : 3.5;

  // Calculate percentiles based on benchmarks airline sector
  const ahtPercentile = avgAHT <= 380 ? 75 : avgAHT <= 420 ? 60 : avgAHT <= 480 ? 40 : 25;
  const fcrPercentile = avgFCR >= 70 ? 70 : avgFCR >= 60 ? 50 : avgFCR >= 50 ? 35 : 20;
  const abandonPercentile = abandonRate <= 5 ? 75 : abandonRate <= 8 ? 55 : abandonRate <= 12 ? 35 : 20;
  const ratioPercentile = avgRatio <= 2.0 ? 75 : avgRatio <= 2.5 ? 50 : avgRatio <= 3.0 ? 30 : 15;

  return [
    {
      kpi: 'AHT P50',
      userValue: Math.round(avgAHT),
      userDisplay: `${Math.round(avgAHT)}s`,
      industryValue: 380,
      industryDisplay: '380s',
      percentile: ahtPercentile,
      p25: 320,
      p50: 380,
      p75: 450,
      p90: 520
    },
    {
      kpi: 'FCR',
      userValue: avgFCR,
      userDisplay: `${Math.round(avgFCR)}%`,
      industryValue: 70,
      industryDisplay: '70%',
      percentile: fcrPercentile,
      p25: 55,
      p50: 70,
      p75: 80,
      p90: 88
    },
    {
      kpi: 'Abandono',
      userValue: abandonRate,
      userDisplay: `${abandonRate.toFixed(1)}%`,
      industryValue: 5,
      industryDisplay: '5%',
      percentile: abandonPercentile,
      p25: 8,
      p50: 5,
      p75: 3,
      p90: 2
    },
    {
      kpi: 'Ratio P90/P50',
      userValue: avgRatio,
      userDisplay: avgRatio.toFixed(2),
      industryValue: 2.0,
      industryDisplay: '<2.0',
      percentile: ratioPercentile,
      p25: 2.5,
      p50: 2.0,
      p75: 1.7,
      p90: 1.4
    },
    {
      kpi: 'Cost/Interaction',
      userValue: avgCPI,
      userDisplay: `€${avgCPI.toFixed(2)}`,
      industryValue: 3.5,
      industryDisplay: '€3.50',
      percentile: avgCPI <= 3.5 ? 65 : avgCPI <= 4.5 ? 45 : 25,
      p25: 4.5,
      p50: 3.5,
      p75: 2.8,
      p90: 2.2
    }
  ];
}
