import { formatCurrency, formatPayback, formatPercent, formatSimulationTimestamp } from '@/lib/format'
import type { ActivitySimResult, Process, Activity } from '@/domain/types'
import type { RoiResult } from '@/domain/roi'
import { PDF, type DocLike } from './pdf-sections'
import { PDF_COLORS } from './pdf-sections-shared'

// ─── Colores locales de página 1 ROI ─────────────────────────────────────────

const C = {
  heroOrange:    [245, 152, 69]  as [number, number, number],  // #F59845
  heroMagenta:   [237, 62, 143]  as [number, number, number],  // #ED3E8F
  orangeLight:   [254, 243, 226] as [number, number, number],  // #FEF3E2
  orangeLighter: [255, 248, 237] as [number, number, number],  // #FFF8ED
  orangeBar:     [254, 215, 170] as [number, number, number],  // #FED7AA
  metaBg:        [250, 251, 252] as [number, number, number],  // #FAFBFC
  orangeDark:    [180, 83, 9]    as [number, number, number],  // #B45309
  orangeDarker:  [146, 64, 14]   as [number, number, number],  // #92400E
  white:         [255, 255, 255] as [number, number, number],
}

// ─── Página 1 ROI — diseño completo (Etapa 6) ────────────────────────────────

export function drawRoiPage1(
  doc: DocLike,
  process: Process,
  roi: RoiResult,
  simulatedAt: number,
  perActivityActual: ActivitySimResult[],
  perActivityAutomated: ActivitySimResult[],
  currency: string
): void {
  const pageW = doc.internal.pageSize.getWidth()
  const m = 20                    // margin mm
  const cW = pageW - 2 * m       // content width

  let y = m
  y = _drawDenseHeader(doc, pageW, m, cW, y)
  y = _drawMetadataStrip(doc, process, simulatedAt, m, cW, y)
  y = _drawNarrativeBanner(doc, process, roi, currency, m, cW, y)
  y = _drawHeroCard(doc, roi, process, currency, m, cW, y)
  y = _drawSecondaryGrid(doc, roi, process, currency, m, cW, y)
  _drawTop3Activities(doc, perActivityActual, perActivityAutomated, currency, m, cW, y)
}

// ── 5.1 Header denso de página 1 ─────────────────────────────────────────────

function _drawDenseHeader(
  doc: DocLike, pageW: number, m: number, cW: number, y: number
): number {
  const right = pageW - m

  // Izquierda: "InQ ROI" 22pt bold naranja
  doc.setFont('helvetica', 'bold')
  doc.setFontSize(22)
  doc.setTextColor(...C.heroOrange)
  doc.text('InQ ROI', m, y)

  // Derecha: "UN PRODUCTO DE" 9pt slate-400
  doc.setFont('helvetica', 'normal')
  doc.setFontSize(9)
  doc.setTextColor(...PDF_COLORS.textDisabled)
  doc.text('UN PRODUCTO DE', right, y, { align: 'right' } as Record<string, unknown>)
  y += 9

  // Izquierda: subtítulo 9pt slate-400 (margin-top 4mm ya incluido en line-height)
  doc.setFont('helvetica', 'normal')
  doc.setFontSize(9)
  doc.setTextColor(...PDF_COLORS.textDisabled)
  doc.text('ANÁLISIS DE RETORNO DE INVERSIÓN', m, y)

  // Derecha: "InQ" 16pt bold slate-600
  doc.setFont('helvetica', 'bold')
  doc.setFontSize(16)
  doc.setTextColor(...PDF_COLORS.textSecondary)
  doc.text('InQ', right, y, { align: 'right' } as Record<string, unknown>)
  y += 6

  // Derecha: "InQuality" 9pt slate-500
  doc.setFont('helvetica', 'normal')
  doc.setFontSize(9)
  doc.setTextColor(...PDF_COLORS.textTertiary)
  doc.text('InQuality', right, y, { align: 'right' } as Record<string, unknown>)
  y += 10

  // Border-bottom naranja 0.8mm
  doc.setDrawColor(...C.heroOrange)
  doc.setLineWidth(0.8)
  doc.line(m, y, m + cW, y)
  doc.setLineWidth(0.2)
  y += 10  // margin-bottom below border

  return y
}

// ── 5.2 Strip de metadata ─────────────────────────────────────────────────────

function _drawMetadataStrip(
  doc: DocLike, process: Process, simulatedAt: number,
  m: number, cW: number, y: number
): number {
  const h = 16, r = 2
  doc.setFillColor(...C.metaBg)
  doc.roundedRect(m, y, cW, h, r, r, 'F')

  const { date } = formatSimulationTimestamp(simulatedAt)
  const col2 = m + cW * 0.5
  const col3 = m + cW * 0.75

  const cells = [
    { label: 'PROCESO', value: process.name, x: m + 4 },
    { label: 'CLIENTE', value: process.clientName || '—', x: col2 + 4 },
    { label: 'FECHA', value: date, x: col3 + 4 },
  ]

  for (const cell of cells) {
    doc.setFont('helvetica', 'normal')
    doc.setFontSize(7)
    doc.setTextColor(...PDF_COLORS.textDisabled)
    doc.text(cell.label, cell.x, y + 5)

    doc.setFont('helvetica', 'bold')
    doc.setFontSize(9)
    doc.setTextColor(...PDF_COLORS.textPrimary)
    const truncated = cell.value.length > 28 ? cell.value.slice(0, 26) + '…' : cell.value
    doc.text(truncated, cell.x, y + 12)
  }

  return y + h + 6
}

// ── 5.3 Banner narrativo ──────────────────────────────────────────────────────

