/**
 * Tests de los componentes de ROI del reporte (Sprint 1 — Etapa 3).
 *
 * Cubre: RoiKpiBar, ComparisonTable, TransparencySection, TopSavingsChart,
 * CumulativeSavingsChart, y el comportamiento de tabs de ReportPage.
 */

import { describe, it, expect, vi, beforeEach } from 'vitest'
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import React from 'react'

// ─── Mocks de dependencias del browser ───────────────────────────────────────

vi.mock('bpmn-js/lib/Viewer', () => ({
  default: class MockViewer {
    importXML = vi.fn().mockResolvedValue({ warnings: [] })
    get = vi.fn().mockImplementation((svc: string) => {
      if (svc === 'canvas') return { zoom: vi.fn(), addMarker: vi.fn(), removeMarker: vi.fn() }
      if (svc === 'elementRegistry') return { forEach: vi.fn(), getGraphics: vi.fn().mockReturnValue(null), get: vi.fn().mockReturnValue({ width: 100 }) }
      if (svc === 'overlays') return { add: vi.fn(), remove: vi.fn() }
      if (svc === 'eventBus') return { on: vi.fn(), off: vi.fn() }
      return {}
    })
    destroy = vi.fn()
  },
}))

vi.mock('echarts-for-react', () => ({
  default: ({ style }: { option: unknown; style?: React.CSSProperties }) => (
    <div data-testid="echarts-chart" style={style} />
  ),
}))

vi.mock('@/features/report/useReportData', () => ({
  useReportData: vi.fn(),
}))

vi.mock('@tanstack/react-router', async (importActual) => {
  const actual = await importActual<typeof import('@tanstack/react-router')>()
  return {
    ...actual,
    useParams: vi.fn().mockReturnValue({ processId: 'test-proc-id' }),
    useNavigate: vi.fn().mockReturnValue(vi.fn()),
  }
})

// ─── Fixtures ─────────────────────────────────────────────────────────────────

import type { ActivitySimResult, Activity, SimulationResult } from '@/domain/types'
import type { RoiResult } from '@/domain/roi'
import { TooltipProvider } from '@/components/ui/tooltip'

const makeActivity = (id: string, name: string, cost: number): ActivitySimResult => ({
  activityId: id,
  bpmnElementId: `bpmn_${id}`,
  activityName: name,
  expectedDirectCost: cost * 0.83,
  expectedIndirectCost: cost * 0.17,
  expectedTotalCost: cost,
  percentOfTotal: 33,
  executionProbability: 1.0,
  expectedExecutions: 1.0,
  executionTimeMinutes: 60,
  resourceCostBreakdown: [],
})

const perActivityActual: ActivitySimResult[] = [
  makeActivity('a1', 'Recibir solicitud', 600),
  makeActivity('a2', 'Calcular score', 300),
  makeActivity('a3', 'Analizar riesgo', 100),
]

const perActivityAutomated: ActivitySimResult[] = [
  makeActivity('a1', 'Recibir solicitud', 60),   // 90% ahorro
  makeActivity('a2', 'Calcular score', 30),        // 90% ahorro
  makeActivity('a3', 'Analizar riesgo', 100),      // sin cambio (no automatable)
]

const mockResult: SimulationResult = {
  totalCost: 1000,
  totalDirectCost: 833,
  totalIndirectCost: 167,
  totalTimeMinutes: 180,
  perActivity: perActivityActual,
  perResource: [],
  warnings: [],
}

const mockResultAutomated: SimulationResult = {
  totalCost: 190,
  totalDirectCost: 158,
  totalIndirectCost: 32,
  totalTimeMinutes: 30,
  perActivity: perActivityAutomated,
  perResource: [],
  warnings: [],
}

const mockRoi: RoiResult = {
  savingsPerExecution: 810,
  savingsPerExecutionPercent: 81,
  annualSavings: 9_720_000,
  paybackMonths: 0.617,
  paybackYears: 0.051,
  cumulativeSavingsHorizon: 29_110_000,
  breaksEvenInHorizon: true,
  roiAnnualPercent: 19440,
}

const roiNoSavings: RoiResult = {
  savingsPerExecution: -50,
  savingsPerExecutionPercent: -10,
  annualSavings: -60_000,
  paybackMonths: Infinity,
  paybackYears: Infinity,
  cumulativeSavingsHorizon: -230_000,
  breaksEvenInHorizon: false,
  roiAnnualPercent: -60,
}

