use std::str::FromStr;

use bdk::{
    bitcoin::secp256k1::PublicKey,
    miniscript::{descriptor::Wsh, policy::Concrete},
};

/// A hash time locked contract between two parties.
///
/// If the hash preimage of the hashlock is revealed, the value is sent to the redeem_identity.
///
/// Alternately, if the refund timelock expires, the value can be refunded to the refund_identity.
pub(crate) struct Htlc {
    redeem_identity: PublicKey,
    hashlock: String,
    refund_timelock: u64,
    refund_indentiy: PublicKey,
}

impl Htlc {
    /// Create a new HTLC.
    pub(crate) fn new(
        redeem_identity: PublicKey,
        hashlock: String,
        refund_timelock: u64,
        refund_indentiy: PublicKey,
    ) -> Self {
        Self {
            redeem_identity,
            hashlock,
            refund_timelock,
            refund_indentiy,
        }
    }

    pub(crate) fn to_miniscript_descriptor(&self) -> Wsh<bdk::bitcoin::PublicKey> {
        let htlc_descriptor = Wsh::new(
            self.to_miniscript_policy()
                .compile()
                .expect("Policy compilation only fails on resource limits or mixed timelocks"),
        )
        .expect("Resource limits");
        assert!(htlc_descriptor.sanity_check().is_ok());
        tracing::info!("descriptor: {}", htlc_descriptor);

        htlc_descriptor
    }

    fn to_miniscript_policy(&self) -> Concrete<bdk::bitcoin::PublicKey> {
        Concrete::<bdk::bitcoin::PublicKey>::from_str(&format!(
            "or(10@and(sha256({secret_hash}),pk({redeem_identity})),1@and(older({expiry}),pk({refund_identity})))",
            secret_hash = self.hashlock,
            redeem_identity = self.redeem_identity,
            refund_identity = self.refund_indentiy,
            expiry = self.refund_timelock
        )).expect("Policy compilation only fails on resource limits or mixed timelocks")
    }
}