function _drawNarrativeBanner(
  doc: DocLike, process: Process, roi: RoiResult, currency: string,
  m: number, cW: number, y: number
): number {
  const padH = 5, padV = 8, borderW = 3

  const investStr = process.automationInvestment > 0
    ? `inversión estimada de ${formatCurrency(process.automationInvestment, currency)}`
    : 'inversión aún no configurada'

  const line1 = `Análisis de retorno de inversión para automatización del proceso` +
    (process.clientName ? ` del cliente "${process.clientName}"` : '') +
    `, con frecuencia anual de ${process.annualFrequency.toLocaleString('es-PY')} ejecuciones,` +
    ` horizonte de ${process.analysisHorizonYears} años e ${investStr}.`

  const resultStr = roi.savingsPerExecution > 0
    ? `Resultado: ahorro de ${formatCurrency(roi.savingsPerExecution, currency)} por ejecución` +
      ` (${formatPercent(roi.savingsPerExecutionPercent)} de reducción).` +
      (Number.isFinite(roi.paybackMonths)
        ? ` La inversión se recupera en ${formatPayback(roi.paybackMonths)}, ` +
          (roi.breaksEvenInHorizon ? 'dentro del horizonte de análisis.' : 'fuera del horizonte analizado.')
        : ' Sin ahorro neto con los datos actuales.')
    : 'El escenario automatizado no genera ahorro con los datos actuales.'

  doc.setFontSize(9)
  const l1 = doc.splitTextToSize(line1, cW - padH * 2 - borderW - 4)
  const l2 = doc.splitTextToSize(resultStr, cW - padH * 2 - borderW - 4)
  const totalLines = l1.length + 1 + l2.length
  const textH = totalLines * 4.2
  const boxH = textH + padV * 2

  // Fondo naranja ligero
  doc.setFillColor(...C.orangeLighter)
  doc.setDrawColor(...C.orangeLighter)
  doc.rect(m + borderW, y, cW - borderW, boxH, 'F')

  // Border-left naranja
  doc.setFillColor(...C.heroOrange)
  doc.rect(m, y, borderW, boxH, 'F')

  const tx = m + borderW + padH
  let ty = y + padV

  // Contexto (línea 1): peso 400, gris — información de fondo
  doc.setFont('helvetica', 'normal')
  doc.setFontSize(9)
  doc.setTextColor(...PDF_COLORS.textSecondary)
  doc.text(l1, tx, ty)
  ty += l1.length * 4.2 + 4

  // Resultado (línea 2): peso 700, naranja oscuro — cifras y conclusión destacadas
  // Refinamiento: contraste visual entre contexto y resultado
  doc.setFont('helvetica', 'bold')
  doc.setFontSize(9)
  doc.setTextColor(...C.orangeDark)
  doc.text(l2, tx, ty)

  return y + boxH + 8
}

// ── 5.4 KPI Hero con gradiente ────────────────────────────────────────────────

function _createGradientDataUrl(wMm: number, hMm: number, radiusMm = 0): string | null {
  if (typeof document === 'undefined') return null
  try {
    const scale = 3
    const w = Math.round(wMm * scale)
    const h = Math.round(hMm * scale)
    const r = Math.round(radiusMm * scale)
    const canvas = document.createElement('canvas')
    canvas.width = w
    canvas.height = h
    const ctx = canvas.getContext('2d')
    if (!ctx) return null

    // Fondo blanco opaco — las esquinas fuera del clip quedan blancas,
    // lo que se ve igual que bordes redondeados sobre página blanca sin
    // depender de la transparencia PNG en jsPDF.
    ctx.fillStyle = '#FFFFFF'
    ctx.fillRect(0, 0, w, h)

    // Clip al rounded rect antes de pintar el gradiente
    if (r > 0) {
      ctx.beginPath()
      ctx.moveTo(r, 0)
      ctx.lineTo(w - r, 0)
      ctx.arcTo(w, 0, w, r, r)
      ctx.lineTo(w, h - r)
      ctx.arcTo(w, h, w - r, h, r)
      ctx.lineTo(r, h)
      ctx.arcTo(0, h, 0, h - r, r)
      ctx.lineTo(0, r)
      ctx.arcTo(0, 0, r, 0, r)
      ctx.closePath()
      ctx.clip()
    }

    const grad = ctx.createLinearGradient(0, 0, w, h) // 135deg approx
    grad.addColorStop(0, '#F59845')
    grad.addColorStop(1, '#ED3E8F')
    ctx.fillStyle = grad
    ctx.fillRect(0, 0, w, h)
    return canvas.toDataURL('image/png')
  } catch {
    return null
  }
}

function _drawGradientFallback(
  doc: DocLike, x: number, y: number, w: number, h: number
): void {
  const steps = 50
  const segW = w / steps
  for (let i = 0; i < steps; i++) {
    const t = i / (steps - 1)
    const r = Math.round(245 + (237 - 245) * t)
    const g = Math.round(152 + (62 - 152) * t)
    const b = Math.round(69 + (143 - 69) * t)
    doc.setFillColor(r, g, b)
    doc.rect(x + i * segW, y, segW + 0.1, h, 'F')
  }
}

function _drawHeroCard(
  doc: DocLike, roi: RoiResult, process: Process, currency: string,
  m: number, cW: number, y: number
): number {
  const heroH = 46

  // Gradiente con esquinas redondeadas baked en el PNG via canvas clip
  const dataUrl = _createGradientDataUrl(cW, heroH, 4)
  if (dataUrl) {
    doc.addImage(dataUrl, 'PNG', m, y, cW, heroH)
  } else {
    _drawGradientFallback(doc, m, y, cW, heroH)
  }

  // Texto sobre el gradiente
  const heroX = m + 14
  let ty = y + 11

  // Label superior
  doc.setFont('helvetica', 'bold')
  doc.setFontSize(9)
  doc.setTextColor(255, 255, 255)
  doc.text(`AHORRO PROYECTADO A ${process.analysisHorizonYears} AÑOS`, heroX, ty)
  ty += 11  // Refinamiento: más espacio label→cifra (era 8mm)

  // Cifra principal — 26pt (Refinamiento: bajada de 28pt para mejor balance)
  doc.setFont('helvetica', 'bold')
  doc.setFontSize(26)
  doc.setTextColor(...C.white)
  doc.text(formatCurrency(roi.cumulativeSavingsHorizon, currency), heroX, ty)
  ty += 9

  // Subtítulo
  const multiple = process.automationInvestment > 0 && Number.isFinite(roi.roiAnnualPercent)
    ? `neto de inversión · ${(roi.cumulativeSavingsHorizon / process.automationInvestment + 1).toFixed(1)}× retorno`
    : 'neto de inversión'
  doc.setFont('helvetica', 'normal')
  doc.setFontSize(9)
  doc.setTextColor(255, 255, 255)
  doc.text(multiple, heroX, ty)

  return y + heroH + 8
}

