use ghost_traits::networks::NetworkRpcResolver;
use serde::{Deserialize, Deserializer};

use sp_std::vec;
use serde_json::json;
use sp_std::vec::Vec;
use sp_core::{H256, U256};
use sp_runtime::offchain::http::{Error as HttpError, HttpResult, PendingRequest};

use crate::{
    ExternalBlockNumber, NetworkType, RequestId, RpcResolverError, WeavingError, WeavingSession,
    LOG_TARGET,
};

fn parse_response(
    http_result: HttpResult,
    request_id: RequestId,
    network_type: &NetworkType,
) -> Result<NetworkResponseType, RpcResolverError> {
    let response = http_result.map_err(|err| match err {
        HttpError::DeadlineReached => RpcResolverError::DeadlineReached,
        HttpError::IoError => RpcResolverError::IoError,
        HttpError::Unknown => RpcResolverError::Unknown,
    })?;

    if response.code != 200 {
        return Err(RpcResolverError::Non200HttpCode);
    }

    let response_body = response.body().collect::<Vec<u8>>();
    let response_str = sp_std::str::from_utf8(&response_body)
        .map_err(|_| RpcResolverError::UnparsableResponseBody)?;

    let response_data: NetworkResponse = match network_type {
        NetworkType::Evm => {
            let evm_response: EvmResponse = serde_json::from_str(response_str)
                .map_err(|_| RpcResolverError::JsonDeserializationFailed)?;

            evm_response.into()
        }
        NetworkType::Utxo => {
            let utxo_response: UtxoResponse = serde_json::from_str(response_str)
                .map_err(|_| RpcResolverError::JsonDeserializationFailed)?;

            utxo_response.into()
        }
        _ => return Err(RpcResolverError::UnknownNetworkType),
    };

    if request_id != response_data.id {
        return Err(RpcResolverError::InvalidResponseId);
    }

    if response_data.error.is_some() {
        return Err(RpcResolverError::RpcProtocolError);
    }

    response_data
        .result
        .ok_or(RpcResolverError::RpcEmptyResultField)
}

fn parse_pending_requests<'a, R: Copy + Into<RequestId> + 'static>(
    pending_requests: Vec<PendingRequest>,
    deadline: sp_runtime::offchain::Timestamp,
    request_id: R,
    network_type: &'a NetworkType,
) -> impl Iterator<Item = NetworkResponseType> + 'a {
    PendingRequest::try_wait_all(pending_requests, Some(deadline))
        .into_iter()
        .filter_map(move |wait_result| {
            let http_result = wait_result
                .inspect_err(|_| {
                    log::error!(
                        target: LOG_TARGET,
                        "🕸️ Error during request preparation: {:?}",
                        WeavingError::<u32>::DeadlineReached,
                    )
                })
                .ok()?;

            parse_response(http_result, request_id.into(), network_type)
                .inspect_err(|err| {
                    log::error!(
                        target: LOG_TARGET,
                        "🕸️ Error during response parsing: {:?}",
                        err,
                    )
                })
                .ok()
        })
}

impl NetworkRpcResolver<WeavingError<u32>, ExternalBlockNumber, H256, RequestId, WeavingSession>
    for NetworkType
{
    fn get_block_request_body(&self, id: u64) -> Vec<u8> {
        let method = match self {
            NetworkType::Evm => "eth_blockNumber",
            NetworkType::Utxo => "getblockcount",
            _ => "unknown",
        };

        serde_json::to_vec(&json!({"id": id, "jsonrpc": "2.0", "method": method}))
            .unwrap_or_else(|_| b"{}".to_vec())
    }

    fn get_hash_request_body(
        &self,
        id: u64,
        target_block: ExternalBlockNumber,
        weaving_session: WeavingSession,
        contract_ref: &[u8],
        selector_ref: &[u8],
    ) -> Vec<u8> {
        extern crate alloc;

        let (method, params) = match self {
            NetworkType::Evm => {
                let contract_hex = hex::encode(contract_ref);
                let selector_hex = hex::encode(selector_ref);

                let encoded_session = alloc::format!("{:064x}", weaving_session);
                let encoded_block = alloc::format!("{:064x}", target_block);

                let to_field = alloc::format!("0x{}", contract_hex);
                let data_field =
                    alloc::format!("0x{}{}{}", selector_hex, encoded_session, encoded_block,);

                (
                    "eth_call",
                    json!([{"to": to_field, "data": data_field}, "latest"]),
                )
            }
            NetworkType::Utxo => ("getblockhash", json!([target_block])),
            _ => ("unknown", json!([])),
        };

        serde_json::to_vec(&json!({"id": id, "jsonrpc": "2.0", "method": method, "params": params}))
            .unwrap_or_else(|_| b"{}".to_vec())
    }

    fn parse_block_requests(
        &self,
        pending_requests: Vec<PendingRequest>,
        deadline: sp_runtime::offchain::Timestamp,
        request_id: RequestId,
    ) -> Vec<ExternalBlockNumber> {
        parse_pending_requests(pending_requests, deadline, request_id, self)
            .filter_map(|response| match response {
                NetworkResponseType::BlockNumber(block) => Some(block),
                NetworkResponseType::TreeRoot(_, _) => None,
            })
            .collect()
    }

    fn parse_hash_requests(
        &self,
        pending_requests: Vec<PendingRequest>,
        deadline: sp_runtime::offchain::Timestamp,
        request_id: RequestId,
    ) -> Vec<(H256, WeavingSession)> {
        parse_pending_requests(pending_requests, deadline, request_id, self)
            .filter_map(|response| match response {
                NetworkResponseType::TreeRoot(hash, next) => Some((hash, next)),
                NetworkResponseType::BlockNumber(_) => None,
            })
            .collect()
    }
}

