import type { EnrichedUserStory } from '@/stores/workitems'
import type { BlockerRecord } from '@/services/blocker-log'

export interface CurvePoint {
  date: string      // ISO date YYYY-MM-DD
  cumulative: number // cumulative SP
}

export interface SCurveData {
  planned: CurvePoint[]
  actual: CurvePoint[]
  projection: CurvePoint[]
  metrics: SCurveMetrics
}

export interface SCurveMetrics {
  totalSpPlanned: number
  totalSpCompleted: number
  velocityPerWeek: number   // average SP completed per week
  weeksElapsed: number
  weeksRemaining: number     // estimated weeks to completion
  estimatedEndDate: string   // projected completion date
  spi: number                // Schedule Performance Index
  clientBlockedHours: number
  totalBlockedHours: number
}

const WORKING_HOURS_PER_DAY = 8
const WORKING_DAYS_PER_WEEK = 5
const SP_PER_WEEK = WORKING_HOURS_PER_DAY * WORKING_DAYS_PER_WEEK // 40

/**
 * Calcula la curva planificada: distribuye los SP de cada HU
 * linealmente entre su start_date y end_date.
 */
export function calculatePlannedCurve(hus: EnrichedUserStory[]): CurvePoint[] {
  const points: { date: string; sp: number }[] = []

  for (const hu of hus) {
    const sp = hu.story_points || 0
    if (sp <= 0) continue

    // Si no tiene fechas, asumimos que empieza hoy y dura 1 día por SP
    const start = hu.initial_date || new Date().toISOString().split('T')[0]
    const end = hu.end_date || calcEndDate(start, sp)

    // Distribuir SP linealmente entre start y end
    const startD = new Date(start)
    const endD = new Date(end)
    const totalDays = Math.max(1, Math.ceil((endD.getTime() - startD.getTime()) / (1000 * 60 * 60 * 24)) + 1)
    const spPerDay = sp / totalDays

    for (let i = 0; i < totalDays; i++) {
      const d = new Date(startD)
      d.setDate(d.getDate() + i)
      // Skip weekends
      if (d.getDay() === 0 || d.getDay() === 6) continue
      const dateStr = d.toISOString().split('T')[0]
      points.push({ date: dateStr, sp: spPerDay })
    }
  }

  // Acumular por fecha
  return accumulatePoints(points)
}

/**
 * Calcula la curva real desde daily logs (datos de progreso real).
 * Por ahora, usa el end_date + status para estimar progreso.
 */
export function calculateActualCurve(hus: EnrichedUserStory[]): CurvePoint[] {
  const points: { date: string; sp: number }[] = []

  for (const hu of hus) {
    const sp = hu.story_points || 0
    if (sp <= 0) continue

    const s = String(hu.status || '').toLowerCase()
    const isDone = ['done', 'completed', 'closed', 'finalizado', '5', '6', '7', 'qa-client', 'ready to deploy'].includes(s)
    const isInProgress = ['in_progress', 'doing', 'wip', 'active', 'in progress', 'en progreso', 'true', '2'].includes(s)

    if (isDone && hu.end_date) {
      points.push({ date: hu.end_date, sp })
    } else if (isInProgress && hu.end_date) {
      // 50% completado (estimado)
      const halfDate = hu.initial_date
        ? midpointDate(hu.initial_date, hu.end_date)
        : hu.end_date
      points.push({ date: halfDate, sp: Math.round(sp * 0.5) })
    }
  }

  return accumulatePoints(points)
}

/**
 * Calcula la proyección: extiende la curva real usando la velocidad actual.
 */