// ── 5.5 Grid 2x2 KPIs secundarios ────────────────────────────────────────────

function _drawSecondaryGrid(
  doc: DocLike, roi: RoiResult, process: Process, currency: string,
  m: number, cW: number, y: number
): number {
  // Refinamiento: cardH aumentado de 24→28 para acomodar tipografía uniforme (9/18/9pt)
  const cardW = (cW - 4) / 2
  const cardH = 28
  const gapX = 4, gapY = 4
  const r = 2

  const paybackStr = formatPayback(roi.paybackMonths)
  const roiStr = !Number.isFinite(roi.roiAnnualPercent) ? 'sin inv.' : `${roi.roiAnnualPercent.toFixed(0)}%`
  const savingsPct = formatPercent(roi.savingsPerExecutionPercent)

  const cards = [
    {
      label: 'AHORRO POR EJECUCIÓN',
      value: formatCurrency(roi.savingsPerExecution, currency),
      delta: `-${savingsPct} vs. actual`,
      col: 0, row: 0,
    },
    {
      label: 'AHORRO ANUAL',
      value: formatCurrency(roi.annualSavings, currency),
      delta: `x ${process.analysisHorizonYears} años`,
      col: 1, row: 0,
    },
    {
      label: 'PAYBACK',
      value: paybackStr,
      delta: roi.breaksEvenInHorizon ? 'Dentro del horizonte' : 'Fuera del horizonte',
      col: 0, row: 1,
    },
    {
      label: 'ROI ANUAL',
      value: roiStr,
      delta: 'retorno anual',
      col: 1, row: 1,
    },
  ]

  let maxY = y
  for (const card of cards) {
    const cx = m + card.col * (cardW + gapX)
    const cy = y + card.row * (cardH + gapY)

    doc.setFillColor(...C.orangeLight)
    doc.roundedRect(cx, cy, cardW, cardH, r, r, 'F')

    // Label: 9pt uniforme en los 4 cards (Refinamiento: era 6.5pt variable)
    doc.setFont('helvetica', 'bold')
    doc.setFontSize(9)
    doc.setTextColor(...C.orangeDarker)
    doc.text(card.label, cx + 5, cy + 7)

    // Valor: 18pt fijo en los 4 cards (Refinamiento: era variable 9-12pt)
    doc.setFont('helvetica', 'bold')
    doc.setFontSize(18)
    doc.setTextColor(...PDF_COLORS.textPrimary)
    doc.text(card.value, cx + 5, cy + 17)

    // Delta: 9pt uniforme en los 4 cards (Refinamiento: era 7.5pt)
    doc.setFont('helvetica', 'normal')
    doc.setFontSize(9)
    doc.setTextColor(...C.orangeDark)
    doc.text(card.delta, cx + 5, cy + 24)

    maxY = Math.max(maxY, cy + cardH)
  }

  return maxY + 8
}

// ── 5.6 Top 3 actividades por ahorro ─────────────────────────────────────────

function _drawTop3Activities(
  doc: DocLike,
  perActivityActual: ActivitySimResult[],
  perActivityAutomated: ActivitySimResult[],
  currency: string,
  m: number, cW: number, y: number
): void {
  const autoMap = new Map(perActivityAutomated.map((a) => [a.bpmnElementId, a]))
  const top3 = perActivityActual
    .map((a) => ({
      name: a.activityName,
      savings: a.expectedTotalCost - (autoMap.get(a.bpmnElementId)?.expectedTotalCost ?? a.expectedTotalCost),
    }))
    .filter((s) => s.savings > 0)
    .sort((a, b) => b.savings - a.savings)
    .slice(0, 3)

  // Título: Refinamiento — 11pt, naranja oscuro (era 8pt, gris)
  doc.setFont('helvetica', 'bold')
  doc.setFontSize(11)
  doc.setTextColor(...C.orangeDarker)
  doc.text('TOP 3 ACTIVIDADES POR AHORRO', m, y)
  y += 10

  if (top3.length === 0) {
    doc.setFont('helvetica', 'normal')
    doc.setFontSize(8)
    doc.setTextColor(...PDF_COLORS.textTertiary)
    doc.text('Sin actividades con ahorro positivo con los datos actuales.', m, y)
    return
  }

  const maxSavings = top3[0].savings
  const totalSavings = top3.reduce((s, a) => s + a.savings, 0)
  const nameColW = cW * 0.38
  const barColW = cW * 0.38
  const valColW = cW * 0.24
  const barH = 5

  for (const act of top3) {
    const pct = totalSavings > 0 ? (act.savings / totalSavings * 100).toFixed(0) : '0'
    const barFill = maxSavings > 0 ? (act.savings / maxSavings) * barColW : 0

    // Nombre (truncado a 30 chars)
    const name = act.name.length > 30 ? act.name.slice(0, 28) + '…' : act.name
    doc.setFont('helvetica', 'normal')
    doc.setFontSize(9)
    doc.setTextColor(...PDF_COLORS.textPrimary)
    doc.text(name, m, y + barH - 1)

    // Barra de fondo (track) con border naranja para definir el área completa
    doc.setFillColor(...C.orangeBar)
    doc.roundedRect(m + nameColW, y, barColW, barH, 1, 1, 'F')
    doc.setDrawColor(...C.heroOrange)
    doc.setLineWidth(0.3)
    doc.roundedRect(m + nameColW, y, barColW, barH, 1, 1, 'S')
    doc.setLineWidth(0.2)

    // Barra de fill
    if (barFill > 0) {
      doc.setFillColor(...C.heroOrange)
      doc.roundedRect(m + nameColW, y, barFill, barH, 1, 1, 'F')
    }

    // Valor + porcentaje
    doc.setFont('helvetica', 'bold')
    doc.setFontSize(8)
    doc.setTextColor(...C.orangeDark)
    const valStr = `${formatCurrency(act.savings, currency)} (${pct}%)`
    doc.text(valStr, m + nameColW + barColW + valColW, y + barH - 1, { align: 'right' } as Record<string, unknown>)

    y += barH + 5
  }
}

const ROI_HIGH_THRESHOLD = 1000

