// Copyright (c) Subzero Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

//! Rialo blockchain data types and RPC client models
//!
//! This module re-exports types from rialo-api-types and provides compatibility types
//! for the CDK's RPC interface. The rialo-api-types crate contains the canonical
//! definitions for all RPC request and response types.

// Use solana-sdk types when bincode feature is enabled (non-WASM builds)
#[cfg(feature = "bincode")]
pub use rialo_s_sdk::hash::Hash;
#[cfg(feature = "bincode")]
pub use rialo_s_sdk::pubkey::Pubkey;
#[cfg(feature = "bincode")]
pub use rialo_s_sdk::signature::Signature;
#[cfg(feature = "bincode")]
pub use rialo_s_sdk::signature::{Keypair, Signer};
pub use rialo_shared_types::Subscription;
#[cfg(feature = "bincode")]
pub use rialo_types::crypto::PublicKey as SecretSharingPubkey;
use serde::{Deserialize, Serialize};

// Define our own types when bincode feature is disabled (WASM builds)
#[cfg(not(feature = "bincode"))]
pub use self::native_types::{Hash, Pubkey, Signature};

#[cfg(not(feature = "bincode"))]
mod native_types {
    use std::{fmt, str::FromStr};

    use super::*;

    /// A 32-byte hash value
    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
    pub struct Hash([u8; 32]);

    impl Hash {
        /// Create a new Hash from 32 bytes
        pub const fn new(bytes: [u8; 32]) -> Self {
            Self(bytes)
        }

        /// Creates a new Hash from 32 bytes.
        pub const fn new_from_array(bytes: [u8; 32]) -> Self {
            Self(bytes)
        }

        /// Get the hash as a byte array
        pub const fn to_bytes(self) -> [u8; 32] {
            self.0
        }

        /// Get the hash as a byte slice
        pub fn as_bytes(&self) -> &[u8; 32] {
            &self.0
        }
    }

