use std::{
    fmt::{Display, Formatter},
    path::{Path, PathBuf},
};

use chainhook_sdk::bitcoincore_rpc::bitcoin::{self, Address, Block, Network, Script};

use super::*;

#[derive(Default, Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub enum Chain {
    #[default]
    Mainnet,
    Testnet,
    Signet,
    Regtest,
}

impl Chain {
    pub fn from_bitcoin_network(network: &BitcoinNetwork) -> Chain {
        match network {
            BitcoinNetwork::Mainnet => Chain::Mainnet,
            BitcoinNetwork::Testnet => Chain::Testnet,
            BitcoinNetwork::Regtest => Chain::Regtest,
            BitcoinNetwork::Signet => Chain::Signet,
        }
    }

    pub(crate) fn network(self) -> Network {
        match self {
            Self::Mainnet => Network::Bitcoin,
            Self::Testnet => Network::Testnet,
            Self::Signet => Network::Signet,
            Self::Regtest => Network::Regtest,
        }
    }

    pub(crate) fn default_rpc_port(self) -> u16 {
        match self {
            Self::Mainnet => 8332,
            Self::Regtest => 18443,
            Self::Signet => 38332,
            Self::Testnet => 18332,
        }
    }

    pub(crate) fn inscription_content_size_limit(self) -> Option<usize> {
        match self {
            Self::Mainnet | Self::Regtest => None,
            Self::Testnet | Self::Signet => Some(1024),
        }
    }

    pub fn first_inscription_height(self) -> u64 {
        match self {
            Self::Mainnet => 767430,
            Self::Regtest => 0,
            Self::Signet => 112402,
            Self::Testnet => 2413343,
        }
    }

    pub(crate) fn genesis_block(self) -> Block {
        bitcoin::blockdata::constants::genesis_block(self.network())
    }

    pub fn address_from_script(self, script: &Script) -> Result<Address, bitcoin::address::Error> {
        Address::from_script(script, self.network())
    }

    pub(crate) fn join_with_data_dir(self, data_dir: &Path) -> PathBuf {
        match self {
            Self::Mainnet => data_dir.to_owned(),
            Self::Testnet => data_dir.join("testnet3"),
            Self::Signet => data_dir.join("signet"),
            Self::Regtest => data_dir.join("regtest"),
        }
    }
}

impl Display for Chain {
    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
        write!(
            f,
            "{}",
            match self {
                Self::Mainnet => "mainnet",
                Self::Regtest => "regtest",
                Self::Signet => "signet",
                Self::Testnet => "testnet",
            }
        )
    }
}