// ─── Resumen ejecutivo ────────────────────────────────────────────────────────

export function drawRoiExecutiveSummary(
  doc: DocLike,
  process: Process,
  roi: RoiResult,
  currency: string,
  startY: number
): number {
  let y = startY

  doc.setFont('helvetica', 'bold')
  doc.setFontSize(12)
  doc.setTextColor(...PDF.slate900)
  doc.text('Análisis de retorno de inversión', PDF.margin, y)
  y += 6

  doc.setDrawColor(...PDF.slate200)
  doc.setLineWidth(0.2)
  doc.line(PDF.margin, y, PDF.margin + PDF.contentW, y)
  y += 5

  doc.setFont('helvetica', 'normal')
  doc.setFontSize(9)
  doc.setTextColor(...PDF.slate700)

  const investmentStr = process.automationInvestment > 0
    ? `inversión estimada de ${formatCurrency(process.automationInvestment, currency)}`
    : 'inversión aún no configurada'

  const summary = `Análisis de retorno de inversión para automatización del proceso "${process.name}"` +
    (process.clientName ? ` del cliente ${process.clientName}` : '') +
    `, con frecuencia anual de ${process.annualFrequency.toLocaleString('es-PY')} ejecuciones,` +
    ` horizonte de ${process.analysisHorizonYears} años e ${investmentStr}.`

  const summaryLines = doc.splitTextToSize(summary, PDF.contentW)
  doc.text(summaryLines, PDF.margin, y)
  y += summaryLines.length * 4.5 + 3

  // Sub-resumen de resultado
  const resultText = roi.savingsPerExecution > 0
    ? `Resultado: ahorro de ${formatCurrency(roi.savingsPerExecution, currency)} por ejecución (${formatPercent(roi.savingsPerExecutionPercent)} de reducción). ` +
      (Number.isFinite(roi.paybackMonths)
        ? roi.breaksEvenInHorizon
          ? `La inversión se recupera en ${roi.paybackMonths < 24 ? `${roi.paybackMonths.toFixed(1)} meses` : `${roi.paybackYears.toFixed(1)} años`}, dentro del horizonte de análisis.`
          : `El payback ocurre en ${roi.paybackYears.toFixed(1)} años, fuera del horizonte analizado.`
        : 'Sin ahorro neto — la automatización no se recupera con los datos actuales.')
    : 'El escenario automatizado no genera ahorro con los datos actuales. Revisá los costos configurados.'

  const resultLines = doc.splitTextToSize(resultText, PDF.contentW)
  doc.setFont('helvetica', 'italic')
  doc.setFontSize(8.5)
  doc.setTextColor(...PDF.slate500)
  doc.text(resultLines, PDF.margin, y)
  y += resultLines.length * 4 + 4

  doc.setFont('helvetica', 'normal')
  doc.setTextColor(...PDF.slate700)
  return y
}

// ─── 5 KPI cards (grid 2×3 — 5 celdas, 6ta vacía o con meta-info) ────────────

export function drawRoiKpiCards(
  doc: DocLike,
  roi: RoiResult,
  currency: string,
  horizonYears: number,
  startY: number
): number {
  const cardW = 52, cardH = 28, gapX = 7, gapY = 5
  const col1 = PDF.margin, col2 = PDF.margin + cardW + gapX, col3 = PDF.margin + (cardW + gapX) * 2

  const paybackStr = formatPayback(roi.paybackMonths)

  const roiStr = !Number.isFinite(roi.roiAnnualPercent)
    ? '∞'
    : `${roi.roiAnnualPercent.toFixed(0)}%`

  const kpis = [
    // Fila 1: 3 cards
    { label: 'AHORRO POR EJECUCIÓN', value: formatCurrency(roi.savingsPerExecution, currency), sub: `${formatPercent(roi.savingsPerExecutionPercent)} vs. actual`, positive: roi.savingsPerExecution >= 0 },
    { label: 'AHORRO ANUAL', value: formatCurrency(roi.annualSavings, currency), sub: `× ${horizonYears} años`, positive: roi.annualSavings >= 0 },
    { label: 'PAYBACK', value: paybackStr, sub: roi.breaksEvenInHorizon ? 'Dentro del horizonte' : 'Fuera del horizonte', positive: roi.breaksEvenInHorizon },
    // Fila 2: 2 cards + 1 vacía
    { label: `ACUMULADO (${horizonYears} AÑOS)`, value: formatCurrency(roi.cumulativeSavingsHorizon, currency), sub: 'neto de inversión', positive: roi.cumulativeSavingsHorizon >= 0 },
    { label: 'ROI ANUAL', value: roiStr, sub: 'retorno sobre la inversión', positive: roi.roiAnnualPercent >= 0 },
  ]

  const cols = [col1, col2, col3]
  let maxY = startY

  kpis.forEach((kpi, idx) => {
    const row = Math.floor(idx / 3)
    const col = idx % 3
    const x = cols[col]
    const y = startY + row * (cardH + gapY)

    // Fondo
    doc.setFillColor(...PDF.slate50)
    doc.roundedRect(x, y, cardW, cardH, 2, 2, 'F')

    // Borde
    doc.setDrawColor(...PDF.slate200)
    doc.setLineWidth(0.2)
    doc.roundedRect(x, y, cardW, cardH, 2, 2, 'S')

    // Label
    doc.setFont('helvetica', 'normal')
    doc.setFontSize(5.5)
    doc.setTextColor(...PDF.slate500)
    doc.text(kpi.label, x + 3, y + 5.5)

    // Valor
    const valueColor = kpi.positive ? [16, 185, 129] as [number,number,number] : [239, 68, 68] as [number,number,number]
    doc.setFont('helvetica', 'bold')
    doc.setFontSize(kpi.value.length > 22 ? 7 : kpi.value.length > 14 ? 9 : 11)
    doc.setTextColor(...valueColor)
    doc.text(kpi.value, x + 3, y + 16)

    // Sub-texto
    doc.setFont('helvetica', 'normal')
    doc.setFontSize(6)
    doc.setTextColor(...PDF.slate500)
    const subLines = doc.splitTextToSize(kpi.sub, cardW - 6)
    doc.text(subLines, x + 3, y + 22)

    maxY = Math.max(maxY, y + cardH)
  })

  return maxY + 6
}

