import { describe, it, expect } from 'vitest'
import { runSimulation } from '@/domain/simulation'
import type { Activity, Resource, SimulationInput } from '@/domain/types'

// BPMN de un solo nodo — execProb = 1.0, sin gateways
const SINGLE_TASK_BPMN = `<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" targetNamespace="test">
  <process id="proc1" isExecutable="false">
    <startEvent id="start"><outgoing>f1</outgoing></startEvent>
    <task id="taskA" name="Tarea A"><incoming>f1</incoming><outgoing>f2</outgoing></task>
    <endEvent id="end"><incoming>f2</incoming></endEvent>
    <sequenceFlow id="f1" sourceRef="start" targetRef="taskA"/>
    <sequenceFlow id="f2" sourceRef="taskA" targetRef="end"/>
  </process>
</definitions>`

const makeResource = (id: string, costPerHour: number): Resource => ({
  id, processId: 'proc1', name: `Recurso ${id}`, type: 'role', costPerHour,
})

const baseActivity = (overrides: Partial<Activity> = {}): Activity => ({
  id: 'act-taskA', processId: 'proc1', bpmnElementId: 'taskA',
  name: 'Tarea A', type: 'task',
  directCostFixed: 0, executionTimeMinutes: 0,
  assignedResources: [],
  automatable: true,
  automatedCostFixed: 0, automatedTimeMinutes: 0,
  automatedAssignedResources: [],
  ...overrides,
})

const baseInput = (activity: Activity, resources: Resource[] = []): SimulationInput => ({
  processXml: SINGLE_TASK_BPMN,
  activities: new Map([['taskA', activity]]),
  gateways: new Map(),
  resources: new Map(resources.map((r) => [r.id, r])),
  globalSettings: { currency: 'USD', overheadPercentage: 0 },
  scenario: 'automated',
})

describe('Sprint 3 — automatedAssignedResources en motor de simulación', () => {

  it('Caso 1: solo costo fijo, sin recursos TO-BE — resourceCostBreakdown vacío', () => {
    const act = baseActivity({ automatedCostFixed: 100, automatedTimeMinutes: 60, automatedAssignedResources: [] })
    const result = runSimulation(baseInput(act))

    const actResult = result.perActivity[0]
    expect(actResult.expectedDirectCost).toBeCloseTo(100)
    expect(actResult.resourceCostBreakdown).toHaveLength(0)
  })

  it('Caso 2: solo recursos TO-BE — costo = costPerHour × (min/60) × units × execProb', () => {
    const r1 = makeResource('r1', 30)
    const act = baseActivity({
      automatedCostFixed: 0, automatedTimeMinutes: 30,
      automatedAssignedResources: [{ resourceId: 'r1', utilizationMinutes: 60, units: 2 }],
    })
    const result = runSimulation(baseInput(act, [r1]))

    // 30 $/h × (60min/60) × 2 units × 1.0 execProb = 60
    const actResult = result.perActivity[0]
    expect(actResult.expectedDirectCost).toBeCloseTo(60)
    expect(actResult.resourceCostBreakdown).toHaveLength(1)
    expect(actResult.resourceCostBreakdown[0].resourceId).toBe('r1')
    expect(actResult.resourceCostBreakdown[0].cost).toBeCloseTo(60)
  })

  it('Caso 3: mixto — costo fijo + recurso TO-BE se suman', () => {
    const r1 = makeResource('r1', 60)
    const act = baseActivity({
      automatedCostFixed: 50, automatedTimeMinutes: 30,
      automatedAssignedResources: [{ resourceId: 'r1', utilizationMinutes: 30, units: 1 }],
    })
    const result = runSimulation(baseInput(act, [r1]))

    // fijo=50 + 60*(30/60)*1=30 → total=80
    const actResult = result.perActivity[0]
    expect(actResult.expectedDirectCost).toBeCloseTo(80)
    expect(actResult.resourceCostBreakdown[0].cost).toBeCloseTo(30)
  })

  it('Caso 4: actividad NO automatable — usa assignedResources, ignora automatedAssignedResources', () => {
    const r1 = makeResource('r1', 60)
    const r2 = makeResource('r2', 100)
    const act = baseActivity({
      automatable: false,
      directCostFixed: 0,
      assignedResources: [{ resourceId: 'r1', utilizationMinutes: 60, units: 1 }],
      automatedAssignedResources: [{ resourceId: 'r2', utilizationMinutes: 60, units: 1 }],
    })
    const result = runSimulation(baseInput(act, [r1, r2]))

    // escenario automated pero automatable=false → usa r1 (assignedResources), no r2
    const actResult = result.perActivity[0]
    expect(actResult.resourceCostBreakdown).toHaveLength(1)
    expect(actResult.resourceCostBreakdown[0].resourceId).toBe('r1')
    expect(actResult.resourceCostBreakdown[0].cost).toBeCloseTo(60)
  })
})
