use crate::{get_asset, get_assets, get_ddi, get_ddis, get_device, get_devices, get_hash, get_hook, get_hooks, get_region_by_id, get_region_by_ident, get_regions, get_str, get_trunk, get_trunks, insert_account, insert_account_idents, insert_region, insert_region_idents};
use cal_core::device::device::DeviceStruct;
use cal_core::{Account, Asset, Hook, Region, Trunk, DDI};
use redis::aio::MultiplexedConnection;
use redis::RedisError;
use std::collections::HashMap;

#[derive(Clone)]
pub struct RedisCache {
    pub connection: MultiplexedConnection,
}

impl RedisCache {
    /// Inserts a region into Redis.
    ///
    /// # Arguments
    /// * `value` - The region to insert
    ///
    /// # Returns
    /// * `Result<RedisCache, RedisError>` - Self for method chaining or a Redis error
    pub async fn insert_region(self, value: Region) -> Result<RedisCache, RedisError> {
        insert_region(self.connection.clone(), value).await?;
        Ok(self)
    }

    /// Inserts region identifiers into Redis.
    ///
    /// # Arguments
    /// * `items` - Pairs of (identifier, region_id) to insert
    ///
    /// # Returns
    /// * `Result<RedisCache, RedisError>` - Self for method chaining or a Redis error
    pub async fn insert_region_idents(self, items: Vec<(String, String)>) -> Result<RedisCache, RedisError> {
        insert_region_idents(self.connection.clone(), &items).await?;
        Ok(self)
    }

    /// Inserts an account into Redis.
    ///
    /// # Arguments
    /// * `value` - The account to insert
    ///
    /// # Returns
    /// * `Result<RedisCache, RedisError>` - Self for method chaining or a Redis error
    pub async fn insert_account(self, value: Account) -> Result<RedisCache, RedisError> {
        insert_account(self.connection.clone(), value).await?;
        Ok(self)
    }

    /// Inserts account identifiers into Redis.
    ///
    /// # Arguments
    /// * `items` - Pairs of (identifier, account_id) to insert
    ///
    /// # Returns
    /// * `Result<RedisCache, RedisError>` - Self for method chaining or a Redis error
    pub async fn insert_account_idents(self, items: Vec<(String, String)>) -> Result<RedisCache, RedisError> {
        insert_account_idents(self.connection.clone(), &items).await?;
        Ok(self)
    }

    /// Retrieves all devices for an account.
    ///
    /// # Arguments
    /// * `account_id` - Account ID
    ///
    /// # Returns
    /// * `Result<Vec<DeviceStruct>, RedisError>` - List of devices or a Redis error
    pub async fn get_devices(self, account_id: String) -> Result<Vec<DeviceStruct>, RedisError> {
        get_devices(self.connection.clone(), &account_id).await
    }

    /// Retrieves a specific device for an account.
    ///
    /// # Arguments
    /// * `account_id` - Account ID
    /// * `device_id` - Device ID to retrieve
    ///
    /// # Returns
    /// * `Result<Option<DeviceStruct>, RedisError>` - The device if found, None if not found, or a Redis error
    pub async fn get_device(self, account_id: String, device_id: String) -> Result<Option<DeviceStruct>, RedisError> {
        get_device(self.connection.clone(), &account_id, &device_id).await
    }

    /// Retrieves all trunks for an account.
    ///
    /// # Arguments
    /// * `account_id` - Account ID
    ///
    /// # Returns
    /// * `Result<Vec<Trunk>, RedisError>` - List of trunks or a Redis error
    pub async fn get_trunks(self, account_id: String) -> Result<Vec<Trunk>, RedisError> {
        get_trunks(self.connection.clone(), &account_id).await
    }

    /// Retrieves a specific trunk for an account.
    ///
    /// # Arguments
    /// * `account_id` - Account ID
    /// * `trunk_id` - Trunk ID to retrieve
    ///
    /// # Returns
    /// * `Result<Option<Trunk>, RedisError>` - The trunk if found, None if not found, or a Redis error
    pub async fn get_trunk(self, account_id: String, trunk_id: String) -> Result<Option<Trunk>, RedisError> {
        get_trunk(self.connection.clone(), &account_id, &trunk_id).await
    }

    /// Retrieves all DDIs for an account.
    ///
    /// # Arguments
    /// * `account_id` - Account ID
    ///
    /// # Returns
    /// * `Result<Vec<DDI>, RedisError>` - List of DDIs or a Redis error
    pub async fn get_ddis(self, account_id: String) -> Result<Vec<DDI>, RedisError> {
        get_ddis(self.connection.clone(), &account_id).await
    }