const roiZeroInvestment: RoiResult = {
  savingsPerExecution: 200,
  savingsPerExecutionPercent: 50,
  annualSavings: 240_000,
  paybackMonths: 0,
  paybackYears: 0,
  cumulativeSavingsHorizon: 720_000,
  breaksEvenInHorizon: true,
  roiAnnualPercent: Infinity,
}

// ─── RoiKpiBar ────────────────────────────────────────────────────────────────

import { RoiKpiBar } from '@/features/report/RoiKpiBar'

describe('RoiKpiBar', () => {
  it('renderiza exactamente 5 ROI KPI cards', () => {
    render(<RoiKpiBar roi={mockRoi} currency="USD" horizonYears={3} />)
    expect(screen.getAllByTestId('roi-kpi-card')).toHaveLength(5)
  })

  it('muestra el ahorro por ejecución formateado con moneda', () => {
    render(<RoiKpiBar roi={mockRoi} currency="USD" horizonYears={3} />)
    const text = document.body.textContent ?? ''
    // $810.00 debe aparecer
    expect(text).toMatch(/810/)
  })

  it('payback Infinity → muestra "No recupera en horizonte"', () => {
    render(<RoiKpiBar roi={roiNoSavings} currency="USD" horizonYears={3} />)
    expect(screen.getByText(/No recupera en horizonte/i)).toBeInTheDocument()
  })

  it('payback 0 (inversión cero) → muestra "Inmediato"', () => {
    render(<RoiKpiBar roi={roiZeroInvestment} currency="USD" horizonYears={3} />)
    expect(screen.getByText(/Inmediato/i)).toBeInTheDocument()
  })

  it('ROI Infinity (inversión cero) → muestra "—" con texto accionable', () => {
    render(<RoiKpiBar roi={roiZeroInvestment} currency="USD" horizonYears={3} />)
    expect(screen.getByText('—')).toBeInTheDocument()
    expect(screen.getByText(/Configurá la inversión/i)).toBeInTheDocument()
  })

  it('ahorro negativo: "breaksEvenInHorizon = false" → subtexto fuera del horizonte', () => {
    render(<RoiKpiBar roi={roiNoSavings} currency="USD" horizonYears={3} />)
    expect(screen.getByText(/Fuera del horizonte/i)).toBeInTheDocument()
  })

  it('breaksEvenInHorizon = true → subtexto dentro del horizonte', () => {
    render(<RoiKpiBar roi={mockRoi} currency="USD" horizonYears={3} />)
    expect(screen.getByText(/Dentro del horizonte/i)).toBeInTheDocument()
  })

  it('horizonYears se refleja en el label del KPI de acumulado', () => {
    render(<RoiKpiBar roi={mockRoi} currency="USD" horizonYears={5} />)
    const text = document.body.textContent ?? ''
    expect(text).toMatch(/5 años/)
  })

  it('ningún KPI muestra NaN', () => {
    render(<RoiKpiBar roi={mockRoi} currency="USD" horizonYears={3} />)
    const text = document.body.textContent ?? ''
    expect(text).not.toContain('NaN')
  })
})

// ─── ComparisonTable ──────────────────────────────────────────────────────────

import { ComparisonTable } from '@/features/report/ComparisonTable'

const automatableIds = new Set(['bpmn_a1', 'bpmn_a2'])  // a1 y a2 son automatizables
const noAutomatableIds = new Set<string>()

