mod redeem;
mod script_sig;
mod witness;

use bitcoin::{TxIn, taproot::LeafVersion};
use brk_types::OutputType;

use crate::TxFeatureFlags;

pub(super) struct Facts<'a> {
    pub(super) script_sig: ScriptSigFacts<'a>,
    redeem: redeem::Facts,
    pub(super) witness: WitnessFacts<'a>,
}

impl Facts<'_> {
    #[inline]
    pub(super) fn redeem_sigops(&self) -> Option<usize> {
        self.redeem.sigops()
    }

    #[inline]
    pub(super) fn redeem_is_p2wpkh(&self) -> bool {
        self.redeem.is_p2wpkh()
    }

    #[inline]
    pub(super) fn redeem_is_p2wsh(&self) -> bool {
        self.redeem.is_p2wsh()
    }

    #[inline]
    pub(super) fn redeem_is_witness_program(&self) -> bool {
        self.redeem.is_witness_program()
    }
}

pub(super) struct ScriptSigFacts<'a> {
    pub(super) accurate_sigops: usize,
    pub(super) last_push: Option<&'a [u8]>,
    pub(super) legacy_sigops: usize,
    pub(super) push_only: bool,
}

impl ScriptSigFacts<'_> {
    const EMPTY: Self = Self {
        accurate_sigops: 0,
        last_push: None,
        legacy_sigops: 0,
        push_only: true,
    };
}

pub(super) struct WitnessFacts<'a> {
    pub(super) has_annex: bool,
    pub(super) last: Option<&'a [u8]>,
    pub(super) leaf_version: Option<LeafVersion>,
    pub(super) max_argument_bytes: usize,
    pub(super) stack_items: usize,
}

pub(super) fn analyze<'a>(
    input: &'a TxIn,
    output_type: OutputType,
    flags: &mut TxFeatureFlags,
) -> Facts<'a> {
    flags.insert_type(output_type);

    let script_sig = if input.script_sig.is_empty() {
        ScriptSigFacts::EMPTY
    } else {
        script_sig::analyze(
            &input.script_sig,
            output_type,
            input.witness.is_empty(),
            flags,
        )
    };
    let redeem = redeem::Facts::analyze(script_sig.last_push, output_type);
    let witness = witness::analyze(
        &input.witness,
        redeem.effective_output_type(output_type, input.witness.len()),
        flags,
    );

    Facts {
        script_sig,
        redeem,
        witness,
    }
}

#[cfg(test)]
mod tests {
    use bitcoin::TxIn;
    use brk_types::OutputType;

    use super::analyze;
    use crate::TxFeatureFlags;

    #[test]
    fn empty_script_sig_has_empty_facts() {
        let input = TxIn::default();
        let facts = analyze(&input, OutputType::Unknown, &mut TxFeatureFlags::default());

        assert_eq!(facts.script_sig.accurate_sigops, 0);
        assert_eq!(facts.script_sig.last_push, None);
        assert_eq!(facts.script_sig.legacy_sigops, 0);
        assert!(facts.script_sig.push_only);
    }
}
