//! RPC API method handlers.

use jsonrpsee::core::RpcResult;
use jsonrpsee::proc_macros::rpc;

use crate::types::*;

/// Block-related RPC methods.
#[rpc(server, client, namespace = "synor")]
pub trait BlockApi {
    /// Gets a block by hash.
    #[method(name = "getBlock")]
    async fn get_block(&self, hash: String, include_txs: Option<bool>) -> RpcResult<RpcBlock>;

    /// Gets blocks by hashes.
    #[method(name = "getBlocks")]
    async fn get_blocks(
        &self,
        hashes: Vec<String>,
        include_txs: Option<bool>,
    ) -> RpcResult<Vec<RpcBlock>>;

    /// Gets the current tips (DAG tips).
    #[method(name = "getTips")]
    async fn get_tips(&self) -> RpcResult<Vec<String>>;

    /// Gets the virtual selected parent chain.
    #[method(name = "getVirtualChainFromBlock")]
    async fn get_virtual_chain_from_block(
        &self,
        start_hash: String,
        include_accepted_tx_ids: Option<bool>,
    ) -> RpcResult<VirtualChainResponse>;

    /// Gets block count.
    #[method(name = "getBlockCount")]
    async fn get_block_count(&self) -> RpcResult<BlockCountResponse>;

    /// Gets block DAG info.
    #[method(name = "getBlockDagInfo")]
    async fn get_block_dag_info(&self) -> RpcResult<RpcDagInfo>;

    /// Gets headers between two blocks.
    #[method(name = "getHeaders")]
    async fn get_headers(
        &self,
        start_hash: String,
        limit: u64,
        is_ascending: bool,
    ) -> RpcResult<Vec<RpcBlockHeader>>;

    /// Gets blocks by blue score.
    /// Returns blocks with blue score closest to the specified value.
    /// In a DAG, multiple blocks may exist at similar blue scores.
    #[method(name = "getBlocksByBlueScore")]
    async fn get_blocks_by_blue_score(
        &self,
        blue_score: u64,
        include_txs: Option<bool>,
    ) -> RpcResult<Vec<RpcBlock>>;
}

/// Virtual chain response.
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VirtualChainResponse {
    /// Removed chain block hashes.
    pub removed_chain_block_hashes: Vec<String>,
    /// Added chain block hashes.
    pub added_chain_block_hashes: Vec<String>,
    /// Accepted transaction IDs.
    pub accepted_transaction_ids: Vec<AcceptedTransactionIds>,
}

/// Accepted transaction IDs for a block.
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AcceptedTransactionIds {
    /// Accepting block hash.
    pub accepting_block_hash: String,
    /// Accepted transaction IDs.
    pub accepted_transaction_ids: Vec<String>,
}

/// Block count response.
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BlockCountResponse {
    /// Block count.
    pub block_count: u64,
    /// Header count.
    pub header_count: u64,
}

/// Transaction-related RPC methods.
#[rpc(server, client, namespace = "synor")]
pub trait TransactionApi {
    /// Submits a transaction.
    #[method(name = "submitTransaction")]
    async fn submit_transaction(
        &self,
        transaction: RpcTransaction,
        allow_orphan: Option<bool>,
    ) -> RpcResult<SubmitTransactionResponse>;

    /// Gets a transaction by ID.
    #[method(name = "getTransaction")]
    async fn get_transaction(&self, tx_id: String) -> RpcResult<RpcTransaction>;

    /// Gets mempool entries.
    #[method(name = "getMempoolEntries")]
    async fn get_mempool_entries(
        &self,
        include_orphan_pool: Option<bool>,
        filter_tx_in_addresses: Option<bool>,
    ) -> RpcResult<Vec<RpcMempoolEntry>>;

    /// Gets mempool entry by transaction ID.
    #[method(name = "getMempoolEntry")]
    async fn get_mempool_entry(
        &self,
        tx_id: String,
        include_orphan_pool: Option<bool>,
    ) -> RpcResult<RpcMempoolEntry>;

    /// Gets mempool entries by addresses.
    #[method(name = "getMempoolEntriesByAddresses")]
    async fn get_mempool_entries_by_addresses(
        &self,
        addresses: Vec<String>,
        include_orphan_pool: Option<bool>,
        filter_tx_in_addresses: Option<bool>,
    ) -> RpcResult<Vec<MempoolEntriesByAddress>>;