#[derive(Clone, PartialEq, Deserialize)]
struct NetworkResponse {
    pub id: RequestId,
    pub error: Option<serde_json::Value>,
    pub result: Option<NetworkResponseType>,
}

impl From<EvmResponse> for NetworkResponse {
    fn from(evm_response: EvmResponse) -> Self {
        Self {
            id: evm_response.id,
            error: evm_response.error,
            result: evm_response.result.map(Into::into),
        }
    }
}

impl From<UtxoResponse> for NetworkResponse {
    fn from(utxo_response: UtxoResponse) -> Self {
        Self {
            id: utxo_response.id,
            error: utxo_response.error,
            result: utxo_response.result.map(Into::into),
        }
    }
}

#[derive(Clone, PartialEq, Deserialize)]
enum NetworkResponseType {
    BlockNumber(ExternalBlockNumber),
    TreeRoot(H256, WeavingSession),
}

impl From<EvmResponseType> for NetworkResponseType {
    fn from(evm_type: EvmResponseType) -> Self {
        match evm_type {
            EvmResponseType::BlockNumber(block) => Self::BlockNumber(block),
            EvmResponseType::TreeRoot(root, next) => Self::TreeRoot(root, next),
        }
    }
}

impl From<UtxoResponseType> for NetworkResponseType {
    fn from(utxo_type: UtxoResponseType) -> Self {
        match utxo_type {
            UtxoResponseType::BlockNumber(block) => Self::BlockNumber(block),
            UtxoResponseType::TreeRoot(root) => Self::TreeRoot(root, WeavingSession::MAX),
        }
    }
}

#[derive(Deserialize, Eq, PartialEq, Clone)]
struct EvmResponse {
    pub id: RequestId,
    pub error: Option<serde_json::Value>,
    pub result: Option<EvmResponseType>,
}

#[derive(Deserialize, Eq, PartialEq, Clone)]
struct UtxoResponse {
    pub id: RequestId,
    pub error: Option<serde_json::Value>,
    pub result: Option<UtxoResponseType>,
}

#[derive(Deserialize, Clone, PartialEq, Eq)]
#[serde(untagged)]
enum EvmResponseType {
    #[serde(deserialize_with = "de_hex_string_to_block_number")]
    BlockNumber(ExternalBlockNumber),
    #[serde(deserialize_with = "de_contract_tuple")]
    TreeRoot(H256, WeavingSession),
}

#[derive(Deserialize, Clone, PartialEq, Eq)]
#[serde(untagged)]
enum UtxoResponseType {
    BlockNumber(ExternalBlockNumber),
    #[serde(deserialize_with = "de_string_to_h256")]
    TreeRoot(H256),
}

fn de_hex_string_to_block_number<'de, D>(de: D) -> Result<ExternalBlockNumber, D::Error>
where
    D: Deserializer<'de>,
{
    let s: &'de str = Deserialize::deserialize(de)?;

    let clean_hex = s.strip_prefix("0x").unwrap_or(s);
    let trimmed_hex = clean_hex.trim_start_matches('0');

    if trimmed_hex.is_empty() {
        return Ok(Default::default());
    }

    ExternalBlockNumber::from_str_radix(trimmed_hex, 16)
        .map_err(|_| serde::de::Error::custom("Failed to parse block number hex"))
}

fn de_string_to_h256<'de, D>(de: D) -> Result<H256, D::Error>
where
    D: Deserializer<'de>,
{
    let s: &'de str = Deserialize::deserialize(de)?;
    let clean_hex = s.strip_prefix("0x").unwrap_or(s);

    let mut bytes = [0u8; 32];
    hex::decode_to_slice(clean_hex, &mut bytes)
        .map_err(|_| serde::de::Error::custom("Failed to parse hex string into H256"))?;

    Ok(H256::from(bytes))
}