describe('ComparisonTable', () => {
  it('renderiza N+1 rows (N actividades + 1 header)', () => {
    render(
      <ComparisonTable
        perActivityActual={perActivityActual}
        perActivityAutomated={perActivityAutomated}
        automatableIds={automatableIds}
        currency="USD"
      />
    )
    const rows = screen.getAllByRole('row')
    expect(rows).toHaveLength(perActivityActual.length + 1)
  })

  it('actividades automatable muestran ícono Bot', () => {
    render(
      <ComparisonTable
        perActivityActual={perActivityActual}
        perActivityAutomated={perActivityAutomated}
        automatableIds={automatableIds}
        currency="USD"
      />
    )
    // a1 y a2 son automatable → 2 íconos Bot
    const botIcons = document.querySelectorAll('[aria-label="Marcada como automatizable"]')
    expect(botIcons).toHaveLength(2)
  })

  it('actividades NO automatable NO muestran ícono Bot', () => {
    render(
      <ComparisonTable
        perActivityActual={perActivityActual}
        perActivityAutomated={perActivityAutomated}
        automatableIds={noAutomatableIds}  // ninguna automatable
        currency="USD"
      />
    )
    const botIcons = document.querySelectorAll('[aria-label="Marcada como automatizable"]')
    expect(botIcons).toHaveLength(0)
  })

  it('ahorro de a1: 600 - 60 = 540 aparece en la tabla', () => {
    render(
      <ComparisonTable
        perActivityActual={perActivityActual}
        perActivityAutomated={perActivityAutomated}
        automatableIds={automatableIds}
        currency="USD"
      />
    )
    const text = document.body.textContent ?? ''
    expect(text).toMatch(/540/)
  })

  it('tabla vacía: muestra mensaje sin actividades', () => {
    render(
      <ComparisonTable
        perActivityActual={[]}
        perActivityAutomated={[]}
        automatableIds={noAutomatableIds}
        currency="USD"
      />
    )
    expect(screen.getByText(/Sin actividades para comparar/i)).toBeInTheDocument()
  })

  it('ahorro negativo: se muestra sin prefijo +', () => {
    const negativeActual = [makeActivity('x1', 'Tarea cara auto', 100)]
    const negativeAuto = [makeActivity('x1', 'Tarea cara auto', 200)]  // costo auto > actual
    render(
      <ComparisonTable
        perActivityActual={negativeActual}
        perActivityAutomated={negativeAuto}
        automatableIds={new Set(['bpmn_x1'])}
        currency="USD"
      />
    )
    // -100 de ahorro, sin prefijo '+'
    const text = document.body.textContent ?? ''
    expect(text).not.toMatch(/\+.*-/)
  })

  it('sort toggle: clic en encabezado "Ahorro" invierte el orden', () => {
    render(
      <ComparisonTable
        perActivityActual={perActivityActual}
        perActivityAutomated={perActivityAutomated}
        automatableIds={automatableIds}
        currency="USD"
      />
    )
    // Buscar el th que contiene "Ahorro" (hay múltiples elementos con ese texto)
    const headers = screen.getAllByRole('columnheader')
    const ahorroHeader = headers.find((h) => /Ahorro/i.test(h.textContent ?? ''))
    expect(ahorroHeader).toBeDefined()

    // Antes del click: orden desc (a1=540 primero)
    const rowsBefore = screen.getAllByRole('row').slice(1)
    const firstNameBefore = rowsBefore[0].textContent

    fireEvent.click(ahorroHeader!)
    // Después del click: orden asc (a3=0 primero)
    const rowsAfter = screen.getAllByRole('row').slice(1)
    const firstNameAfter = rowsAfter[0].textContent

    expect(firstNameBefore).not.toBe(firstNameAfter)
  })
})

// ─── TransparencySection ──────────────────────────────────────────────────────

import { TransparencySection } from '@/features/report/TransparencySection'

const makeActivityDomain = (id: string, name: string, automatable: boolean, zeroCosts: boolean): Activity => ({
  id, processId: 'p1', bpmnElementId: `bpmn_${id}`, name, type: 'task',
  directCostFixed: 200, executionTimeMinutes: 60, assignedResources: [],
  automatable,
  automatedCostFixed: zeroCosts ? 0 : 50,
  automatedTimeMinutes: zeroCosts ? 0 : 10,
  automatedAssignedResources: [],
})