// ─── Config de autotable para la tabla comparativa ────────────────────────────
// Se llama desde pdf-export.ts donde autoTable ya está importado dinámicamente.

export function buildComparisonTableConfig(
  perActivityActual: ActivitySimResult[],
  perActivityAutomated: ActivitySimResult[],
  activities: Activity[],
  currency: string,
  startY: number
): Record<string, unknown> {
  const autoByBpmnId = new Map(perActivityAutomated.map((a) => [a.bpmnElementId, a]))
  const isAutomatableMap = new Map(activities.map((a) => [a.bpmnElementId, a.automatable]))

  const RA = 'right' as const

  const head = [[
    { content: 'Actividad', styles: { cellWidth: 42 } },
    { content: `Act. (${currency})`, styles: { halign: RA, cellWidth: 22 } },
    { content: `Auto. (${currency})`, styles: { halign: RA, cellWidth: 22 } },
    { content: `Ahorro (${currency})`, styles: { halign: RA, cellWidth: 22 } },
    { content: 'Ahorro %', styles: { halign: RA, cellWidth: 16 } },
    { content: 'Prob.', styles: { halign: RA, cellWidth: 14 } },
  ]]

  const body = [...perActivityActual]
    .sort((a, b) => {
      const aCost = autoByBpmnId.get(a.bpmnElementId)?.expectedTotalCost ?? a.expectedTotalCost
      const bCost = autoByBpmnId.get(b.bpmnElementId)?.expectedTotalCost ?? b.expectedTotalCost
      return (b.expectedTotalCost - bCost) - (a.expectedTotalCost - aCost)
    })
    .map((act) => {
      const auto = autoByBpmnId.get(act.bpmnElementId)
      const autoCost = auto?.expectedTotalCost ?? act.expectedTotalCost
      const savings = act.expectedTotalCost - autoCost
      const savingsPct = act.expectedTotalCost > 0 ? (savings / act.expectedTotalCost) * 100 : 0
      const isAutomatable = isAutomatableMap.get(act.bpmnElementId) ?? false

      const savingsColor = savings > 0 ? [16, 185, 129] : savings < 0 ? [239, 68, 68] : [100, 116, 139]
      const rowFill = isAutomatable ? [255, 255, 255] : [248, 250, 252]
      const nonAutoText = [148, 163, 184]  // slate-400, más tenue

      const savingsCell = isAutomatable
        ? { content: (savings >= 0 ? '+' : '') + fmtNum(savings), styles: { halign: RA, font: 'courier', fillColor: rowFill, textColor: savingsColor } }
        : { content: '—', styles: { halign: RA, fillColor: rowFill, textColor: nonAutoText } }
      const savingsPctCell = isAutomatable
        ? { content: (savings >= 0 ? '+' : '') + savingsPct.toFixed(1) + '%', styles: { halign: RA, fillColor: rowFill, textColor: savingsColor } }
        : { content: '—', styles: { halign: RA, fillColor: rowFill, textColor: nonAutoText } }

      return [
        { content: act.activityName, styles: { fillColor: rowFill, fontStyle: isAutomatable ? 'bold' : 'normal' as const, textColor: isAutomatable ? [15, 23, 42] : nonAutoText } },
        { content: fmtNum(act.expectedTotalCost), styles: { halign: RA, font: 'courier', fillColor: rowFill, textColor: isAutomatable ? [15, 23, 42] : nonAutoText } },
        { content: fmtNum(autoCost), styles: { halign: RA, font: 'courier', fillColor: rowFill, textColor: isAutomatable ? [15, 23, 42] : nonAutoText } },
        savingsCell,
        savingsPctCell,
        { content: `${(act.executionProbability * 100).toFixed(0)}%`, styles: { halign: RA, fillColor: rowFill, textColor: isAutomatable ? [15, 23, 42] : nonAutoText } },
      ]
    })

  return {
    head,
    body,
    startY,
    margin: { left: PDF.margin, right: PDF.margin },
    styles: { fontSize: 7.5, cellPadding: 2.5, overflow: 'ellipsize', lineColor: [226, 232, 240], lineWidth: 0.2 },
    headStyles: {
      fillColor: [255, 248, 237],    // #FFF8ED naranja claro
      textColor: [146, 64, 14],      // #92400E naranja oscuro
      fontStyle: 'bold',
      fontSize: 8,
      lineColor: [245, 152, 69],     // #F59845 border naranja
      lineWidth: 0.5,
    },
    columnStyles: { 0: { cellWidth: 42 } },
  }
}

// ─── Página 3: resúmenes textuales + transparencia ────────────────────────────

