// SPDX-License-Identifier: MIT
pragma solidity 0.8.24;

/// @title BondStateV3
/// @notice Canonical bond state machine used by V3 star bonds and task escrow.
///         Every non-terminal state has a deadline and a safe forward path.
library BondStateV3 {
    enum State {
        Uninitialized,
        Proposed, // one party signed
        Accepted, // both signed, awaiting funding
        Funded, // principal escrowed
        Active, // running under signed terms
        SettlementRequested,
        Finalizable,
        Challenged,
        SettlementFrozen, // oracle inputs snapshotted; distribution imminent
        Settled, // TERMINAL
        Cancelled, // TERMINAL (mutual or pre-fund)
        Expired, // TERMINAL (time-bound, permissionless force exit)
        ForceExited // TERMINAL (post-deadline unilateral exit)
    }

    error IllegalTransition(State from, State to);

    /// @dev Enforces the whitelisted transitions. Any other pairing reverts.
    function requireTransition(State from, State to) internal pure {
        // Terminal states cannot transition further.
        if (from == State.Settled || from == State.Cancelled || from == State.ForceExited) {
            revert IllegalTransition(from, to);
        }

        if (from == State.Uninitialized && to == State.Proposed) return;
        if (from == State.Proposed && (to == State.Accepted || to == State.Expired || to == State.Cancelled)) return;
        if (from == State.Accepted && (to == State.Funded || to == State.Cancelled || to == State.Expired)) return;
        if (from == State.Funded && (to == State.Active || to == State.Cancelled || to == State.Expired)) return;
        if (from == State.Active && (to == State.SettlementRequested || to == State.Expired)) return;
        if (
            from == State.SettlementRequested
                && (to == State.Finalizable || to == State.Challenged || to == State.Cancelled || to == State.Expired)
        ) return;
        if (from == State.Finalizable && (to == State.SettlementFrozen || to == State.Cancelled || to == State.Expired))
        {
            return;
        }
        if (from == State.SettlementFrozen && (to == State.Settled || to == State.Cancelled || to == State.Expired)) {
            return;
        }
        // A challenged accountability settlement can either fail closed to a
        // refund or advance only after its contract verifies a typed threshold
        // resolution. Partnership bonds expose no callable path to this edge.
        if (from == State.Challenged && (to == State.Cancelled || to == State.SettlementFrozen)) return;
        if (from == State.Expired && to == State.ForceExited) return;

        revert IllegalTransition(from, to);
    }

    function isTerminal(State s) internal pure returns (bool) {
        return s == State.Settled || s == State.Cancelled || s == State.ForceExited;
    }
}