describe('TransparencySection', () => {
  it('retorna null cuando no hay issues — no renderiza nada', () => {
    const { container } = render(
      <TransparencySection
        activities={[makeActivityDomain('a1', 'Tarea', true, false)]}  // automatable con costos configurados
        investment={50_000}
      />
    )
    expect(container.firstChild).toBeNull()
  })

  it('Caso 1: actividad automatable con costos en cero → sección visible', () => {
    render(
      <TransparencySection
        activities={[makeActivityDomain('a1', 'Facturación', true, true)]}  // automatable, costo=0
        investment={50_000}
      />
    )
    expect(screen.getByText(/Transparencia metodológica/i)).toBeInTheDocument()
    expect(screen.getByText('Facturación')).toBeInTheDocument()
  })

  it('Caso 1: mensaje explica que el ahorro puede estar inflado', () => {
    render(
      <TransparencySection
        activities={[makeActivityDomain('a1', 'Facturación', true, true)]}
        investment={50_000}
      />
    )
    expect(screen.getByText(/inflar artificialmente el ahorro/i)).toBeInTheDocument()
  })

  it('Caso 1: plural correcto cuando hay múltiples actividades en cero', () => {
    render(
      <TransparencySection
        activities={[
          makeActivityDomain('a1', 'Tarea 1', true, true),
          makeActivityDomain('a2', 'Tarea 2', true, true),
        ]}
        investment={50_000}
      />
    )
    expect(screen.getByText(/2 actividades automatizables sin costo configurado/i)).toBeInTheDocument()
  })

  it('Caso 2: investment=0 → muestra aviso de inversión no configurada', () => {
    render(
      <TransparencySection
        activities={[makeActivityDomain('a1', 'Tarea', true, false)]}  // costos OK
        investment={0}
      />
    )
    expect(screen.getByText(/Inversión en automatización no configurada/i)).toBeInTheDocument()
    expect(screen.getByText(/payback aparece como inmediato/i)).toBeInTheDocument()
  })

  it('Caso 3: ambos issues coexisten — se muestran ambos mensajes en la misma sección', () => {
    render(
      <TransparencySection
        activities={[makeActivityDomain('a1', 'Facturación', true, true)]}  // costo cero
        investment={0}  // inversión cero
      />
    )
    expect(screen.getByText('Facturación')).toBeInTheDocument()
    expect(screen.getByText(/Inversión en automatización no configurada/i)).toBeInTheDocument()
  })

  it('actividades no automatable no generan issue aunque tengan costos cero', () => {
    const { container } = render(
      <TransparencySection
        activities={[makeActivityDomain('a1', 'Manual', false, true)]}  // no automatable
        investment={50_000}
      />
    )
    expect(container.firstChild).toBeNull()
  })

  // ─── Caso 4: ROI inusualmente alto ────────────────────────────────────────

  const makeRoi = (roiAnnualPercent: number): RoiResult => ({
    savingsPerExecution: 100,
    savingsPerExecutionPercent: 50,
    annualSavings: 1_200_000,
    paybackMonths: 0.5,
    paybackYears: 0.04,
    cumulativeSavingsHorizon: 3_550_000,
    breaksEvenInHorizon: true,
    roiAnnualPercent,
  })

  it('Caso 4: ROI al 999% NO dispara el aviso de ROI inusual', () => {
    const { container } = render(
      <TransparencySection
        activities={[makeActivityDomain('a1', 'T', true, false)]}
        investment={50_000}
        roi={makeRoi(999)}
      />
    )
    // No hay otros issues → el componente no renderiza (999% no supera el umbral)
    expect(container.firstChild).toBeNull()
  })

  it('Caso 4: ROI al 1001% SÍ dispara el aviso de ROI inusual', () => {
    render(
      <TransparencySection
        activities={[makeActivityDomain('a1', 'T', true, false)]}
        investment={50_000}
        roi={makeRoi(1001)}
      />
    )
    expect(screen.getByText(/ROI inusualmente alto detectado/i)).toBeInTheDocument()
    // El texto incluye el valor formateado (>1000%)
    const text = document.body.textContent ?? ''
    expect(text).toMatch(/1\.001/)
  })

  it('Caso 4: ROI Infinity (investment=0) NO dispara este aviso — lo cubre el caso 2', () => {
    const { container } = render(
      <TransparencySection
        activities={[makeActivityDomain('a1', 'T', true, false)]}
        investment={0}  // caso 2 activo
        roi={makeRoi(Infinity)}
      />
    )
    // El caso 2 (investment=0) sí aparece, pero NO el aviso de "ROI inusualmente alto"
    expect(screen.queryByText(/ROI inusualmente alto detectado/i)).not.toBeInTheDocument()
    // El caso 2 sí está presente
    expect(container.firstChild).not.toBeNull()
    expect(screen.getByText(/Inversión en automatización no configurada/i)).toBeInTheDocument()
  })

  it('Caso 4: ROI negativo NO dispara el aviso de ROI inusual', () => {
    const { container } = render(
      <TransparencySection
        activities={[makeActivityDomain('a1', 'T', true, false)]}
        investment={50_000}
        roi={makeRoi(-50)}
      />
    )
    expect(container.firstChild).toBeNull()
    expect(screen.queryByText(/ROI inusualmente alto detectado/i)).not.toBeInTheDocument()
  })

  it('Caso 4 coexiste con Caso 1: ROI alto Y actividad con costo=0 → ambos mensajes', () => {
    render(
      <TransparencySection
        activities={[makeActivityDomain('a1', 'Facturación', true, true)]}  // costo=0
        investment={50_000}  // no es cero
        roi={makeRoi(5000)}  // > 1000 y finite
      />
    )
    expect(screen.getByText('Facturación')).toBeInTheDocument()               // Caso 1
    expect(screen.getByText(/ROI inusualmente alto detectado/i)).toBeInTheDocument()  // Caso 4
  })
})