export function drawRoiAnalysisPage(
  doc: DocLike,
  roi: RoiResult,
  process: Process,
  perActivityActual: ActivitySimResult[],
  perActivityAutomated: ActivitySimResult[],
  activities: Activity[],
  currency: string,
  startY: number
): number {
  let y = startY
  const m = PDF.margin, cW = PDF.contentW

  // Título de página
  doc.setFont('helvetica', 'bold')
  doc.setFontSize(11)
  doc.setTextColor(...C.orangeDarker)
  doc.text('ANÁLISIS DEL AHORRO', m, y)
  y += 5

  doc.setDrawColor(...C.heroOrange)
  doc.setLineWidth(0.5)
  doc.line(m, y, m + cW, y)
  doc.setLineWidth(0.2)
  y += 7

  // ── COMPOSICIÓN DEL AHORRO ────────────────────────────────────────────────
  doc.setFont('helvetica', 'bold')
  doc.setFontSize(9)
  doc.setTextColor(...C.orangeDarker)
  doc.text('COMPOSICIÓN DEL AHORRO', m, y)
  y += 7

  const autoByBpmnId = new Map(perActivityAutomated.map((a) => [a.bpmnElementId, a]))
  const savingsPerAct = perActivityActual
    .map((a) => {
      const auto = autoByBpmnId.get(a.bpmnElementId)
      return { name: a.activityName, savings: a.expectedTotalCost - (auto?.expectedTotalCost ?? a.expectedTotalCost) }
    })
    .filter((s) => s.savings > 0)
    .sort((a, b) => b.savings - a.savings)

  doc.setFont('helvetica', 'normal')
  doc.setFontSize(9)
  doc.setTextColor(...PDF_COLORS.textPrimary)

  if (savingsPerAct.length > 0) {
    const top3 = savingsPerAct.slice(0, 3)
    const top3TotalSavings = top3.reduce((s, a) => s + a.savings, 0)
    const totalSavingsAll = savingsPerAct.reduce((s, a) => s + a.savings, 0)
    const top3Pct = totalSavingsAll > 0 ? (top3TotalSavings / totalSavingsAll * 100).toFixed(0) : '0'

    const compositionText = `El ${top3Pct}% del ahorro total proviene de las primeras ${Math.min(3, savingsPerAct.length)} actividades automatizadas:`
    const compositionLines = doc.splitTextToSize(compositionText, cW)
    doc.text(compositionLines, m, y)
    y += compositionLines.length * 4.5 + 4

    doc.setFontSize(9)
    doc.setTextColor(...PDF_COLORS.textSecondary)
    for (const act of top3) {
      const actPct = totalSavingsAll > 0 ? (act.savings / totalSavingsAll * 100).toFixed(1) : '0'
      const actLine = doc.splitTextToSize(
        `  - ${act.name}: ${formatCurrency(act.savings, currency)} (${actPct}% del ahorro total)`,
        cW
      )
      doc.text(actLine, m, y)
      y += actLine.length * 4.5
    }
  } else {
    doc.setTextColor(...PDF_COLORS.textTertiary)
    doc.text('Sin actividades con ahorro positivo en el escenario actual.', m, y)
    y += 5
  }
  y += 6

  // ── TRAYECTORIA DEL AHORRO ACUMULADO (texto narrativo) ───────────────────
  doc.setFont('helvetica', 'bold')
  doc.setFontSize(9)
  doc.setTextColor(...C.orangeDarker)
  doc.text('TRAYECTORIA DEL AHORRO ACUMULADO', m, y)
  y += 7

  let trajectoryText: string
  if (process.automationInvestment > 0 && Number.isFinite(roi.paybackMonths)) {
    const recoveryStr = roi.paybackMonths < 24
      ? `${roi.paybackMonths.toFixed(1)} meses`
      : `${roi.paybackYears.toFixed(1)} años`
    const multiple = (roi.cumulativeSavingsHorizon / process.automationInvestment + 1).toFixed(1)
    trajectoryText =
      `La inversión de ${formatCurrency(process.automationInvestment, currency)} se recupera en ${recoveryStr}. ` +
      `A ${process.analysisHorizonYears} años, el ahorro acumulado proyectado es ` +
      `${formatCurrency(roi.cumulativeSavingsHorizon, currency)}, equivalente a ${multiple}x la inversión inicial. ` +
      `Ver gráfico en página 5.`
  } else if (process.automationInvestment === 0) {
    trajectoryText =
      `Sin inversión configurada. El ahorro anual de ${formatCurrency(roi.annualSavings, currency)} ` +
      `equivale a ${formatCurrency(roi.cumulativeSavingsHorizon, currency)} acumulados en ${process.analysisHorizonYears} años. ` +
      `Ver gráfico en página 5.`
  } else {
    trajectoryText = 'Sin ahorro proyectado con los datos actuales. Revisá los costos automatizados configurados.'
  }

  doc.setFont('helvetica', 'normal')
  doc.setFontSize(9)
  doc.setTextColor(...PDF_COLORS.textSecondary)
  const trajectoryLines = doc.splitTextToSize(trajectoryText, cW)
  doc.text(trajectoryLines, m, y)
  y += trajectoryLines.length * 4.5 + 8

  // ── Transparencia metodológica (si aplica) ────────────────────────────────
  y = drawTransparencyBlock(doc, activities, process.automationInvestment, roi, y)

  return y
}

export function drawTransparencyBlock(
  doc: DocLike,
  activities: Activity[],
  investment: number,
  roi: RoiResult,
  startY: number
): number {
  const zeroCost = activities.filter(
    (a) => a.automatable && a.automatedCostFixed === 0 && a.automatedTimeMinutes === 0
  )
  const investmentZero = investment === 0
  const roiHigh = Number.isFinite(roi.roiAnnualPercent) && roi.roiAnnualPercent > ROI_HIGH_THRESHOLD

  if (zeroCost.length === 0 && !investmentZero && !roiHigh) return startY

  const boxX = PDF.margin
  const boxW = PDF.contentW
  const padH = 5, padV = 6

  // ── Paso 1: recopilar contenido para calcular altura ─────────────────────
  type TextBlock = { text: string[]; bold?: boolean; fontSize: number }
  const blocks: TextBlock[] = []

  blocks.push({ text: ['Transparencia metodológica'], bold: true, fontSize: 9 })

  if (zeroCost.length > 0) {
    const msg = zeroCost.length === 1
      ? `Actividad "${zeroCost[0].name || zeroCost[0].bpmnElementId}" automatizable sin costo configurado. El ahorro puede estar inflado artificialmente.`
      : `${zeroCost.length} actividades sin costo automatizado: ${zeroCost.map((a) => a.name || a.bpmnElementId).join(', ')}. El ahorro puede estar inflado.`
    blocks.push({ text: doc.splitTextToSize(msg, boxW - padH * 2), fontSize: 8.5 })
  }

  if (investmentZero) {
    blocks.push({
      text: doc.splitTextToSize(
        'Inversión en automatización no configurada. El payback aparece como inmediato y el ROI como infinito. Configurá la inversión para un análisis realista.',
        boxW - padH * 2
      ),
      fontSize: 8.5,
    })
  }

  if (roiHigh) {
    blocks.push({
      text: doc.splitTextToSize(
        `ROI inusualmente alto: ${formatPercent(roi.roiAnnualPercent)}. Un ROI > ${formatPercent(ROI_HIGH_THRESHOLD, 0)} anual suele indicar frecuencia sobreestimada, costo automatizado subestimado o inversión insuficiente.`,
        boxW - padH * 2
      ),
      fontSize: 8.5,
    })
  }

  // Calcular altura total
  let height = padV
  for (const b of blocks) {
    height += b.text.length * (b.bold ? 5 : 4.2) + 3
  }
  height += padV

  // ── Paso 2: fondo + borde ─────────────────────────────────────────────────
  doc.setFillColor(255, 251, 235)    // #FFFBEB warning-bg
  doc.setDrawColor(245, 158, 11)     // #F59E0B warning-border
  doc.setLineWidth(0.4)
  doc.rect(boxX, startY, boxW, height, 'FD')

  // ── Paso 3: texto ─────────────────────────────────────────────────────────
  let y = startY + padV

  for (const b of blocks) {
    if (b.bold) {
      doc.setFont('helvetica', 'bold')
      doc.setFontSize(b.fontSize)
      doc.setTextColor(...C.orangeDarker)
    } else {
      doc.setFont('helvetica', 'normal')
      doc.setFontSize(b.fontSize)
      doc.setTextColor(120, 53, 15)  // #78350F
    }
    doc.text(b.text, boxX + padH, y)
    y += b.text.length * (b.bold ? 5 : 4.2) + 3
  }

  return startY + height + 6
}

