use derive_more::derive::Debug;
use dutils::error::ContextWrapper;
use fixedstr::str8;

use crate::utils::{fixed::Fixed128, sha256::IntoHash256};

use super::inscriptions::{Address, Outpoint};

#[derive(Clone, Debug, derive_more::From, serde::Serialize, serde::Deserialize, Eq, PartialEq, Hash)]
#[serde(transparent)]
pub struct Brc4Ticker(pub str8);

impl Brc4Ticker {
    pub fn from_u32(v: u32) -> Self {
        v.to_le_bytes().as_slice().try_into().unwrap()
    }
}

impl IntoHash256 for Brc4Ticker {
    fn hash_256(&self) -> super::hash::Hash256 {
        "@brc4ticker_".hash_256().chain_hash(&self.0.as_bytes())
    }
}

impl<'a> TryFrom<&'a [u8]> for Brc4Ticker {
    type Error = <[u8; 4] as TryFrom<&'a [u8]>>::Error;

    fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
        <[u8; 4]>::try_from(value).map(|v| {
            let v = String::from_utf8_lossy(&v).to_lowercase();
            assert!(v.len() <= 7, "all brc4 tickers must fit into 6 bytes in lowercase form");
            Self(str8::make(&v))
        })
    }
}

impl std::str::FromStr for Brc4Ticker {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.starts_with("0x") && s.len() > 4 {
            let x = u32::from_str_radix(&s[2..], 16).anyhow()?;
            Ok(Self::from_u32(x))
        } else {
            s.as_bytes().try_into().anyhow()
        }
    }
}

impl std::fmt::Display for Brc4Ticker {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(self.0.as_str())
    }
}

#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
#[repr(u8)]
pub enum Brc4OpErr {
    NotDeployed,
    AlreadyDeployed,
    ReachDecBound,
    ReachLimBound,
    SupplyMinted,
    InsufficientBalance,
    Transferred,
}

#[derive(Clone, Copy, serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
pub enum InscriptionProtocolErr {
    Brc4(Brc4OpErr),
    Bellmap(BellmapOpErr),
}

#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, Copy, Eq, PartialEq, Hash)]
#[repr(u8)]
pub enum BellmapOpErr {
    LowFee,
    InvalidHeight,
    AlreadyExists,
}

#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Eq, PartialEq, Hash)]
pub enum Brc4Op {
    Mint { tick: Brc4Ticker, amt: Fixed128<18> },
    Deploy { tick: Brc4Ticker, max: Fixed128<18>, lim: Fixed128<18>, dec: u8 },
    Transfer { tick: Brc4Ticker, amt: Fixed128<18> },
    Transferred { tick: Brc4Ticker, recipient: Address, amt: Fixed128<18> },
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Eq, PartialEq, Hash)]
pub struct SnsValue {
    pub name: String,
    pub avatar: Option<Outpoint>,
    pub rev: Option<String>,
    pub relay: Option<Outpoint>,
}

#[derive(Clone, serde::Serialize, serde::Deserialize, Debug, Eq, PartialEq, Hash)]
pub enum InscriptionProtocol {
    Brc4 { operation: Brc4Op, error: Option<Brc4Err> },
    Bellmap { block: u32, error: Option<BellmapOpErr> },
    Numbers(i128),
    Sns(SnsValue),
}
impl InscriptionProtocol {
    pub fn is_token(&self) -> bool {
        matches!(self, InscriptionProtocol::Brc4 { .. })
    }
}

#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, Eq, PartialEq, Hash)]
pub enum Brc4Err {
    NotDeployed,
    AlreadyDeployed,
    ReachDecBound,
    ReachLimBound,
    SupplyMinted,
    InsufficientBalance,
    Transferred,
}
