//! Token metadata fetching service

use eth_etl_core::{normalize_address, EthToken, Result};
use eth_etl_rpc::{generate_eth_call_request, BatchProvider, JsonRpcRequest};
use std::sync::Arc;

/// ABI-encoded function selectors for ERC20 methods
const NAME_SELECTOR: &str = "0x06fdde03";      // name()
const SYMBOL_SELECTOR: &str = "0x95d89b41";    // symbol()
const DECIMALS_SELECTOR: &str = "0x313ce567";  // decimals()
const TOTAL_SUPPLY_SELECTOR: &str = "0x18160ddd"; // totalSupply()

/// Service for fetching token metadata
pub struct TokenService {
    provider: Arc<dyn BatchProvider>,
}

impl TokenService {
    pub fn new(provider: Arc<dyn BatchProvider>) -> Self {
        Self { provider }
    }

    /// Fetch token metadata for multiple addresses
    pub async fn fetch_tokens(
        &self,
        addresses: Vec<String>,
        block_number: u64,
    ) -> Result<Vec<EthToken>> {
        if addresses.is_empty() {
            return Ok(vec![]);
        }

        // Generate batch requests for all token metadata calls
        let mut requests: Vec<JsonRpcRequest> = Vec::new();
        let mut request_map: Vec<(String, &str)> = Vec::new(); // (address, method)

        let mut id = 0u64;
        for address in &addresses {
            // name()
            requests.push(generate_eth_call_request(address, NAME_SELECTOR, Some(block_number), id));
            request_map.push((address.clone(), "name"));
            id += 1;

            // symbol()
            requests.push(generate_eth_call_request(address, SYMBOL_SELECTOR, Some(block_number), id));
            request_map.push((address.clone(), "symbol"));
            id += 1;

            // decimals()
            requests.push(generate_eth_call_request(address, DECIMALS_SELECTOR, Some(block_number), id));
            request_map.push((address.clone(), "decimals"));
            id += 1;

            // totalSupply()
            requests.push(generate_eth_call_request(address, TOTAL_SUPPLY_SELECTOR, Some(block_number), id));
            request_map.push((address.clone(), "totalSupply"));
            id += 1;
        }

        // Make batch request
        let results = self.provider.make_batch_request(requests).await?;

        // Parse results
        let mut tokens: Vec<EthToken> = Vec::new();
        let mut token_data: std::collections::HashMap<String, TokenData> = std::collections::HashMap::new();

        for (idx, result) in results.iter().enumerate() {
            let (address, method) = &request_map[idx];
            let entry = token_data.entry(address.clone()).or_insert_with(TokenData::default);

            if let Some(hex_value) = result.as_str() {
                match *method {
                    "name" => entry.name = decode_string(hex_value),
                    "symbol" => entry.symbol = decode_string(hex_value),
                    "decimals" => entry.decimals = decode_uint8(hex_value),
                    "totalSupply" => entry.total_supply = decode_uint256(hex_value),
                    _ => {}
                }
            }
        }

        // Convert to EthToken
        for address in addresses {
            let data = token_data.get(&address).cloned().unwrap_or_default();
            tokens.push(EthToken {
                item_type: "token".to_string(),
                address: normalize_address(Some(&address)).unwrap_or(address),
                symbol: data.symbol,
                name: data.name,
                decimals: data.decimals,
                total_supply: data.total_supply,
                block_number,
                block_hash: None,
                block_timestamp: None,
            });
        }

        Ok(tokens)
    }
}

#[derive(Default, Clone)]
struct TokenData {
    name: Option<String>,
    symbol: Option<String>,
    decimals: Option<u8>,
    total_supply: Option<String>,
}

/// Decode ABI-encoded string
fn decode_string(hex: &str) -> Option<String> {
    let hex = hex.strip_prefix("0x").unwrap_or(hex);
    if hex.len() < 128 {
        return None;
    }

    // Skip offset (32 bytes) and read length (32 bytes)
    let length_hex = &hex[64..128];
    let length = u64::from_str_radix(length_hex, 16).ok()? as usize;

    if hex.len() < 128 + length * 2 {
        return None;
    }

    // Read string data
    let data_hex = &hex[128..128 + length * 2];
    let bytes = hex::decode(data_hex).ok()?;

    String::from_utf8(bytes.into_iter().filter(|&b| b != 0).collect()).ok()
}

/// Decode ABI-encoded uint8
fn decode_uint8(hex: &str) -> Option<u8> {
    let hex = hex.strip_prefix("0x").unwrap_or(hex);
    if hex.len() < 64 {
        return None;
    }

    // Last byte contains the value
    u8::from_str_radix(&hex[62..64], 16).ok()
}

/// Decode ABI-encoded uint256 to decimal string
fn decode_uint256(hex: &str) -> Option<String> {
    let hex = hex.strip_prefix("0x").unwrap_or(hex);
    if hex.is_empty() || hex == "0" || hex.chars().all(|c| c == '0') {
        return Some("0".to_string());
    }

    // Parse as big integer
    let trimmed = hex.trim_start_matches('0');
    if trimmed.is_empty() {
        return Some("0".to_string());
    }

    // Convert hex to decimal string using u128 for reasonable values
    // For values > u128::MAX, return the hex representation
    if trimmed.len() <= 32 {
        u128::from_str_radix(trimmed, 16)
            .map(|v| v.to_string())
            .ok()
    } else {
        // Return hex for very large values
        Some(format!("0x{}", hex))
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_decode_uint8() {
        assert_eq!(decode_uint8("0x0000000000000000000000000000000000000000000000000000000000000012"), Some(18));
    }

    #[test]
    fn test_decode_uint256() {
        assert_eq!(decode_uint256("0x0000000000000000000000000000000000000000000000000000000000000064"), Some("100".to_string()));
    }
}