    /// Retrieves a specific DDI for an account.
    ///
    /// # Arguments
    /// * `account_id` - Account ID
    /// * `ddi_id` - DDI ID to retrieve
    ///
    /// # Returns
    /// * `Result<Option<DDI>, RedisError>` - The DDI if found, None if not found, or a Redis error
    pub async fn get_ddi(self, account_id: String, ddi_id: String) -> Result<Option<DDI>, RedisError> {
        get_ddi(self.connection.clone(), &account_id, &ddi_id).await
    }

    /// Retrieves all hooks for an account.
    ///
    /// # Arguments
    /// * `account_id` - Account ID
    ///
    /// # Returns
    /// * `Result<Vec<Hook>, RedisError>` - List of hooks or a Redis error
    pub async fn get_hooks(self, account_id: String) -> Result<Vec<Hook>, RedisError> {
        get_hooks(self.connection.clone(), &account_id).await
    }

    /// Retrieves a specific hook for an account.
    ///
    /// # Arguments
    /// * `account_id` - Account ID
    /// * `hook_id` - Hook ID to retrieve
    ///
    /// # Returns
    /// * `Result<Option<Hook>, RedisError>` - The hook if found, None if not found, or a Redis error
    pub async fn get_hook(self, account_id: String, hook_id: String) -> Result<Option<Hook>, RedisError> {
        get_hook(self.connection.clone(), &account_id, &hook_id).await
    }

    /// Retrieves all assets for an account.
    ///
    /// # Arguments
    /// * `account_id` - Account ID
    ///
    /// # Returns
    /// * `Result<Vec<Asset>, RedisError>` - List of assets or a Redis error
    pub async fn get_assets(self, account_id: String) -> Result<Vec<Asset>, RedisError> {
        get_assets(self.connection.clone(), &account_id).await
    }

    /// Retrieves a specific asset for an account.
    ///
    /// # Arguments
    /// * `account_id` - Account ID
    /// * `asset_id` - Asset ID to retrieve
    ///
    /// # Returns
    /// * `Result<Option<Asset>, RedisError>` - The asset if found, None if not found, or a Redis error
    pub async fn get_asset(self, account_id: String, asset_id: String) -> Result<Option<Asset>, RedisError> {
        get_asset(self.connection.clone(), &account_id, &asset_id).await
    }

    /// Retrieves a region by its identifier.
    ///
    /// # Arguments
    /// * `key` - Region identifier (ID, name, etc.)
    ///
    /// # Returns
    /// * `Result<Option<Region>, RedisError>` - Region if found, None if not found, or a Redis error
    pub async fn get_region_by_ident(self, key: &str) -> Result<Option<Region>, RedisError> {
        get_region_by_ident(self.connection.clone(), key).await
    }

    /// Retrieves a region by its ID.
    ///
    /// # Arguments
    /// * `id` - Region ID
    ///
    /// # Returns
    /// * `Result<Option<Region>, RedisError>` - Region if found, None if not found, or a Redis error
    pub async fn get_region_by_id(self, id: &str) -> Result<Option<Region>, RedisError> {
        get_region_by_id(self.connection.clone(), id).await
    }

    /// Retrieves all regions.
    ///
    /// # Returns
    /// * `Result<HashMap<String, Region>, RedisError>` - Map of region IDs to regions
    pub async fn get_regions(self) -> Result<Vec<Region>, RedisError> {
        get_regions(self.connection.clone()).await
    }

    /// Retrieves a string value from Redis.
    ///
    /// # Arguments
    /// * `key` - Redis key
    ///
    /// # Returns
    /// * `Result<Option<String>, RedisError>` - String value if found, None if not found, or a Redis error
    pub async fn get_str(self, key: &str) -> Result<Option<String>, RedisError> {
        get_str(self.connection.clone(), key).await
    }

    /// Retrieves a field from a Redis hash.
    ///
    /// # Arguments
    /// * `key` - Redis hash key
    /// * `field` - Field to retrieve
    ///
    /// # Returns
    /// * `Result<Option<String>, RedisError>` - Field value if found, None if not found, or a Redis error
    pub async fn get_hash(self, key: &str, field: &str) -> Result<Option<String>, RedisError> {
        get_hash(self.connection.clone(), key, field).await
    }
}