/**
 * Tests del módulo domain/roi.ts.
 * Cubren: happy path, edge cases de inversión cero, ahorro cero/negativo,
 * división por cero, escenarios de largo plazo y corto plazo.
 * Todas las fórmulas son nominales simples (ADR-014).
 */

import { describe, it, expect } from 'vitest'
import { calculateRoi } from '@/domain/roi'
import type { RoiInput } from '@/domain/roi'

// ─── Fixture base ─────────────────────────────────────────────────────────────

function baseInput(overrides: Partial<RoiInput> = {}): RoiInput {
  return {
    costPerExecutionActual: 100,
    costPerExecutionAutomated: 20,
    annualFrequency: 1000,
    analysisHorizonYears: 3,
    automationInvestment: 50_000,
    ...overrides,
  }
}

// ─── Happy path ───────────────────────────────────────────────────────────────

describe('calculateRoi — happy path', () => {
  it('calcula ahorro por ejecución correctamente', () => {
    const r = calculateRoi(baseInput())
    expect(r.savingsPerExecution).toBeCloseTo(80)
  })

  it('calcula ahorro por ejecución en %', () => {
    const r = calculateRoi(baseInput())
    expect(r.savingsPerExecutionPercent).toBeCloseTo(80)  // 80/100 = 80%
  })

  it('calcula ahorro anual = ahorro/ejecución × frecuencia', () => {
    const r = calculateRoi(baseInput())
    expect(r.annualSavings).toBeCloseTo(80_000)  // 80 × 1000
  })

  it('calcula payback en meses correctamente', () => {
    const r = calculateRoi(baseInput())
    // $50.000 / $80.000/año × 12 = 7.5 meses
    expect(r.paybackMonths).toBeCloseTo(7.5)
  })

  it('calcula payback en años', () => {
    const r = calculateRoi(baseInput())
    expect(r.paybackYears).toBeCloseTo(0.625)
  })

  it('calcula ahorro acumulado neto al horizonte', () => {
    const r = calculateRoi(baseInput())
    // ($80.000 × 3 años) - $50.000 = $190.000
    expect(r.cumulativeSavingsHorizon).toBeCloseTo(190_000)
  })

  it('el payback dentro del horizonte es true cuando paybackYears < horizonYears', () => {
    const r = calculateRoi(baseInput())  // payback ~0.625 < 3 años
    expect(r.breaksEvenInHorizon).toBe(true)
  })

  it('calcula ROI anualizado %', () => {
    const r = calculateRoi(baseInput())
    // ($80.000 / $50.000) × 100 = 160%
    expect(r.roiAnnualPercent).toBeCloseTo(160)
  })
})

// ─── Edge case: inversión cero con ahorro positivo ────────────────────────────

describe('calculateRoi — inversión cero', () => {
  it('payback es 0 meses (recuperación instantánea)', () => {
    const r = calculateRoi(baseInput({ automationInvestment: 0 }))
    expect(r.paybackMonths).toBe(0)
    expect(r.paybackYears).toBe(0)
  })

  it('breaksEvenInHorizon es true', () => {
    const r = calculateRoi(baseInput({ automationInvestment: 0 }))
    expect(r.breaksEvenInHorizon).toBe(true)
  })

  it('roiAnnualPercent es Infinity', () => {
    const r = calculateRoi(baseInput({ automationInvestment: 0 }))
    expect(r.roiAnnualPercent).toBe(Infinity)
  })

  it('inversión cero con ahorro cero: roiAnnualPercent es 0', () => {
    const r = calculateRoi(baseInput({
      automationInvestment: 0,
      costPerExecutionActual: 100,
      costPerExecutionAutomated: 100,
    }))
    expect(r.roiAnnualPercent).toBe(0)
  })
})

// ─── Edge case: ahorro cero o negativo ────────────────────────────────────────

describe('calculateRoi — ahorro negativo o cero', () => {
  it('si el costo automatizado = costo actual, ahorro = 0', () => {
    const r = calculateRoi(baseInput({ costPerExecutionAutomated: 100 }))
    expect(r.savingsPerExecution).toBeCloseTo(0)
    expect(r.annualSavings).toBeCloseTo(0)
  })

  it('ahorro cero → payback Infinity', () => {
    const r = calculateRoi(baseInput({ costPerExecutionAutomated: 100 }))
    expect(r.paybackMonths).toBe(Infinity)
    expect(r.paybackYears).toBe(Infinity)
  })

  it('ahorro cero → breaksEvenInHorizon false', () => {
    const r = calculateRoi(baseInput({ costPerExecutionAutomated: 100 }))
    expect(r.breaksEvenInHorizon).toBe(false)
  })

  it('ahorro negativo (automatizado más caro): payback Infinity', () => {
    const r = calculateRoi(baseInput({ costPerExecutionAutomated: 150 }))
    expect(r.annualSavings).toBeLessThan(0)
    expect(r.paybackMonths).toBe(Infinity)
    expect(r.breaksEvenInHorizon).toBe(false)
  })

  it('ahorro negativo: cumulativeSavings también es negativo', () => {
    const r = calculateRoi(baseInput({ costPerExecutionAutomated: 150 }))
    expect(r.cumulativeSavingsHorizon).toBeLessThan(0)
  })
})

// ─── Edge case: costo actual cero (evitar div/0) ──────────────────────────────

describe('calculateRoi — costo actual cero', () => {
  it('savingsPerExecutionPercent es 0, no NaN ni Infinity', () => {
    const r = calculateRoi(baseInput({
      costPerExecutionActual: 0,
      costPerExecutionAutomated: 0,
    }))
    expect(r.savingsPerExecutionPercent).toBe(0)
    expect(Number.isFinite(r.savingsPerExecutionPercent)).toBe(true)
  })
})

// ─── Escenario de largo plazo (horizonte 10 años) ─────────────────────────────

describe('calculateRoi — horizonte extendido', () => {
  it('payback fuera del horizonte corto → breaksEvenInHorizon false', () => {
    // savingsPerExec=10, freq=1000 → annualSavings=$10.000; payback=$500.000/$10.000=50 años
    const r = calculateRoi(baseInput({
      automationInvestment: 500_000,
      costPerExecutionActual: 100,
      costPerExecutionAutomated: 90,
      annualFrequency: 1000,
    }))
    expect(r.paybackYears).toBeGreaterThan(3)
    expect(r.breaksEvenInHorizon).toBe(false)
  })
})