    impl fmt::Display for Hash {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            write!(f, "{}", bs58::encode(self.0).into_string())
        }
    }

    impl AsRef<[u8; 32]> for Hash {
        fn as_ref(&self) -> &[u8; 32] {
            &self.0
        }
    }

    impl AsRef<[u8]> for Hash {
        fn as_ref(&self) -> &[u8] {
            &self.0
        }
    }

    impl FromStr for Hash {
        type Err = crate::error::RialoError;

        fn from_str(s: &str) -> Result<Self, Self::Err> {
            let bytes = bs58::decode(s).into_vec().map_err(|_| {
                crate::error::RialoError::InvalidInput(format!("Invalid hash format: {}", s))
            })?;
            if bytes.len() != 32 {
                return Err(crate::error::RialoError::InvalidInput(format!(
                    "Hash must be 32 bytes, got {}",
                    bytes.len()
                )));
            }
            let mut array = [0u8; 32];
            array.copy_from_slice(&bytes);
            Ok(Hash(array))
        }
    }

    /// A 32-byte public key
    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
    pub struct Pubkey([u8; 32]);

    impl Pubkey {
        /// Create a new Pubkey from 32 bytes
        pub const fn new(bytes: [u8; 32]) -> Self {
            Self(bytes)
        }

        /// Creates a new Pubkey from 32 bytes.
        pub const fn new_from_array(bytes: [u8; 32]) -> Self {
            Self(bytes)
        }

        /// Get the pubkey as a byte array
        pub const fn to_bytes(self) -> [u8; 32] {
            self.0
        }

        /// Get the pubkey as a byte array
        pub const fn from_bytes(bytes: [u8; 32]) -> Self {
            Self::new(bytes)
        }

        /// Get the pubkey as a byte slice
        pub fn as_bytes(&self) -> &[u8; 32] {
            &self.0
        }

        /// Create a unique random pubkey for testing
        pub fn new_unique() -> Self {
            use rand::Rng;
            let mut rng = rand::thread_rng();
            let bytes: [u8; 32] = rng.gen();
            Self(bytes)
        }
    }

    impl fmt::Display for Pubkey {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            write!(f, "{}", bs58::encode(self.0).into_string())
        }
    }

    impl FromStr for Pubkey {
        type Err = crate::error::RialoError;

        fn from_str(s: &str) -> Result<Self, Self::Err> {
            let bytes = bs58::decode(s).into_vec().map_err(|_| {
                crate::error::RialoError::InvalidInput(format!("Invalid pubkey format: {}", s))
            })?;
            if bytes.len() != 32 {
                return Err(crate::error::RialoError::InvalidInput(format!(
                    "Pubkey must be 32 bytes, got {}",
                    bytes.len()
                )));
            }
            let mut array = [0u8; 32];
            array.copy_from_slice(&bytes);
            Ok(Pubkey(array))
        }
    }

    impl From<[u8; 32]> for Pubkey {
        fn from(bytes: [u8; 32]) -> Self {
            Self(bytes)
        }
    }

    impl TryFrom<&[u8]> for Pubkey {
        type Error = crate::error::RialoError;

        fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
            if bytes.len() != 32 {
                return Err(crate::error::RialoError::InvalidInput(format!(
                    "Pubkey must be 32 bytes, got {}",
                    bytes.len()
                )));
            }
            let mut array = [0u8; 32];
            array.copy_from_slice(bytes);
            Ok(Pubkey(array))
        }
    }

    impl AsRef<[u8; 32]> for Pubkey {
        fn as_ref(&self) -> &[u8; 32] {
            &self.0
        }
    }

    impl AsRef<[u8]> for Pubkey {
        fn as_ref(&self) -> &[u8] {
            &self.0
        }
    }

    impl TryFrom<Vec<u8>> for Pubkey {
        type Error = crate::error::RialoError;

        fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
            Self::try_from(bytes.as_slice())
        }
    }

    /// A 64-byte signature
    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
    pub struct Signature([u8; 64]);

    impl Signature {
        /// Create a new Signature from 64 bytes
        pub const fn new(bytes: [u8; 64]) -> Self {
            Self(bytes)
        }

        /// Get the signature as a byte array
        pub const fn to_bytes(self) -> [u8; 64] {
            self.0
        }

        /// Get the signature as a byte slice
        pub fn as_bytes(&self) -> &[u8; 64] {
            &self.0
        }
    }

    impl fmt::Display for Signature {
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
            write!(f, "{}", bs58::encode(self.0).into_string())
        }
    }

    impl FromStr for Signature {
        type Err = crate::error::RialoError;

        fn from_str(s: &str) -> Result<Self, Self::Err> {
            let bytes = bs58::decode(s).into_vec().map_err(|_| {
                crate::error::RialoError::InvalidInput(format!("Invalid signature format: {}", s))
            })?;
            if bytes.len() != 64 {
                return Err(crate::error::RialoError::InvalidInput(format!(
                    "Signature must be 64 bytes, got {}",
                    bytes.len()
                )));
            }
            let mut array = [0u8; 64];
            array.copy_from_slice(&bytes);
            Ok(Signature(array))
        }
    }

    impl Serialize for Signature {
        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where
            S: serde::Serializer,
        {
            serializer.serialize_str(&bs58::encode(self.0).into_string())
        }
    }

    impl<'de> Deserialize<'de> for Signature {
        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where
            D: serde::Deserializer<'de>,
        {
            let s = String::deserialize(deserializer)?;
            Self::from_str(&s).map_err(serde::de::Error::custom)
        }
    }
}

