use std::fmt;

use eyre::Result;
use revm::primitives::{Address, U256};

use crate::misc::{
    symbol_utils::{ERC20SymbolsLookup, symbol_lookup_async, symbol_lookup_only_cached},
    utils::UNKNOWN,
};

#[derive(Debug, Clone)]
pub struct MEVLogSignature {
    pub signature: String,
    pub symbol: Option<String>,
    pub amount: Option<U256>,
    show_amount: bool,
}

#[derive(Debug, Clone)]
pub enum MEVLogSignatureType {
    ERC20,
    UNIV2,
    UNIV3,
}

#[hotpath::measure_all]
impl MEVLogSignature {
    pub async fn new(
        address: Address,
        signature_str: Option<String>,
        symbols_lookup: &ERC20SymbolsLookup,
        show_amount: bool,
    ) -> Result<Self> {
        let signature_str = signature_str.unwrap_or(UNKNOWN.to_string());
        let signature_type = get_signature_type(&signature_str);

        let symbol = match symbols_lookup {
            ERC20SymbolsLookup::Async(symbols_lookup) => {
                symbol_lookup_async(address, signature_type, symbols_lookup).await?
            }
            ERC20SymbolsLookup::OnlyCached => symbol_lookup_only_cached(address).await?,
        };

        Ok(Self {
            signature: signature_str,
            symbol,
            amount: None,
            show_amount,
        })
    }

    pub fn with_amount(mut self, amount: Option<U256>) -> Self {
        self.amount = amount;
        self
    }
}

fn get_signature_type(signature_str: &str) -> Option<MEVLogSignatureType> {
    match signature_str {
        "Transfer(address,address,uint256)" => Some(MEVLogSignatureType::ERC20),
        "Approval(address,address,uint256)" => Some(MEVLogSignatureType::ERC20),
        "Swap(address,uint256,uint256,uint256,uint256,address)" => Some(MEVLogSignatureType::UNIV2),
        "Swap(address,address,int256,int256,uint160,uint128,int24)" => {
            Some(MEVLogSignatureType::UNIV3)
        }
        _ => None,
    }
}

impl fmt::Display for MEVLogSignature {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if let Some(amount) = self.amount {
            if self.show_amount {
                write!(
                    f,
                    "{} {} {}",
                    self.signature,
                    amount,
                    self.symbol.as_deref().unwrap_or_default()
                )
            } else {
                write!(
                    f,
                    "{} {}",
                    self.signature,
                    self.symbol.as_deref().unwrap_or_default()
                )
            }
        } else {
            write!(
                f,
                "{} {}",
                self.signature,
                self.symbol.as_deref().unwrap_or_default()
            )
        }
    }
}