// ─── Página 4: nota metodológica con identidad InQ ───────────────────────────

export function drawRoiMethodologySection(doc: DocLike, process: Process, startY: number): number {
  let y = startY
  const m = PDF.margin, cW = PDF.contentW
  const boxPad = 6

  // Título uppercase naranja oscuro
  doc.setFont('helvetica', 'bold')
  doc.setFontSize(11)
  doc.setTextColor(...C.orangeDarker)
  doc.text('NOTA METODOLOGICA - ANALISIS DE ROI', m, y)
  y += 5

  doc.setDrawColor(...C.heroOrange)
  doc.setLineWidth(0.5)
  doc.line(m, y, m + cW, y)
  doc.setLineWidth(0.2)
  y += 8

  // Caja contenedora: fondo slate-50 + border-left #94A3B8
  const boxX = m
  const boxW = cW
  const contentX = m + boxPad + 3  // +3 para el border-left visual

  const sections = [
    {
      title: 'Costo por ejecucion actual:',
      body: `SUM(actividades) [costo_fijo x prob_ejecucion] x (1 + overhead ${(process.overheadPercentage * 100).toFixed(0)}%). Probabilidades calculadas por propagacion hacia adelante en el grafo BPMN.`,
    },
    {
      title: 'Costo por ejecucion automatizado:',
      body: 'Mismo modelo, pero las actividades marcadas como automatizables usan su costo automatizado configurado. Las no marcadas mantienen el costo actual.',
    },
    {
      title: 'Ahorro anual:',
      body: `(Costo actual - Costo automatizado) x ${process.annualFrequency.toLocaleString('es-PY')} ejecuciones/año.`,
    },
    {
      title: 'Payback (meses):',
      body: '(Inversion / Ahorro anual) x 12. Nominal simple, sin tasa de descuento ni inflacion.',
    },
    {
      title: 'Ahorro acumulado:',
      body: `Ahorro anual x ${process.analysisHorizonYears} años - Inversion inicial. Nominal, sin VP.`,
    },
    {
      title: 'ROI anualizado:',
      body: '(Ahorro anual / Inversion) x 100%. Referencia: METHODOLOGY_AUTOMATED_COST.md.',
    },
  ]

  // Pre-calcular altura total para dibujar el fondo primero
  let boxHeight = boxPad * 2
  for (const s of sections) {
    const bodyLines = doc.splitTextToSize(s.body, boxW - boxPad * 2 - 4)
    boxHeight += 5 + bodyLines.length * 4.2 + 5
  }

  doc.setFillColor(248, 250, 252)   // #F8FAFC slate-50
  doc.rect(boxX, y, boxW, boxHeight, 'F')
  doc.setFillColor(...C.metaBg)
  doc.rect(boxX, y, 2, boxHeight, 'F')  // border-left visual: 2mm rect gris

  let ty = y + boxPad
  for (const s of sections) {
    doc.setFont('helvetica', 'bold')
    doc.setFontSize(9)
    doc.setTextColor(...PDF_COLORS.textPrimary)
    doc.text(s.title, contentX, ty)
    ty += 5

    doc.setFont('helvetica', 'normal')
    doc.setFontSize(8.5)
    doc.setTextColor(...PDF_COLORS.textSecondary)
    const lines = doc.splitTextToSize(s.body, boxW - boxPad * 2 - 4)
    doc.text(lines, contentX, ty)
    ty += lines.length * 4.2 + 5
  }

  return y + boxHeight + 4
}

// ─── Página 5: Trayectoria del ahorro acumulado (SVG nativo) ─────────────────

