//! Virtual garage repository implementation for item data persistence operations.
//!
//! This module provides the data access layer for virtual garage management.
//! Each player's virtual garage is stored as a Redis hash with six fields:
//! - cars: JSON array of car classnames
//! - armor: JSON array of armor classnames
//! - helis: JSON array of helis classnames
//! - planes: JSON array of plane classnames
//! - naval: JSON array of naval classnames
//! - other: JSON array of other classnames

use forge_models::VGarage;
use forge_shared::{RedisClient, parse_json_value, parse_redis_value};

/// Repository trait defining the contract for virtual garage data operations.
pub trait VGarageRepository: Send + Sync {
    /// Creates a new virtual garage for a player
    fn create(&self, uid: &str, garage: &VGarage) -> Result<(), String>;

    /// Updates an existing virtual garage with new item data
    fn update(&self, uid: &str, garage: &VGarage) -> Result<(), String>;

    /// Retrieves a player's virtual garage
    fn fetch(&self, uid: &str) -> Result<Option<VGarage>, String>;

    /// Retrieves a specific field from a player's virtual garage
    /// Fields: "cars", "armor", "helis", "planes", "naval", "other"
    fn get(&self, uid: &str, field: &str) -> Result<Vec<String>, String>;

    /// Deletes a player's virtual garage (all items)
    fn delete(&self, uid: &str) -> Result<(), String>;

    /// Checks if a player has a virtual garage
    fn exists(&self, uid: &str) -> Result<bool, String>;
}

/// Redis-based implementation of the VGarageRepository trait.
///
/// Stores each player's virtual garage as a Redis hash with six fields:
/// - cars: JSON array of car classnames
/// - armor: JSON array of armor classnames
/// - helis: JSON array of helis classnames
/// - planes: JSON array of plane classnames
/// - naval: JSON array of naval classnames
/// - other: JSON array of other classnames
///
/// Key format: `vgarage:{uid}`
pub struct RedisVGarageRepository<C: RedisClient> {
    client: C,
}

impl<C: RedisClient> RedisVGarageRepository<C> {
    pub fn new(client: C) -> Self {
        Self { client }
    }
}

impl<C: RedisClient> VGarageRepository for RedisVGarageRepository<C> {
    fn create(&self, uid: &str, garage: &VGarage) -> Result<(), String> {
        let redis_key = format!("owned:garage:{}", uid);

        // Serialize locker to JSON string
        let garage_json = serde_json::to_string(garage)
            .map_err(|e| format!("Failed to serialize garage: {}", e))?;

        // Parse JSON string back to Value for field extraction
        let json_value: serde_json::Value = serde_json::from_str(&garage_json)
            .map_err(|e| format!("Failed to parse garage JSON: {}", e))?;

        // Extract fields from JSON object
        if let serde_json::Value::Object(garage_map) = json_value {
            // Convert each field to Redis-compatible format
            let fields: Vec<(String, String)> = garage_map
                .into_iter()
                .map(|(field, value)| (field, parse_json_value(&value)))
                .collect();

            // Store all fields atomically using Redis HMSET
            self.client.hash_mset(redis_key, fields)
        } else {
            Err("Failed to convert garage to object".to_string())
        }
    }

    fn update(&self, uid: &str, garage: &VGarage) -> Result<(), String> {
        // For a hash, update is the same as create - it's atomic
        self.create(uid, garage)
    }

    fn fetch(&self, uid: &str) -> Result<Option<VGarage>, String> {
        let redis_key = format!("owned:garage:{}", uid);

        // Retrieve all hash fields from Redis
        let garage_string = self.client.hash_get_all(redis_key)?;

        // Return None if no data found (garage doesn't exist)
        if garage_string.is_empty() {
            return Ok(None);
        }

        // Parse comma-separated field-value pairs
        let parts: Vec<&str> = garage_string.split(", ").collect();
        let mut json_map = serde_json::Map::new();
        let mut i = 0;

        // Process pairs of field names and values
        while i + 1 < parts.len() {
            let key = parts[i];
            let value = parts[i + 1];

            // Convert Redis string value back to proper JSON type
            let json_value = parse_redis_value(value);
            json_map.insert(key.to_string(), json_value);

            i += 2; // Move to next field-value pair
        }

        // Reconstruct VLocker from JSON object
        let json_obj = serde_json::Value::Object(json_map);
        match serde_json::from_value::<VGarage>(json_obj) {
            Ok(garage) => Ok(Some(garage)),
            // Return None for any deserialization errors (corrupted data)
            Err(_) => Ok(None),
        }
    }

    fn get(&self, uid: &str, field: &str) -> Result<Vec<String>, String> {
        let redis_key = format!("owned:garage:{}", uid);

        // Retrieve the specific field from the hash
        let field_json = self.client.hash_get(redis_key, field.to_string())?;

        // Return empty vector if field is empty
        if field_json.is_empty() {
            return Ok(Vec::new());
        }

        // Deserialize the JSON array
        serde_json::from_str::<Vec<String>>(&field_json)
            .map_err(|e| format!("Failed to deserialize field '{}': {}", field, e))
    }

    fn delete(&self, uid: &str) -> Result<(), String> {
        let redis_key = format!("owned:garage:{}", uid);
        self.client.delete_key(redis_key)
    }

    fn exists(&self, uid: &str) -> Result<bool, String> {
        let redis_key = format!("owned:garage:{}", uid);
        self.client.key_exists(redis_key)
    }
}
