use std::cmp::Ordering;
use std::fmt::{self, Display, Formatter};
use std::num::ParseIntError;
use std::str::FromStr;
use chrono::Utc;
use crate::LIB_NAME_BITCOIN;
pub const LOCKTIME_THRESHOLD: u32 = 500_000_000;
pub const SEQ_NO_CSV_DISABLE_MASK: u32 = 0x80000000;
pub const SEQ_NO_CSV_TYPE_MASK: u32 = 0x00400000;
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display, Error)]
#[display("invalid timelock value {0}")]
pub struct InvalidTimelock(pub u32);
#[derive(Debug, Clone, PartialEq, Eq, From, Display)]
#[display(doc_comments)]
pub enum TimelockParseError {
    #[from]
    InvalidNumber(ParseIntError),
    InvalidHeight(u32),
    InvalidTimestamp(u32),
    InvalidDescriptor(String),
    NoRand,
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
#[derive(StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BITCOIN)]
#[cfg_attr(
    feature = "serde",
    derive(Serialize, Deserialize),
    serde(crate = "serde_crate", transparent)
)]
pub struct LockTime(u32);
impl PartialOrd for LockTime {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        if self.is_height_based() != other.is_height_based() {
            None
        } else {
            Some(self.0.cmp(&other.0))
        }
    }
}
impl LockTime {
    pub const ZERO: Self = Self(0);
    #[inline]
    #[deprecated(since = "0.10.8", note = "use LockTime::ZERO")]
    pub const fn zero() -> Self { Self(0) }
    #[inline]
    pub const fn from_height(height: u32) -> Option<Self> {
        if height < LOCKTIME_THRESHOLD {
            Some(Self(height))
        } else {
            None
        }
    }
    #[inline]
    pub const fn from_unix_timestamp(timestamp: u32) -> Option<Self> {
        if timestamp < LOCKTIME_THRESHOLD {
            None
        } else {
            Some(Self(timestamp))
        }
    }
    #[inline]
    pub const fn from_consensus_u32(lock_time: u32) -> Self { LockTime(lock_time) }
    #[inline]
    pub const fn to_consensus_u32(&self) -> u32 { self.0 }
    #[inline]
    pub const fn into_consensus_u32(self) -> u32 { self.0 }
    #[inline]
    pub const fn is_height_based(self) -> bool { self.0 < LOCKTIME_THRESHOLD }
    #[inline]
    pub const fn is_time_based(self) -> bool { !self.is_height_based() }
}
#[derive(Copy, Clone, PartialOrd, Ord, Eq, PartialEq, Hash, Debug, Default)]
#[derive(StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BITCOIN)]
#[cfg_attr(
    feature = "serde",
    derive(Serialize, Deserialize),
    serde(crate = "serde_crate", transparent)
)]
pub struct LockTimestamp(u32);
impl From<LockTimestamp> for u32 {
    fn from(lock_timestamp: LockTimestamp) -> Self { lock_timestamp.into_consensus_u32() }
}
impl From<LockTimestamp> for LockTime {
    fn from(lock: LockTimestamp) -> Self { LockTime::from_consensus_u32(lock.into_consensus_u32()) }
}
impl TryFrom<u32> for LockTimestamp {
    type Error = InvalidTimelock;
    fn try_from(value: u32) -> Result<Self, Self::Error> { Self::try_from_consensus_u32(value) }
}
impl TryFrom<LockTime> for LockTimestamp {
    type Error = InvalidTimelock;
    fn try_from(lock_time: LockTime) -> Result<Self, Self::Error> {
        Self::try_from_lock_time(lock_time)
    }
}
impl LockTimestamp {
    #[inline]
    pub fn anytime() -> Self { Self(0) }
    #[cfg(feature = "chrono")]
    pub fn since_now() -> Self {
        let now = Utc::now();
        LockTimestamp::from_unix_timestamp(now.timestamp() as u32)
            .expect("we are too far in the future")
    }
    #[inline]
    pub fn from_unix_timestamp(timestamp: u32) -> Option<Self> {
        if timestamp < LOCKTIME_THRESHOLD {
            None
        } else {
            Some(Self(timestamp))
        }
    }
    #[inline]
    pub const fn try_from_lock_time(lock_time: LockTime) -> Result<Self, InvalidTimelock> {
        Self::try_from_consensus_u32(lock_time.into_consensus_u32())
    }
    #[inline]
    pub const fn try_from_consensus_u32(lock_time: u32) -> Result<Self, InvalidTimelock> {
        if !LockTime::from_consensus_u32(lock_time).is_time_based() {
            return Err(InvalidTimelock(lock_time));
        }
        Ok(Self(lock_time))
    }
    #[inline]
    pub const fn to_consensus_u32(&self) -> u32 { self.0 }
    #[inline]
    pub const fn into_consensus_u32(self) -> u32 { self.0 }
    #[inline]
    pub fn into_lock_time(self) -> LockTime { self.into() }
    #[inline]
    pub fn to_lock_time(self) -> LockTime { self.into_lock_time() }
}
impl Display for LockTimestamp {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        f.write_str("time(")?;
        Display::fmt(&self.0, f)?;
        f.write_str(")")
    }
}
impl FromStr for LockTimestamp {
    type Err = TimelockParseError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let s = s.to_lowercase();
        if s == "0" || s == "none" {
            Ok(LockTimestamp::anytime())
        } else if s.starts_with("time(") && s.ends_with(')') {
            let no = s[5..].trim_end_matches(')').parse()?;
            LockTimestamp::try_from(no).map_err(|_| TimelockParseError::InvalidTimestamp(no))
        } else {
            Err(TimelockParseError::InvalidDescriptor(s))
        }
    }
}
#[derive(Copy, Clone, PartialOrd, Ord, Eq, PartialEq, Hash, Debug, Default)]
#[derive(StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BITCOIN)]
#[cfg_attr(
    feature = "serde",
    derive(Serialize, Deserialize),
    serde(crate = "serde_crate", transparent)
)]
pub struct LockHeight(u32);
impl From<LockHeight> for u32 {
    fn from(lock_height: LockHeight) -> Self { lock_height.into_consensus_u32() }
}
impl From<LockHeight> for LockTime {
    fn from(lock: LockHeight) -> Self { LockTime::from_consensus_u32(lock.into_consensus_u32()) }
}
impl TryFrom<u32> for LockHeight {
    type Error = InvalidTimelock;
    fn try_from(value: u32) -> Result<Self, Self::Error> { Self::try_from_consensus_u32(value) }
}
impl TryFrom<LockTime> for LockHeight {
    type Error = InvalidTimelock;
    fn try_from(lock_time: LockTime) -> Result<Self, Self::Error> {
        Self::try_from_lock_time(lock_time)
    }
}
impl LockHeight {
    #[inline]
    pub fn anytime() -> Self { Self(0) }
    #[inline]
    pub fn from_height(height: u32) -> Option<Self> {
        if height < LOCKTIME_THRESHOLD {
            Some(Self(height))
        } else {
            None
        }
    }
    #[inline]
    pub const fn try_from_lock_time(lock_time: LockTime) -> Result<Self, InvalidTimelock> {
        Self::try_from_consensus_u32(lock_time.into_consensus_u32())
    }
    #[inline]
    pub const fn try_from_consensus_u32(lock_time: u32) -> Result<Self, InvalidTimelock> {
        if !LockTime::from_consensus_u32(lock_time).is_height_based() {
            return Err(InvalidTimelock(lock_time));
        }
        Ok(Self(lock_time))
    }
    #[inline]
    pub const fn to_consensus_u32(&self) -> u32 { self.0 }
    #[inline]
    pub const fn into_consensus_u32(self) -> u32 { self.0 }
    #[inline]
    pub fn to_lock_time(&self) -> LockTime { self.into_lock_time() }
    #[inline]
    pub fn into_lock_time(self) -> LockTime { self.into() }
}
impl Display for LockHeight {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        f.write_str("height(")?;
        Display::fmt(&self.0, f)?;
        f.write_str(")")
    }
}
impl FromStr for LockHeight {
    type Err = TimelockParseError;
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let s = s.to_lowercase();
        if s == "0" || s == "none" {
            Ok(LockHeight::anytime())
        } else if s.starts_with("height(") && s.ends_with(')') {
            let no = s[7..].trim_end_matches(')').parse()?;
            LockHeight::try_from(no).map_err(|_| TimelockParseError::InvalidHeight(no))
        } else {
            Err(TimelockParseError::InvalidDescriptor(s))
        }
    }
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
#[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BITCOIN)]
#[cfg_attr(
    feature = "serde",
    derive(Serialize, Deserialize),
    serde(crate = "serde_crate", transparent)
)]
pub struct SeqNo(u32);
impl SeqNo {
    pub const ZERO: SeqNo = SeqNo(0);
    #[inline]
    pub const fn from_consensus_u32(lock_time: u32) -> Self { SeqNo(lock_time) }
    #[inline]
    pub const fn to_consensus_u32(&self) -> u32 { self.0 }
    #[inline]
    pub const fn from_height(blocks: u16) -> SeqNo { SeqNo(blocks as u32) }
    #[inline]
    pub const fn from_intervals(intervals: u16) -> SeqNo {
        SeqNo(intervals as u32 | SEQ_NO_CSV_TYPE_MASK)
    }
    pub const fn time_lock_interval(self) -> Option<TimeLockInterval> {
        if self.0 & SEQ_NO_CSV_DISABLE_MASK != 0 {
            None
        } else if self.0 & SEQ_NO_CSV_TYPE_MASK != 0 {
            Some(TimeLockInterval::Time((self.0 & 0xFFFF) as u16))
        } else {
            Some(TimeLockInterval::Height((self.0 & 0xFFFF) as u16))
        }
    }
    pub const fn is_timelock(self) -> bool { self.0 & SEQ_NO_CSV_DISABLE_MASK > 1 }
}
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)]
#[derive(StrictType, StrictEncode, StrictDecode)]
#[strict_type(lib = LIB_NAME_BITCOIN, tags = order)]
#[cfg_attr(
    feature = "serde",
    derive(Serialize, Deserialize),
    serde(crate = "serde_crate", rename_all = "camelCase")
)]
pub enum TimeLockInterval {
    #[display("height({0})")]
    Height(u16),
    #[display("time({0})")]
    Time(u16),
}
impl Default for TimeLockInterval {
    fn default() -> Self { TimeLockInterval::Height(default!()) }
}