// Tests for fn_ui::process_transition (cpp/functions/core/process_state_machine).
// Tabla de transiciones definida en .md.

#define CATCH_CONFIG_MAIN
#include "catch_amalgamated.hpp"

#include "core/process_state_machine.h"
#include <cstring>

using fn_ui::process_transition;
using fn_ui::process_state_name;
using fn_ui::process_event_name;
using fn_ui::ProcessState;
using fn_ui::ProcessEvent;

TEST_CASE("process_transition: idle -> running on Spawned") {
    REQUIRE(process_transition(ProcessState::Idle, ProcessEvent::Spawned) == ProcessState::Running);
}

TEST_CASE("process_transition: idle + Trigger sigue Idle (la solicitud no muta el estado)") {
    REQUIRE(process_transition(ProcessState::Idle, ProcessEvent::Trigger) == ProcessState::Idle);
}

TEST_CASE("process_transition: running -> success on Finished") {
    REQUIRE(process_transition(ProcessState::Running, ProcessEvent::Finished) == ProcessState::Success);
}

TEST_CASE("process_transition: running -> error on Failed/Timeout") {
    REQUIRE(process_transition(ProcessState::Running, ProcessEvent::Failed)  == ProcessState::Error);
    REQUIRE(process_transition(ProcessState::Running, ProcessEvent::Timeout) == ProcessState::Error);
}

TEST_CASE("process_transition: success/error -> idle on Reset") {
    REQUIRE(process_transition(ProcessState::Success, ProcessEvent::Reset) == ProcessState::Idle);
    REQUIRE(process_transition(ProcessState::Error,   ProcessEvent::Reset) == ProcessState::Idle);
}

TEST_CASE("process_transition: invalid events keep state") {
    // Idle no acepta Finished/Failed/Timeout.
    REQUIRE(process_transition(ProcessState::Idle, ProcessEvent::Finished) == ProcessState::Idle);
    REQUIRE(process_transition(ProcessState::Idle, ProcessEvent::Failed)   == ProcessState::Idle);
    REQUIRE(process_transition(ProcessState::Idle, ProcessEvent::Timeout)  == ProcessState::Idle);
    REQUIRE(process_transition(ProcessState::Idle, ProcessEvent::Reset)    == ProcessState::Idle);

    // Running ignora Trigger/Spawned/Reset.
    REQUIRE(process_transition(ProcessState::Running, ProcessEvent::Trigger) == ProcessState::Running);
    REQUIRE(process_transition(ProcessState::Running, ProcessEvent::Spawned) == ProcessState::Running);
    REQUIRE(process_transition(ProcessState::Running, ProcessEvent::Reset)   == ProcessState::Running);

    // Success ignora todo menos Reset.
    REQUIRE(process_transition(ProcessState::Success, ProcessEvent::Trigger)  == ProcessState::Success);
    REQUIRE(process_transition(ProcessState::Success, ProcessEvent::Spawned)  == ProcessState::Success);
    REQUIRE(process_transition(ProcessState::Success, ProcessEvent::Finished) == ProcessState::Success);
    REQUIRE(process_transition(ProcessState::Success, ProcessEvent::Failed)   == ProcessState::Success);

    // Error idem.
    REQUIRE(process_transition(ProcessState::Error, ProcessEvent::Finished) == ProcessState::Error);
    REQUIRE(process_transition(ProcessState::Error, ProcessEvent::Spawned)  == ProcessState::Error);
}

TEST_CASE("process_state_name and process_event_name") {
    REQUIRE(std::strcmp(process_state_name(ProcessState::Idle),    "Idle")    == 0);
    REQUIRE(std::strcmp(process_state_name(ProcessState::Running), "Running") == 0);
    REQUIRE(std::strcmp(process_state_name(ProcessState::Success), "Success") == 0);
    REQUIRE(std::strcmp(process_state_name(ProcessState::Error),   "Error")   == 0);

    REQUIRE(std::strcmp(process_event_name(ProcessEvent::Trigger),  "Trigger")  == 0);
    REQUIRE(std::strcmp(process_event_name(ProcessEvent::Spawned),  "Spawned")  == 0);
    REQUIRE(std::strcmp(process_event_name(ProcessEvent::Finished), "Finished") == 0);
    REQUIRE(std::strcmp(process_event_name(ProcessEvent::Failed),   "Failed")   == 0);
    REQUIRE(std::strcmp(process_event_name(ProcessEvent::Timeout),  "Timeout")  == 0);
    REQUIRE(std::strcmp(process_event_name(ProcessEvent::Reset),    "Reset")    == 0);
}

TEST_CASE("process_transition: ciclo completo idle -> running -> success -> idle") {
    ProcessState s = ProcessState::Idle;
    s = process_transition(s, ProcessEvent::Spawned);
    REQUIRE(s == ProcessState::Running);
    s = process_transition(s, ProcessEvent::Finished);
    REQUIRE(s == ProcessState::Success);
    s = process_transition(s, ProcessEvent::Reset);
    REQUIRE(s == ProcessState::Idle);
}