fn de_contract_tuple<'de, D>(de: D) -> Result<(H256, WeavingSession), D::Error>
where
    D: Deserializer<'de>,
{
    let s: &str = Deserialize::deserialize(de)?;
    let clean_hex = s.strip_prefix("0x").unwrap_or(s);

    if clean_hex.len() != 128 {
        return Err(serde::de::Error::custom(
            "Invalid hex string length for (bytes32, uint256)",
        ));
    }

    let (bytes32_hex, uint256_hex) = clean_hex.split_at(64);

    let mut bytes = [0u8; 32];
    hex::decode_to_slice(bytes32_hex, &mut bytes)
        .map_err(|_| serde::de::Error::custom("Failed to parse hex string into H256"))?;

    let root_hash = H256::from(bytes);

    let uint256_val = U256::from_str_radix(uint256_hex, 16)
        .map_err(|_| serde::de::Error::custom("Failed to parse uint256"))?;

    let next_weaving_session: WeavingSession = uint256_val
        .try_into()
        .map_err(|_| serde::de::Error::custom("EVM uint256 value overflow bounds"))?;

    Ok((root_hash, next_weaving_session))
}

#[cfg(test)]
mod parsing_tests {
    use super::*;
    use serde::Deserialize;
    use sp_core::H256;

    #[derive(Deserialize)]
    struct TestBlock {
        #[serde(deserialize_with = "de_hex_string_to_block_number")]
        val: ExternalBlockNumber,
    }

    #[derive(Deserialize)]
    struct TestHash {
        #[serde(deserialize_with = "de_string_to_h256")]
        val: H256,
    }

    #[derive(Deserialize)]
    struct TestTuple {
        #[serde(deserialize_with = "de_contract_tuple")]
        val: (H256, WeavingSession),
    }

    #[test]
    fn test_de_hex_string_to_block_number_success() {
        let json = r#"{"val": "0x64"}"#;
        let parsed: TestBlock = serde_json::from_str(json).unwrap();
        assert_eq!(parsed.val, 100);

        let json_zeros = r#"{"val": "0x00000a"}"#;
        let parsed_zeros: TestBlock = serde_json::from_str(json_zeros).unwrap();
        assert_eq!(parsed_zeros.val, 10);

        let json_zero = r#"{"val": "0x0"}"#;
        let parsed_zero: TestBlock = serde_json::from_str(json_zero).unwrap();
        assert_eq!(parsed_zero.val, 0);
    }

    #[test]
    fn test_de_hex_string_to_block_number_invalid() {
        let json = r#"{"val": "0x6G"}"#;
        let result: Result<TestBlock, _> = serde_json::from_str(json);
        assert!(result.is_err());
    }

    #[test]
    fn test_de_string_to_h256_success() {
        let raw_hash = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
        let json = format!(r#"{{"val": "0x{}"}}"#, raw_hash);

        let parsed: TestHash = serde_json::from_str(&json).unwrap();
        assert_eq!(parsed.val, H256::repeat_byte(0xAA));
    }

    #[test]
    fn test_de_string_to_h256_invalid_length() {
        let json = r#"{"val": "0x00aa"}"#;
        let result: Result<TestHash, _> = serde_json::from_str(json);
        assert!(result.is_err());
    }

    #[test]
    fn test_de_contract_tuple_success() {
        let hash_part = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
        let session_part = "000000000000000000000000000000000000000000000000000000000000002a";
        let json = format!(r#"{{"val": "0x{}{}"}}"#, hash_part, session_part);

        let parsed: TestTuple = serde_json::from_str(&json).unwrap();

        assert_eq!(parsed.val.0, H256::repeat_byte(0xBB));
        assert_eq!(parsed.val.1, 42u64);
    }

    #[test]
    fn test_de_contract_tuple_invalid_length() {
        let json = r#"{"val": "0xbbbb002a"}"#;
        let result: Result<TestTuple, _> = serde_json::from_str(json);

        assert!(result.is_err());
        let err_msg = result.err().unwrap().to_string();
        assert!(err_msg.contains("Invalid hex string length"));
    }

    #[test]
    fn test_de_contract_tuple_overflow() {
        let hash_part = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
        let session_overflow = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
        let json = format!(r#"{{"val": "0x{}{}"}}"#, hash_part, session_overflow);

        let result: Result<TestTuple, _> = serde_json::from_str(&json);

        assert!(result.is_err());
        let err_msg = result.err().unwrap().to_string();
        assert!(err_msg.contains("EVM uint256 value overflow bounds"));
    }
}