// Re-export types from rialo-api-types
pub use rialo_api_types::messages::{
    get_account_info::{AccountInfoResponse, DataSliceConfig, GetAccountInfoConfig},
    get_accounts_by_owner::GetAccountsByOwnerResponse,
    get_balance::GetBalanceResponse,
    get_block_height::RequestBlockHeightConfig,
    get_connected_full_nodes::GetConnectedFullNodesResponse,
    get_epoch_info::{EpochInfoResponse, GetEpochInfoConfig},
    get_fee_for_message::GetFeeForMessageResponse,
    get_health::GetHealthResponse,
    get_multiple_accounts::MultipleAccountsResponse,
    get_secret_sharing_pubkey::GetSecretSharingPubkeyResponse,
    get_signature_statuses::{
        GetIndividualSignatureStatus, GetSignatureStatusesRequest, GetSignatureStatusesResponse,
    },
    get_signatures_for_address::{
        GetSignaturesForAddressConfig, GetSignaturesForAddressRequest,
        GetSignaturesForAddressResponse, SignatureInfo,
    },
    get_subscription::{GetSubscriptionRequest, GetSubscriptionResponse},
    get_transaction::{
        GetTransactionResponse, Instruction, MessageHeader, Transaction, TransactionMessage,
        TransactionStatusMetadata,
    },
    get_transaction_count::GetTransactionCountResponse,
    get_transactions::{
        GetTransactionsConfig, GetTransactionsRequest, GetTransactionsResponse, TransactionInfo,
    },
    get_triggered_transactions::{GetTriggeredTransactionsResponse, TriggeredTransaction},
    get_validator_health::GetValidatorHealthResponse,
    get_workflow_lineage::{
        EventData, GetWorkflowLineageRequest, GetWorkflowLineageResponse, TimestampRange,
        TransactionNodeData, TriggerInfo, TruncationReason, WorkflowLineage, WorkflowNode,
    },
    is_blockhash_valid::IsBlockhashValidResponse,
    request_airdrop::{RequestAirdropRequest, RequestAirdropResponse},
    rpc_response_context::RpcResponseContext,
    send_transaction::{SendTransactionConfig, TransactionEncoding as RpcTransactionEncoding},
    submit_epoch_change::{
        SubmitEpochChangeRequest, SubmitEpochChangeResponse, ValidatorInfoRequest,
    },
};
// Import shared types from rialo-shared-types
use rialo_shared_types::AccountInfoResponseValue;

// Type alias for backward compatibility - map to the shared type
pub type AccountInfo = AccountInfoResponseValue;

/// Supported transaction encodings.
#[derive(serde::Serialize)]
#[serde(rename_all = "lowercase")]
pub enum TransactionEncoding {
    Base64,
    Base58,
}

// Options for sendTransaction - create compatibility wrapper
#[derive(serde::Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SendTransactionOptions {
    /// Encoding used for the transaction data.
    pub encoding: TransactionEncoding,
    /// When true, skip the preflight transaction checks.
    pub skip_preflight: bool,
    /// Maximum number of times for the RPC node to retry sending the transaction to the leader.
    pub max_retries: usize,
    /// Wait until the transaction has been executed before returning to caller.
    pub wait_for_execution: bool,
}

impl Default for SendTransactionOptions {
    fn default() -> Self {
        Self {
            encoding: TransactionEncoding::Base64,
            skip_preflight: false,
            max_retries: 5,
            wait_for_execution: false,
        }
    }
}

impl From<SendTransactionOptions> for SendTransactionConfig {
    fn from(options: SendTransactionOptions) -> Self {
        SendTransactionConfig {
            encoding: match options.encoding {
                TransactionEncoding::Base64 => RpcTransactionEncoding::Base64,
                TransactionEncoding::Base58 => RpcTransactionEncoding::Base58,
            },
            max_retries: Some(options.max_retries),
            min_context_slot: None,
            skip_preflight: options.skip_preflight,
            wait_for_execution: options.wait_for_execution,
        }
    }
}

// Type aliases for backward compatibility with existing CDK API
pub type Message = TransactionMessage;
pub type CompiledInstruction = Instruction;

// Create a backward compatible Transaction structure that maps to GetTransactionResponse
#[derive(Debug, Serialize, Deserialize)]
pub struct TransactionResponse {
    /// The block in which this transaction was processed
    pub block_height: u64,
    /// Metadata about the transaction execution, including fees and errors
    pub meta: TransactionStatusMetadata,
    /// The actual transaction content including signatures and instructions
    pub transaction: Transaction,
    /// Block time
    pub block_time: Option<i64>,
}

// Re-export SignatureStatus from generated types (canonical definition).
pub use crate::generated::types::SignatureStatus;