// ─── TopSavingsChart ──────────────────────────────────────────────────────────

import { TopSavingsChart } from '@/features/report/TopSavingsChart'

describe('TopSavingsChart', () => {
  it('muestra empty state cuando no hay ahorros positivos', () => {
    // perActivityAutomated con costos mayores → ahorro negativo
    const actualCosts = [makeActivity('a1', 'Tarea', 100)]
    const autoCosts = [makeActivity('a1', 'Tarea', 200)]
    render(
      <TopSavingsChart
        perActivityActual={actualCosts}
        perActivityAutomated={autoCosts}
        currency="USD"
      />
    )
    expect(screen.getByText(/Sin actividades con ahorro positivo/i)).toBeInTheDocument()
  })

  it('con ahorros positivos: renderiza el chart ECharts', () => {
    render(
      <TopSavingsChart
        perActivityActual={perActivityActual}
        perActivityAutomated={perActivityAutomated}
        currency="USD"
      />
    )
    expect(screen.getByTestId('echarts-chart')).toBeInTheDocument()
  })

  it('excluye actividades con ahorro cero o negativo del chart', () => {
    // a3 no tiene ahorro (costos iguales) → no debe aparecer en el chart
    // Solo a1 y a2 tienen ahorro positivo
    // Si renderiza chart → hay ≥1 actividad con ahorro > 0
    render(
      <TopSavingsChart
        perActivityActual={perActivityActual}
        perActivityAutomated={perActivityAutomated}
        currency="USD"
      />
    )
    // Chart renderizado → hay actividades con ahorro positivo
    expect(screen.getByTestId('echarts-chart')).toBeInTheDocument()
  })

  it('con más de 5 actividades: sigue renderizando chart (top-5 filtrado internamente)', () => {
    const manyActual = Array.from({ length: 8 }, (_, i) =>
      makeActivity(`a${i}`, `Tarea ${i}`, (i + 1) * 100)
    )
    const manyAuto = Array.from({ length: 8 }, (_, i) =>
      makeActivity(`a${i}`, `Tarea ${i}`, (i + 1) * 10)
    )
    render(
      <TopSavingsChart
        perActivityActual={manyActual}
        perActivityAutomated={manyAuto}
        currency="USD"
      />
    )
    // No debe tirar error ni mostrar más de 5 en el chart (ECharts mockeado — no podemos inspeccionar option)
    expect(screen.getByTestId('echarts-chart')).toBeInTheDocument()
  })
})

// ─── CumulativeSavingsChart ───────────────────────────────────────────────────

import { CumulativeSavingsChart } from '@/features/report/CumulativeSavingsChart'

describe('CumulativeSavingsChart', () => {
  it('renderiza el chart sin crash (payback dentro del horizonte)', () => {
    render(
      <CumulativeSavingsChart
        roi={mockRoi}
        horizonYears={3}
        currency="USD"
        investment={50_000}
      />
    )
    expect(screen.getByTestId('echarts-chart')).toBeInTheDocument()
  })

  it('renderiza el chart sin crash cuando payback = Infinity (sin ahorro)', () => {
    render(
      <CumulativeSavingsChart
        roi={roiNoSavings}
        horizonYears={3}
        currency="USD"
        investment={50_000}
      />
    )
    expect(screen.getByTestId('echarts-chart')).toBeInTheDocument()
  })

  it('renderiza el chart sin crash cuando investment = 0', () => {
    render(
      <CumulativeSavingsChart
        roi={roiZeroInvestment}
        horizonYears={3}
        currency="USD"
        investment={0}
      />
    )
    expect(screen.getByTestId('echarts-chart')).toBeInTheDocument()
  })
})

