// mover_oportunidad
// Mueve una oportunidad por el pipeline de ventas. La etapa destino la
// fija el caller; el morfismo valida que la transición sea legal:
//  - la etapa destino debe ser conocida,
//  - una oportunidad cerrada (ganada/perdida) ya no se mueve,
//  - dentro del pipeline abierto sólo se avanza, no se retrocede;
//    cerrar (ganada/perdida) es legal desde cualquier etapa abierta.
//
// states.oportunidad: el registro Oportunidad.
// params: { etapa:str, timestamp:str }

let etapa_actual  = input.states.oportunidad.etapa;
let etapa_destino = input.params.etapa;

// Rango de cada etapa en el pipeline (-1 = etapa desconocida).
let r_actual = -1;
if etapa_actual == "prospecto"   { r_actual = 0; }
if etapa_actual == "calificado"  { r_actual = 1; }
if etapa_actual == "propuesta"   { r_actual = 2; }
if etapa_actual == "negociacion" { r_actual = 3; }
if etapa_actual == "ganada"      { r_actual = 4; }
if etapa_actual == "perdida"     { r_actual = 5; }

let r_destino = -1;
if etapa_destino == "prospecto"   { r_destino = 0; }
if etapa_destino == "calificado"  { r_destino = 1; }
if etapa_destino == "propuesta"   { r_destino = 2; }
if etapa_destino == "negociacion" { r_destino = 3; }
if etapa_destino == "ganada"      { r_destino = 4; }
if etapa_destino == "perdida"     { r_destino = 5; }

if r_destino < 0 {
    throw "etapa destino desconocida: " + etapa_destino
}
if etapa_actual == "ganada" || etapa_actual == "perdida" {
    throw "la oportunidad ya está cerrada (etapa " + etapa_actual + ")"
}

// Cerrar es legal desde cualquier etapa abierta; dentro del pipeline
// abierto sólo se avanza.
let cerrando = etapa_destino == "ganada" || etapa_destino == "perdida";
if !cerrando {
    if r_destino <= r_actual {
        throw "transición inválida " + etapa_actual + " -> " + etapa_destino
            + " (no se retrocede)"
    }
}

[
    #{
        op: "set",
        path: #{ entity: "Oportunidad", id: input.ids.oportunidad, field: "etapa" },
        value: etapa_destino,
    },
]
