use runx_contracts::{
    FanoutReceiptDecision, FanoutReceiptStrategy, FanoutReceiptSyncPoint, JsonObject,
};
use runx_core::state_machine::{FanoutSyncDecision, FanoutSyncOutcome, FanoutSyncStrategy};
use runx_parser::ExecutionGraph;

use super::StepRun;

pub(super) fn latest_fanout_receipt_ids(
    runs: &[StepRun],
    graph: &ExecutionGraph,
    group_id: &str,
) -> Vec<String> {
    graph
        .steps
        .iter()
        .filter(|step| step.fanout_group.as_deref() == Some(group_id))
        .filter_map(|step| {
            runs.iter()
                .rev()
                .find(|run| run.step_id == step.id)
                .map(|run| run.receipt.id.to_string())
        })
        .collect()
}

pub(super) fn fanout_sync_point(
    decision: &FanoutSyncDecision,
    branch_receipts: &[String],
) -> FanoutReceiptSyncPoint {
    FanoutReceiptSyncPoint {
        group_id: decision.group_id.clone().into(),
        strategy: receipt_strategy(&decision.strategy),
        decision: receipt_decision(&decision.decision),
        rule_fired: decision.rule_fired.clone().into(),
        reason: decision.reason.clone().into(),
        branch_count: decision.branch_count,
        success_count: decision.success_count,
        failure_count: decision.failure_count,
        required_successes: decision.required_successes,
        branch_receipts: branch_receipts.iter().cloned().map(Into::into).collect(),
        gate: decision_gate(&decision.gate),
    }
}

pub(super) fn receipt_strategy(strategy: &FanoutSyncStrategy) -> FanoutReceiptStrategy {
    match strategy {
        FanoutSyncStrategy::All => FanoutReceiptStrategy::All,
        FanoutSyncStrategy::Any => FanoutReceiptStrategy::Any,
        FanoutSyncStrategy::Quorum => FanoutReceiptStrategy::Quorum,
    }
}

pub(super) fn receipt_decision(decision: &FanoutSyncOutcome) -> FanoutReceiptDecision {
    match decision {
        FanoutSyncOutcome::Proceed => FanoutReceiptDecision::Proceed,
        FanoutSyncOutcome::Halt => FanoutReceiptDecision::Halt,
        FanoutSyncOutcome::Pause => FanoutReceiptDecision::Pause,
        FanoutSyncOutcome::Escalate => FanoutReceiptDecision::Escalate,
    }
}

pub(super) fn decision_gate(
    gate: &Option<runx_core::state_machine::FanoutGate>,
) -> Option<JsonObject> {
    let value = serde_json::to_value(gate.as_ref()?).ok()?;
    let runx_contracts::JsonValue::Object(object) = serde_json::from_value(value).ok()? else {
        return None;
    };
    Some(object)
}