// ─── ReportPage — comportamiento de tabs ──────────────────────────────────────

import { useReportData } from '@/features/report/useReportData'
import { ReportPage } from '@/features/report/ReportPage'

const mockProcessBase = {
  id: 'test-proc-id', name: 'Proceso Test', clientName: 'Cliente',
  bpmnXml: '<?xml version="1.0"?><definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"><process id="p"><startEvent id="s"><outgoing>f1</outgoing></startEvent><task id="t1" name="Task 1"><incoming>f1</incoming><outgoing>f2</outgoing></task><endEvent id="e"><incoming>f2</incoming></endEvent><sequenceFlow id="f1" sourceRef="s" targetRef="t1"/><sequenceFlow id="f2" sourceRef="t1" targetRef="e"/></process></definitions>',
  currency: 'USD', overheadPercentage: 0.2,
  annualFrequency: 1200, analysisHorizonYears: 3, automationInvestment: 50_000,
  createdAt: Date.now() - 3600_000, updatedAt: Date.now() - 3600_000,
  groupId: null, tags: [],
}

const mockSimulationBase = {
  id: 'sim-1', processId: 'test-proc-id',
  executedAt: Date.now() - 600_000,
  result: mockResult,
}

const automtableActivity: Activity = {
  id: 'act-1', processId: 'test-proc-id', bpmnElementId: 'bpmn_a1', name: 'Recibir solicitud',
  type: 'task', directCostFixed: 500, executionTimeMinutes: 60, assignedResources: [],
  automatable: true, automatedCostFixed: 50, automatedTimeMinutes: 5,
  automatedAssignedResources: [],
}

const nonAutomatableActivity: Activity = {
  ...automtableActivity, id: 'act-2', bpmnElementId: 'bpmn_a2', name: 'Procesar',
  automatable: false,
}