    /// Estimates transaction fee.
    #[method(name = "estimateFee")]
    async fn estimate_fee(&self, transaction: RpcTransaction) -> RpcResult<u64>;

    /// Gets fee estimate buckets.
    #[method(name = "getFeeEstimate")]
    async fn get_fee_estimate(&self) -> RpcResult<RpcFeeEstimate>;
}

/// Mempool entries by address.
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct MempoolEntriesByAddress {
    /// Address.
    pub address: String,
    /// Sending entries.
    pub sending: Vec<RpcMempoolEntry>,
    /// Receiving entries.
    pub receiving: Vec<RpcMempoolEntry>,
}

/// Submit transaction response.
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SubmitTransactionResponse {
    /// Transaction ID.
    pub transaction_id: String,
}

/// UTXO-related RPC methods.
#[rpc(server, client, namespace = "synor")]
pub trait UtxoApi {
    /// Gets UTXOs by addresses.
    #[method(name = "getUtxosByAddresses")]
    async fn get_utxos_by_addresses(&self, addresses: Vec<String>) -> RpcResult<Vec<RpcUtxo>>;

    /// Gets balance by address.
    #[method(name = "getBalanceByAddress")]
    async fn get_balance_by_address(&self, address: String) -> RpcResult<RpcBalance>;

    /// Gets balances by addresses.
    #[method(name = "getBalancesByAddresses")]
    async fn get_balances_by_addresses(&self, addresses: Vec<String>)
        -> RpcResult<Vec<RpcBalance>>;

    /// Gets coin supply.
    #[method(name = "getCoinSupply")]
    async fn get_coin_supply(&self) -> RpcResult<RpcCoinSupply>;
}

/// Mining-related RPC methods.
#[rpc(server, client, namespace = "synor")]
pub trait MiningApi {
    /// Gets a block template for mining.
    #[method(name = "getBlockTemplate")]
    async fn get_block_template(
        &self,
        pay_address: String,
        extra_data: Option<String>,
    ) -> RpcResult<RpcBlockTemplate>;

    /// Submits a mined block.
    #[method(name = "submitBlock")]
    async fn submit_block(
        &self,
        request: RpcSubmitBlockRequest,
    ) -> RpcResult<RpcSubmitBlockResponse>;

    /// Gets current network hashrate.
    #[method(name = "getNetworkHashrate")]
    async fn get_network_hashrate(&self) -> RpcResult<NetworkHashrateResponse>;

    /// Gets current difficulty.
    #[method(name = "getDifficulty")]
    async fn get_difficulty(&self) -> RpcResult<f64>;
}

/// Network hashrate response.
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct NetworkHashrateResponse {
    /// Network hashrate in H/s.
    pub hashrate: f64,
    /// Difficulty.
    pub difficulty: f64,
    /// Block rate (blocks per second).
    pub block_rate: f64,
}

/// Network-related RPC methods.
#[rpc(server, client, namespace = "net")]
pub trait NetworkApi {
    /// Gets network info.
    #[method(name = "getInfo")]
    async fn get_info(&self) -> RpcResult<RpcNetworkInfo>;

    /// Gets connected peer info.
    #[method(name = "getPeerInfo")]
    async fn get_peer_info(&self) -> RpcResult<RpcConnectedPeers>;

    /// Gets server info.
    #[method(name = "getServerInfo")]
    async fn get_server_info(&self) -> RpcResult<RpcServerInfo>;

    /// Adds a peer.
    #[method(name = "addPeer")]
    async fn add_peer(&self, address: String, is_permanent: Option<bool>) -> RpcResult<bool>;

    /// Bans a peer.
    #[method(name = "banPeer")]
    async fn ban_peer(&self, address: String) -> RpcResult<bool>;

    /// Unbans a peer.
    #[method(name = "unbanPeer")]
    async fn unban_peer(&self, address: String) -> RpcResult<bool>;

    /// Gets banned peer list.
    #[method(name = "getBannedPeers")]
    async fn get_banned_peers(&self) -> RpcResult<Vec<BannedPeer>>;

    /// Pings the server.
    #[method(name = "ping")]
    async fn ping(&self) -> RpcResult<()>;

    /// Shuts down the node.
    #[method(name = "shutdown")]
    async fn shutdown(&self) -> RpcResult<()>;
}

/// Banned peer info.
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BannedPeer {
    /// Peer address.
    pub address: String,
    /// Ban reason.
    pub reason: String,
    /// Ban time.
    pub ban_time: u64,
    /// Ban duration.
    pub ban_duration: u64,
}