export function calculateProjection(
  actual: CurvePoint[],
  planned: CurvePoint[],
  totalSpPlanned: number,
  totalSpCompleted: number,
): { projection: CurvePoint[]; estimatedEndDate: string; weeksRemaining: number } {
  if (actual.length === 0) {
    return {
      projection: [],
      estimatedEndDate: planned[planned.length - 1]?.date || '',
      weeksRemaining: 0,
    }
  }

  // Calcular velocidad semanal
  const firstActual = new Date(actual[0].date)
  const lastActual = new Date(actual[actual.length - 1].date)
  const daysElapsed = Math.max(1, Math.ceil((lastActual.getTime() - firstActual.getTime()) / (1000 * 60 * 60 * 24)))
  const weeksElapsed = Math.max(1, daysElapsed / WORKING_DAYS_PER_WEEK)
  const velocityPerWeek = totalSpCompleted / weeksElapsed

  if (velocityPerWeek <= 0) {
    return { projection: [], estimatedEndDate: '', weeksRemaining: 0 }
  }

  const remainingSp = totalSpPlanned - totalSpCompleted
  const weeksRemaining = Math.ceil(remainingSp / velocityPerWeek)
  const estimatedEnd = new Date(lastActual)
  estimatedEnd.setDate(estimatedEnd.getDate() + weeksRemaining * WORKING_DAYS_PER_WEEK)

  // Generar puntos de proyección
  const projection: CurvePoint[] = []
  const lastCumulative = actual[actual.length - 1]?.cumulative || 0
  let projectedCumulative = lastCumulative
  const projDate = new Date(lastActual)

  for (let w = 0; w <= weeksRemaining; w++) {
    projectedCumulative += velocityPerWeek * WORKING_DAYS_PER_WEEK / 7
    if (projectedCumulative > totalSpPlanned) projectedCumulative = totalSpPlanned
    projDate.setDate(projDate.getDate() + 7)
    projection.push({
      date: projDate.toISOString().split('T')[0],
      cumulative: Math.round(projectedCumulative),
    })
  }

  return {
    projection,
    estimatedEndDate: estimatedEnd.toISOString().split('T')[0],
    weeksRemaining,
  }
}

/**
 * Calcula todas las métricas de la curva S.
 */
export function calculateSCurve(hus: EnrichedUserStory[], blockers: BlockerRecord[]): SCurveData {
  const planned = calculatePlannedCurve(hus)
  const actual = calculateActualCurve(hus)

  const totalSpPlanned = hus.reduce((s, h) => s + (h.story_points || 0), 0)
  const totalSpCompleted = actual.length > 0 ? actual[actual.length - 1].cumulative : 0

  const firstActual = actual.length > 0 ? new Date(actual[0].date) : new Date()
  const lastActual = actual.length > 0 ? new Date(actual[actual.length - 1].date) : new Date()
  const daysElapsed = Math.max(1, Math.ceil((lastActual.getTime() - firstActual.getTime()) / (1000 * 60 * 60 * 24)))
  const weeksElapsed = Math.max(0.5, daysElapsed / WORKING_DAYS_PER_WEEK)
  const velocityPerWeek = weeksElapsed > 0 ? +(totalSpCompleted / weeksElapsed).toFixed(1) : 0

  // SPI = SP completados / SP planeados en el mismo periodo
  const plannedAtEnd = planned.filter(p => new Date(p.date) <= lastActual)
  const plannedInPeriod = plannedAtEnd.length > 0 ? plannedAtEnd[plannedAtEnd.length - 1].cumulative : 0
  const spi = plannedInPeriod > 0 ? +(totalSpCompleted / plannedInPeriod).toFixed(2) : 1

  const { estimatedEndDate, weeksRemaining } = calculateProjection(actual, planned, totalSpPlanned, totalSpCompleted)

  // Estadísticas de bloqueos
  const clientBlockedHours = blockers
    .filter(b => b.category === 'client')
    .reduce((s, b) => s + (b.timeLostHours || 0), 0)
  const totalBlockedHours = blockers.reduce((s, b) => s + (b.timeLostHours || 0), 0)

  return {
    planned,
    actual,
    projection: [],
    metrics: {
      totalSpPlanned,
      totalSpCompleted,
      velocityPerWeek,
      weeksElapsed,
      weeksRemaining,
      estimatedEndDate,
      spi,
      clientBlockedHours,
      totalBlockedHours,
    },
  }
}

// ─── Helpers ────────────────────────────────────────

function accumulatePoints(points: { date: string; sp: number }[]): CurvePoint[] {
  if (points.length === 0) return []

  const sorted = [...points].sort((a, b) => a.date.localeCompare(b.date))
  const result: CurvePoint[] = []
  let cumulative = 0
  let currentDate = sorted[0].date
  let daySum = 0

  for (const p of sorted) {
    if (p.date !== currentDate) {
      cumulative += Math.round(daySum)
      result.push({ date: currentDate, cumulative })
      currentDate = p.date
      daySum = 0
    }
    daySum += p.sp
  }
  // Último día
  cumulative += Math.round(daySum)
  result.push({ date: currentDate, cumulative })

  return result
}

function calcEndDate(start: string, sp: number): string {
  const d = new Date(start)
  d.setDate(d.getDate() + sp)
  return d.toISOString().split('T')[0]
}

function midpointDate(start: string, end: string): string {
  const s = new Date(start)
  const e = new Date(end)
  const mid = new Date((s.getTime() + e.getTime()) / 2)
  return mid.toISOString().split('T')[0]
}