describe('ReportPage — comportamiento de tabs', () => {
  beforeEach(() => {
    vi.clearAllMocks()
  })

  it('sin resultAutomated (snapshot viejo): tabs "Automatizado" y "ROI" están disabled', () => {
    vi.mocked(useReportData).mockReturnValue({
      process: mockProcessBase,
      simulation: { ...mockSimulationBase },  // sin resultAutomated
      activities: [automtableActivity],
      resources: [],
      isLoading: false,
      error: null,
    })
    render(
      <TooltipProvider>
        <ReportPage />
      </TooltipProvider>
    )
    const tabAuto = screen.getByRole('tab', { name: /automatizado/i })
    const tabRoi = screen.getByRole('tab', { name: /comparación/i })
    expect(tabAuto).toBeDisabled()
    expect(tabRoi).toBeDisabled()
  })

  it('con resultAutomated pero SIN actividades automatizables: tabs disabled', () => {
    vi.mocked(useReportData).mockReturnValue({
      process: mockProcessBase,
      simulation: { ...mockSimulationBase, resultAutomated: mockResultAutomated },
      activities: [nonAutomatableActivity],  // no hay automatable
      resources: [],
      isLoading: false,
      error: null,
    })
    render(
      <TooltipProvider>
        <ReportPage />
      </TooltipProvider>
    )
    const tabAuto = screen.getByRole('tab', { name: /automatizado/i })
    const tabRoi = screen.getByRole('tab', { name: /comparación/i })
    expect(tabAuto).toBeDisabled()
    expect(tabRoi).toBeDisabled()
  })

  it('con resultAutomated Y actividades automatizables: tabs habilitados', () => {
    vi.mocked(useReportData).mockReturnValue({
      process: mockProcessBase,
      simulation: { ...mockSimulationBase, resultAutomated: mockResultAutomated },
      activities: [automtableActivity],  // hay 1 automatable
      resources: [],
      isLoading: false,
      error: null,
    })
    render(
      <TooltipProvider>
        <ReportPage />
      </TooltipProvider>
    )
    const tabAuto = screen.getByRole('tab', { name: /automatizado/i })
    const tabRoi = screen.getByRole('tab', { name: /comparación/i })
    expect(tabAuto).not.toBeDisabled()
    expect(tabRoi).not.toBeDisabled()
  })

  it('tab "Actual" activo por defecto: muestra los 4 KPI cards del costo actual', () => {
    vi.mocked(useReportData).mockReturnValue({
      process: mockProcessBase,
      simulation: { ...mockSimulationBase, resultAutomated: mockResultAutomated },
      activities: [automtableActivity],
      resources: [],
      isLoading: false,
      error: null,
    })
    render(
      <TooltipProvider>
        <ReportPage />
      </TooltipProvider>
    )
    // Tab "Actual" activo por defecto → 4 kpi-cards (no 5 roi-kpi-cards)
    expect(screen.getAllByTestId('kpi-card')).toHaveLength(4)
    expect(screen.queryByTestId('roi-kpi-card')).toBeNull()
  })

  it('click en tab "Comparación + ROI": muestra 5 ROI KPI cards', async () => {
    const user = userEvent.setup()
    vi.mocked(useReportData).mockReturnValue({
      process: mockProcessBase,
      simulation: { ...mockSimulationBase, resultAutomated: mockResultAutomated },
      activities: [automtableActivity],
      resources: [],
      isLoading: false,
      error: null,
    })
    render(
      <TooltipProvider>
        <ReportPage />
      </TooltipProvider>
    )
    await user.click(screen.getByRole('tab', { name: /comparación/i }))
    await waitFor(() => expect(screen.getAllByTestId('roi-kpi-card')).toHaveLength(5))
  })

  it('click en tab "Comparación + ROI": la tabla de comparación tiene N+1 rows', async () => {
    const user = userEvent.setup()
    vi.mocked(useReportData).mockReturnValue({
      process: mockProcessBase,
      simulation: { ...mockSimulationBase, resultAutomated: mockResultAutomated },
      activities: [automtableActivity],
      resources: [],
      isLoading: false,
      error: null,
    })
    render(
      <TooltipProvider>
        <ReportPage />
      </TooltipProvider>
    )
    await user.click(screen.getByRole('tab', { name: /comparación/i }))
    // mockResult tiene 3 actividades → 3 data rows + 1 header
    await waitFor(() => expect(screen.getAllByRole('row')).toHaveLength(perActivityActual.length + 1))
  })

  it('click en tab "Comparación + ROI" con investment=0: TransparencySection visible', async () => {
    const user = userEvent.setup()
    vi.mocked(useReportData).mockReturnValue({
      process: { ...mockProcessBase, automationInvestment: 0 },
      simulation: { ...mockSimulationBase, resultAutomated: mockResultAutomated },
      activities: [automtableActivity],
      resources: [],
      isLoading: false,
      error: null,
    })
    render(
      <TooltipProvider>
        <ReportPage />
      </TooltipProvider>
    )
    await user.click(screen.getByRole('tab', { name: /comparación/i }))
    await waitFor(() => expect(screen.getByText(/Transparencia metodológica/i)).toBeInTheDocument())
  })

  it('tab "Automatizado": muestra los 4 KPI cards del escenario automatizado', () => {
    vi.mocked(useReportData).mockReturnValue({
      process: mockProcessBase,
      simulation: { ...mockSimulationBase, resultAutomated: mockResultAutomated },
      activities: [automtableActivity],
      resources: [],
      isLoading: false,
      error: null,
    })
    render(
      <TooltipProvider>
        <ReportPage />
      </TooltipProvider>
    )
    fireEvent.click(screen.getByRole('tab', { name: /automatizado/i }))
    // El tab automatizado también muestra 4 kpi-cards (del escenario automatizado)
    const kpiCards = screen.getAllByTestId('kpi-card')
    // Puede haber 4 del tab actual (ocultos) + 4 del tab automatizado = 8
    // O solo 4 si Radix desmonta el tab inactivo
    expect(kpiCards.length).toBeGreaterThanOrEqual(4)
  })

  it('tab "Automatizado": la nota de AutomationScenarioNote está presente', async () => {
    const user = userEvent.setup()
    vi.mocked(useReportData).mockReturnValue({
      process: mockProcessBase,
      simulation: { ...mockSimulationBase, resultAutomated: mockResultAutomated },
      activities: [automtableActivity],
      resources: [],
      isLoading: false,
      error: null,
    })
    render(
      <TooltipProvider>
        <ReportPage />
      </TooltipProvider>
    )
    await user.click(screen.getByRole('tab', { name: /automatizado/i }))
    await waitFor(() =>
      expect(screen.getByText(/actividades no marcadas como automatizables/i)).toBeInTheDocument()
    )
  })
})
