//! Virtual locker repository implementation for item data persistence operations.
//!
//! This module provides the data access layer for virtual locker management.
//! Each player's virtual locker is stored as a Redis hash with four fields:
//! - items: JSON array of item classnames
//! - weapons: JSON array of weapon classnames
//! - magazines: JSON array of magazine classnames
//! - backpacks: JSON array of backpack classnames

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

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

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

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

    /// Retrieves a specific field from a player's virtual locker
    /// Fields: "items", "weapons", "magazines", "backpacks"
    fn get(&self, uid: &str, field: &str) -> Result<Vec<String>, String>;

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

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

/// Redis-based implementation of the VLockerRepository trait.
///
/// Stores each player's virtual locker as a Redis hash with four fields:
/// - items: JSON array of item classnames
/// - weapons: JSON array of weapon classnames
/// - magazines: JSON array of magazine classnames
/// - backpacks: JSON array of backpack classnames
///
/// Key format: `owned:locker:{uid}`
pub struct RedisVLockerRepository<C: RedisClient> {
    client: C,
}

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

impl<C: RedisClient> VLockerRepository for RedisVLockerRepository<C> {
    fn create(&self, uid: &str, locker: &VLocker) -> Result<(), String> {
        let redis_key = format!("owned:locker:{}", uid);

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

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

        // Extract fields from JSON object
        if let serde_json::Value::Object(locker_map) = json_value {
            // Convert each field to Redis-compatible format
            let fields: Vec<(String, String)> = locker_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 locker to object".to_string())
        }
    }

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

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

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

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

        // Parse comma-separated field-value pairs
        let parts: Vec<&str> = locker_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::<VLocker>(json_obj) {
            Ok(locker) => Ok(Some(locker)),
            // 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:locker:{}", 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:locker:{}", uid);
        self.client.delete_key(redis_key)
    }

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