/// Contract-related RPC methods.
#[rpc(server, client, namespace = "contract")]
pub trait ContractApi {
    /// Deploys a contract.
    #[method(name = "deploy")]
    async fn deploy(&self, request: DeployContractRequest) -> RpcResult<DeployContractResponse>;

    /// Calls a contract method.
    #[method(name = "call")]
    async fn call(&self, request: CallContractRequest) -> RpcResult<CallContractResponse>;

    /// Estimates gas for a contract call.
    #[method(name = "estimateGas")]
    async fn estimate_gas(&self, request: CallContractRequest) -> RpcResult<u64>;

    /// Gets contract code.
    #[method(name = "getCode")]
    async fn get_code(&self, address: String) -> RpcResult<String>;

    /// Gets contract storage value.
    #[method(name = "getStorageAt")]
    async fn get_storage_at(&self, address: String, key: String) -> RpcResult<String>;
}

/// Deploy contract request.
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DeployContractRequest {
    /// Contract bytecode (hex).
    pub code: String,
    /// Constructor arguments (hex).
    pub args: Option<String>,
    /// Initial value to send.
    pub value: Option<u64>,
    /// Gas limit.
    pub gas_limit: u64,
    /// Deployer address.
    pub from: String,
}

/// Deploy contract response.
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct DeployContractResponse {
    /// Transaction ID.
    pub transaction_id: String,
    /// Contract address.
    pub contract_address: String,
}

/// Call contract request.
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CallContractRequest {
    /// Target contract address.
    pub to: String,
    /// Method to call.
    pub method: String,
    /// Arguments (hex).
    pub args: Option<String>,
    /// Value to send.
    pub value: Option<u64>,
    /// Gas limit.
    pub gas_limit: Option<u64>,
    /// Caller address.
    pub from: Option<String>,
}

/// Call contract response.
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CallContractResponse {
    /// Return data (hex).
    pub result: String,
    /// Gas used.
    pub gas_used: u64,
    /// Logs emitted.
    pub logs: Vec<ContractLog>,
}

/// Contract log.
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ContractLog {
    /// Contract address.
    pub address: String,
    /// Log topics.
    pub topics: Vec<String>,
    /// Log data (hex).
    pub data: String,
}

/// Subscription-related RPC methods (WebSocket only).
#[rpc(server, client, namespace = "synor")]
pub trait SubscriptionApi {
    /// Subscribes to new block headers.
    #[subscription(name = "subscribeNewBlockHeaders", item = RpcBlockHeader)]
    async fn subscribe_new_block_headers(&self) -> jsonrpsee::core::SubscriptionResult;

    /// Subscribes to virtual chain changes.
    #[subscription(name = "subscribeVirtualChainChanged", item = VirtualChainResponse)]
    async fn subscribe_virtual_chain_changed(
        &self,
        include_accepted_tx_ids: Option<bool>,
    ) -> jsonrpsee::core::SubscriptionResult;

    /// Subscribes to UTXO changes for addresses.
    #[subscription(name = "subscribeUtxosChanged", item = UtxosChangedNotification)]
    async fn subscribe_utxos_changed(
        &self,
        addresses: Vec<String>,
    ) -> jsonrpsee::core::SubscriptionResult;

    /// Subscribes to virtual DAA score changes.
    #[subscription(name = "subscribeVirtualDaaScoreChanged", item = u64)]
    async fn subscribe_virtual_daa_score_changed(&self) -> jsonrpsee::core::SubscriptionResult;
}

/// UTXO change notification.
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UtxosChangedNotification {
    /// Added UTXOs.
    pub added: Vec<RpcUtxo>,
    /// Removed UTXOs.
    pub removed: Vec<RpcOutpoint>,
}

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

    #[test]
    fn test_submit_response_serialization() {
        let response = SubmitTransactionResponse {
            transaction_id: "abc123".to_string(),
        };

        let json = serde_json::to_string(&response).unwrap();
        assert!(json.contains("transactionId"));
    }

    #[test]
    fn test_deploy_request() {
        let request = DeployContractRequest {
            code: "0x1234".to_string(),
            args: None,
            value: Some(1000),
            gas_limit: 100000,
            from: "synor:abc123".to_string(),
        };

        let json = serde_json::to_string(&request).unwrap();
        assert!(json.contains("gasLimit"));
    }
}
