use crate::constants::MAX_SOMPI;
use crate::subnets::SubnetworkId;
use crate::tx::TransactionOutpoint;
use kaspa_txscript_errors::TxScriptError;
use thiserror::Error;

#[derive(Error, Debug, Clone, PartialEq, Eq)]
pub enum TxRuleError {
    #[error("transaction has no inputs")]
    NoTxInputs,

    #[error("transaction has duplicate inputs")]
    TxDuplicateInputs,

    #[error("transaction has non zero gas value")]
    TxHasGas,

    #[error("a non coinbase transaction has a payload")]
    NonCoinbaseTxHasPayload,

    #[error("transaction version {0} is unknown")]
    UnknownTxVersion(u16),

    #[error("transaction has {0} inputs where the max allowed is {1}")]
    TooManyInputs(usize, usize),

    #[error("transaction has {0} outputs where the max allowed is {1}")]
    TooManyOutputs(usize, usize),

    #[error("transaction input #{0} signature script is above {1} bytes")]
    TooBigSignatureScript(usize, usize),

    #[error("transaction input #{0} signature script is above {1} bytes")]
    TooBigScriptPublicKey(usize, usize),

    #[error("transaction input #{0} is not finalized")]
    NotFinalized(usize),

    #[error("coinbase transaction has {0} inputs while none are expected")]
    CoinbaseHasInputs(usize),

    #[error("coinbase transaction has {0} outputs while at most {1} are expected")]
    CoinbaseTooManyOutputs(usize, u64),

    #[error("script public key of coinbase output #{0} is too long")]
    CoinbaseScriptPublicKeyTooLong(usize),

    #[error(
        "transaction input #{0} tried to spend coinbase outpoint {1} with daa score of {2} 
    while the merging block daa score is {3} and the coinbase maturity period of {4} hasn't passed yet"
    )]
    ImmatureCoinbaseSpend(usize, TransactionOutpoint, u64, u64, u64),

    #[error("transaction total inputs spending amount overflowed u64")]
    InputAmountOverflow,

    #[error("transaction total inputs spending amount is higher than the max allowed of {}", MAX_SOMPI)]
    InputAmountTooHigh,

    #[error("transaction output {0} has zero value")]
    TxOutZero(usize),

    #[error("transaction output {0} value is higher than the max allowed of {}", MAX_SOMPI)]
    TxOutTooHigh(usize),

    #[error("transaction total outputs value overflowed u64")]
    OutputsValueOverflow,

    #[error("transaction total outputs value is higher than the max allowed of {}", MAX_SOMPI)]
    TotalTxOutTooHigh,

    #[error("transaction tries to spend {0} while its total inputs amount is {1}")]
    SpendTooHigh(u64, u64),

    #[error("one of the transaction sequence locks conditions was not met")]
    SequenceLockConditionsAreNotMet,

    #[error("outpoints corresponding to some transaction inputs are missing from current utxo context")]
    MissingTxOutpoints,

    #[error("failed to verify the signature script: {0}")]
    SignatureInvalid(TxScriptError),

    #[error("failed to verify empty signature script. Inner error: {0}")]
    SignatureEmpty(TxScriptError),

    #[error("input {0} sig op count is {1}, but the calculated value is {2}")]
    WrongSigOpCount(usize, u64, u64),

    #[error("contextual mass (including storage mass) is incomputable")]
    MassIncomputable,

    #[error("calculated contextual mass (including storage mass) {0} is not equal to the committed mass field {1}")]
    WrongMass(u64, u64),

    #[error("transaction subnetwork id {0} is neither native nor coinbase")]
    SubnetworksDisabled(SubnetworkId),

    /// [`TxRuleError::FeerateTooLow`] is not a consensus error but a mempool error triggered by the
    /// fee/mass RBF validation rule
    #[error("fee rate per contextual mass gram is not greater than the fee rate of the replaced transaction")]
    FeerateTooLow,
}

pub type TxResult<T> = std::result::Result<T, TxRuleError>;