export function drawTrajectoryChart(
  doc: DocLike,
  roi: RoiResult,
  process: Process,
  currency: string,
  m: number,
  cW: number,
  startY: number
): number {
  const horizonMonths = process.analysisHorizonYears * 12
  const investment = process.automationInvestment
  const annualSavings = roi.annualSavings
  const finalCumulative = roi.cumulativeSavingsHorizon

  // Función de la trayectoria: lineal desde -investment hasta finalCumulative
  const valueAt = (month: number) => -investment + (annualSavings / 12) * month

  // Área del gráfico
  const labelColW = 20            // mm para etiquetas eje Y
  const chartLeft = m + labelColW
  const chartRight = m + cW
  const chartWidth = chartRight - chartLeft
  const chartTop = startY
  const chartHeight = 90          // mm — 60-70% del alto disponible
  const chartBottom = chartTop + chartHeight

  // Rango de valores Y
  const yMin = Math.min(-investment, 0)
  const yMax = Math.max(finalCumulative, 0)
  const yRange = yMax - yMin || 1

  // Conversiones coordenadas
  const toX = (month: number) => chartLeft + (month / (horizonMonths || 1)) * chartWidth
  const toY = (value: number) => chartBottom - ((value - yMin) / yRange) * chartHeight

  const zeroY = toY(0)
  const paybackX = roi.breaksEvenInHorizon && roi.paybackMonths > 0
    ? toX(roi.paybackMonths)
    : chartLeft   // sin payback: toda el área es negativa o positiva

  // 1. Fondo del gráfico (slate-50)
  doc.setFillColor(248, 250, 252)
  doc.rect(chartLeft, chartTop, chartWidth, chartHeight, 'F')

  // 2. Área negativa (rojo claro) y positiva (naranja claro) — 100 tiras verticales
  const strips = 100
  for (let i = 0; i < strips; i++) {
    const m1 = (i / strips) * horizonMonths
    const m2 = ((i + 1) / strips) * horizonMonths
    const mMid = (m1 + m2) / 2
    const val = valueAt(mMid)
    const x1 = toX(m1)
    const x2 = toX(m2)
    const cy = toY(val)
    const yTop = Math.max(Math.min(cy, zeroY), chartTop)
    const yBot = Math.min(Math.max(cy, zeroY), chartBottom)
    const h = yBot - yTop
    if (h > 0.1) {
      if (val < 0) {
        doc.setFillColor(254, 226, 226)  // #FEE2E2 rojo claro
      } else {
        doc.setFillColor(254, 215, 170)  // #FED7AA naranja claro
      }
      doc.rect(x1, yTop, Math.max(x2 - x1, 0.1), h, 'F')
    }
  }

  // 3. Línea principal naranja (segmentos mensuales)
  doc.setDrawColor(245, 152, 69)   // #F59845
  doc.setLineWidth(0.5)
  for (let mo = 0; mo < horizonMonths; mo++) {
    doc.line(toX(mo), toY(valueAt(mo)), toX(mo + 1), toY(valueAt(mo + 1)))
  }

  // 4. Ejes
  doc.setDrawColor(148, 163, 184)  // slate-400
  doc.setLineWidth(0.3)
  doc.line(chartLeft, chartTop, chartLeft, chartBottom)          // eje Y
  doc.line(chartLeft, zeroY, chartRight, zeroY)                   // eje X (línea cero)

  // 5. Labels eje X: Año 0, 1, 2, ... N
  doc.setFont('helvetica', 'normal')
  doc.setFontSize(7)
  doc.setTextColor(100, 116, 139)  // slate-500
  for (let yr = 0; yr <= process.analysisHorizonYears; yr++) {
    const lx = toX(yr * 12)
    doc.text(`Año ${yr}`, lx, chartBottom + 5, { align: 'center' } as Record<string, unknown>)
    doc.setDrawColor(200, 200, 200)
    doc.setLineWidth(0.2)
    doc.line(lx, chartBottom, lx, chartBottom + 2)
  }

  // 6. Labels eje Y: valor mínimo (abajo), 0 (línea cero), valor máximo (arriba)
  doc.setDrawColor(148, 163, 184)
  doc.setLineWidth(0.2)
  doc.setFont('helvetica', 'normal')
  doc.setFontSize(6.5)
  doc.setTextColor(100, 116, 139)

  // Máximo
  doc.text(_fmtAxisVal(yMax), chartLeft - 2, chartTop + 4, { align: 'right' } as Record<string, unknown>)
  // Cero y mínimo: cuando la línea cero queda casi en el borde inferior (inversión
  // muy pequeña relativa al ahorro), los labels "0" y yMin se solapan con "Año 0".
  // En ese caso mostramos "0" elevado 4mm dentro del área del gráfico y omitimos
  // yMin (la zona negativa es visualmente invisible; el dato está en la anotación).
  if (yMin < 0) {
    const minSepMm = 8
    const zeroNearBottom = (chartBottom - zeroY) < minSepMm
    if (!zeroNearBottom) {
      doc.text('0', chartLeft - 2, zeroY + 1, { align: 'right' } as Record<string, unknown>)
      doc.text(_fmtAxisVal(yMin), chartLeft - 2, chartBottom, { align: 'right' } as Record<string, unknown>)
    } else {
      doc.text('0', chartLeft - 2, chartBottom - 4, { align: 'right' } as Record<string, unknown>)
    }
  }

  // 7. Marker de payback (línea vertical punteada, solo si hay payback en horizonte)
  if (roi.breaksEvenInHorizon && roi.paybackMonths > 0 && roi.paybackMonths <= horizonMonths) {
    doc.setDrawColor(180, 83, 9)   // #B45309
    doc.setLineWidth(0.4)
    const dashLen = 2.5, gapLen = 1.8
    let dy = chartTop
    while (dy < chartBottom) {
      doc.line(paybackX, dy, paybackX, Math.min(dy + dashLen, chartBottom))
      dy += dashLen + gapLen
    }
    // Label del payback (offset a la derecha para no tapar el eje Y)
    const labelX = Math.min(paybackX + 3, chartLeft + chartWidth - 35)
    doc.setFont('helvetica', 'bold')
    doc.setFontSize(7)
    doc.setTextColor(180, 83, 9)
    doc.text(`Payback: ${formatPayback(roi.paybackMonths)}`, labelX, Math.max(zeroY - 3, chartTop + 5))
  }

  doc.setLineWidth(0.2)

  // 8. Anotaciones al pie del gráfico
  const annY = chartBottom + 12
  doc.setFont('helvetica', 'normal')
  doc.setFontSize(8)
  doc.setTextColor(...PDF_COLORS.textSecondary)

  const annotations: string[] = []
  if (investment > 0) {
    annotations.push(`Inversión inicial: ${formatCurrency(investment, currency)}`)
  }
  annotations.push(`Ahorro acumulado al año ${process.analysisHorizonYears}: ${formatCurrency(finalCumulative, currency)}`)
  if (investment > 0 && finalCumulative > 0) {
    const mult = (finalCumulative / investment + 1).toFixed(1)
    annotations.push(`Multiplicador de retorno: ${mult}x la inversión`)
  }

  const annotText = annotations.join('   |   ')
  const annotLines = doc.splitTextToSize(annotText, cW)
  doc.text(annotLines, m, annY)

  return annY + annotLines.length * 4.5 + 4
}

// ─── Helpers locales ──────────────────────────────────────────────────────────

function _fmtAxisVal(n: number): string {
  const abs = Math.abs(n)
  const sign = n < 0 ? '-' : ''
  if (abs >= 1_000_000) return `${sign}${(abs / 1_000_000).toFixed(1)}M`
  if (abs >= 1_000)     return `${sign}${(abs / 1_000).toFixed(0)}K`
  return `${sign}${abs.toFixed(0)}`
}

function fmtNum(n: number): string {
  return n.toLocaleString('es-PY', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
}
