// Copyright (c) 2025 Kirky.X
//
// Licensed under the MIT License
// See LICENSE file in the project root for full license information.

use crate::error::ConfigError;
use crate::key::{
    KeyBundle, KeyRing, KeyRotationSchedule, KeyStatus, RotationPlan, RotationResult,
    CURRENT_KEY_VERSION,
};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
use std::time::{Duration, SystemTime, UNIX_EPOCH};

#[cfg(feature = "encryption")]
use rand::Rng;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KeyVersion {
    pub id: String,
    pub version: u32,
    pub created_at: u64,
    pub status: KeyStatus,
    pub algorithm: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KeyInfo {
    pub key_id: String,
    pub current_version: u32,
    pub total_versions: usize,
    pub active_versions: usize,
    pub deprecated_versions: usize,
    pub created_at: u64,
    pub last_rotated_at: Option<u64>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KeyManager {
    master_key_hash: String,
    key_rings: HashMap<String, KeyRing>,
    schedules: HashMap<String, KeyRotationSchedule>,
    default_key_id: String,
    storage_path: PathBuf,
}

impl KeyManager {
    #[cfg(feature = "encryption")]
    pub fn new(storage_path: PathBuf) -> Result<Self, ConfigError> {
        Ok(Self {
            master_key_hash: String::new(),
            key_rings: HashMap::new(),
            schedules: HashMap::new(),
            default_key_id: "default".to_string(),
            storage_path,
        })
    }

    /// Initialize a new key ring with the given master key
    ///
    /// # Security Notes
    ///
    /// - ⚠️ **Master Key**: The master key must be stored securely and never shared or committed to version control
    /// - ⚠️ **Key ID**: Use descriptive key IDs (e.g., "production", "staging", "development")
    /// - ⚠️ **Created By**: Include creator information for audit trail
    /// - ⚠️ **Key Backup**: Ensure you have a secure backup of the master key
    /// - ⚠️ **Key Rotation**: Set up automatic key rotation schedule after initialization
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// # use confers::key::KeyManager;
    /// # use std::path::PathBuf;
    /// # let master_key = [0u8; 32];
    /// let mut km = KeyManager::new(PathBuf::from("./keys"))?;
    /// let version = km.initialize(
    ///     &master_key,
    ///     "production".to_string(),
    ///     "security-team".to_string()
    /// )?;
    /// # Ok::<(), confers::error::ConfigError>(())
    /// ```
    #[cfg(feature = "encryption")]
    pub fn initialize(
        &mut self,
        master_key: &[u8; 32],
        key_id: String,
        created_by: String,
    ) -> Result<KeyVersion, ConfigError> {
        let key_ring = KeyRing::new(master_key, key_id.clone(), created_by)?;
        self.key_rings.insert(key_id.clone(), key_ring);

        let schedule = KeyRotationSchedule::new(key_id.clone(), 90, now_timestamp(), 5);
        self.schedules.insert(key_id.clone(), schedule);

        self.default_key_id = key_id.clone();

        Ok(KeyVersion {
            id: format!("{}_{}", key_id, crate::key::CONFERS_KEY_VERSION),
            version: CURRENT_KEY_VERSION,
            created_at: now_timestamp(),
            status: KeyStatus::Active,
            algorithm: "AES256-GCM".to_string(),
        })
    }

    /// Generate a new cryptographically secure random key
    ///
    /// # Security Notes
    ///
    /// - ⚠️ **Randomness**: Uses cryptographically secure random number generator (CSPRNG)
    /// - ⚠️ **Key Strength**: Generates 256-bit keys for AES-256-GCM encryption
    /// - ⚠️ **Key Usage**: Use the generated key immediately or store it securely
    /// - ⚠️ **Key Disposal**: Ensure the key is properly zeroized when no longer needed
    /// - ⚠️ **Key Reuse**: Never reuse keys for different purposes
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// # use confers::key::KeyManager;
    /// # use confers::encryption::ConfigEncryption;
    /// # use std::path::PathBuf;
    /// # let mut km = KeyManager::new(PathBuf::from("./keys")).unwrap();
    /// let key = km.generate_key()?;
    /// let encryption = ConfigEncryption::new(key);
    /// # Ok::<(), confers::error::ConfigError>(())
    /// ```
    #[cfg(feature = "encryption")]
    pub fn generate_key(&mut self) -> Result<[u8; 32], ConfigError> {
        let mut key_bytes = [0u8; 32];
        let mut rng = rand::rng();
        rng.fill(&mut key_bytes);
        Ok(key_bytes)
    }

    #[cfg(feature = "encryption")]
    pub fn create_key_ring(
        &mut self,
        master_key: &[u8; 32],
        key_id: String,
        created_by: String,
        description: Option<String>,
    ) -> Result<KeyVersion, ConfigError> {
        if self.key_rings.contains_key(&key_id) {
            return Err(ConfigError::FormatDetectionFailed(format!(
                "Key ring '{}' already exists",
                key_id
            )));
        }

        let key_ring = KeyRing::new(master_key, key_id.clone(), created_by)?;

        self.key_rings.insert(key_id.clone(), key_ring);

        if let Some(desc) = description {
            if let Some(key) = self.key_rings.get_mut(&key_id) {
                key.primary_key.metadata.description = Some(desc);
            }
        }

        let schedule = KeyRotationSchedule::new(key_id.clone(), 90, now_timestamp(), 5);
        self.schedules.insert(key_id.clone(), schedule);

        Ok(KeyVersion {
            id: format!("{}_{}", key_id, crate::key::CONFERS_KEY_VERSION),
            version: CURRENT_KEY_VERSION,
            created_at: now_timestamp(),
            status: KeyStatus::Active,
            algorithm: "AES256-GCM".to_string(),
        })
    }

    /// Rotate the key to a new version
    ///
    /// # Security Notes
    ///
    /// - ⚠️ **Master Key**: Must use the same master key that was used to initialize the key ring
    /// - ⚠️ **Key Rotation**: Regular key rotation is recommended (every 90 days for production)
    /// - ⚠️ **Key Transition**: Old keys remain available for decryption during transition period
    /// - ⚠️ **Audit Trail**: Include creation information and description for audit purposes
    /// - ⚠️ **Re-encryption**: After rotation, re-encrypt all data that was encrypted with the old key
    ///
    /// # Example
    ///
    /// ```rust,no_run
    /// # use confers::key::KeyManager;
    /// # use std::path::PathBuf;
    /// # let mut km = KeyManager::new(PathBuf::from("./keys")).unwrap();
    /// # let master_key = [0u8; 32];
    /// let result = km.rotate_key(
    ///     &master_key,
    ///     Some("production".to_string()),
    ///     "security-team".to_string(),
    ///     Some("Scheduled rotation".to_string())
    /// )?;
    /// println!("Rotated from version {} to {}", result.previous_version, result.new_version);
    /// # Ok::<(), confers::error::ConfigError>(())
    /// ```
    #[cfg(feature = "encryption")]
    pub fn rotate_key(
        &mut self,
        master_key: &[u8; 32],
        key_id: Option<String>,
        created_by: String,
        description: Option<String>,
    ) -> Result<RotationResult, ConfigError> {
        let key_id = key_id.unwrap_or_else(|| self.default_key_id.clone());

        let key_ring = self.key_rings.get_mut(&key_id).ok_or_else(|| {
            ConfigError::FormatDetectionFailed(format!("Key ring '{}' not found", key_id))
        })?;

        let old_version = key_ring.current_version;
        let new_key = key_ring.rotate(master_key, created_by, description)?;

        if let Some(schedule) = self.schedules.get_mut(&key_id) {
            schedule.update_after_rotation();
        }

        Ok(RotationResult {
            key_id: key_ring.key_id.clone(),
            previous_version: old_version,
            new_version: new_key.metadata.version,
            rotated_at: now_timestamp(),
            reencryption_required: true,
        })
    }

    pub fn get_key_info(&self, key_id: &str) -> Result<KeyInfo, ConfigError> {
        let key_ring = self.key_rings.get(key_id).ok_or_else(|| {
            ConfigError::FormatDetectionFailed(format!("Key ring '{}' not found", key_id))
        })?;

        Ok(KeyInfo {
            key_id: key_ring.key_id.clone(),
            current_version: key_ring.current_version,
            total_versions: key_ring.secondary_keys.len() + 1,
            active_versions: key_ring
                .secondary_keys
                .iter()
                .filter(|k| k.metadata.is_active())
                .count()
                + 1,
            deprecated_versions: key_ring
                .secondary_keys
                .iter()
                .filter(|k| k.metadata.status == KeyStatus::Deprecated)
                .count(),
            created_at: key_ring.created_at,
            last_rotated_at: key_ring.last_rotated_at,
        })
    }

    pub fn list_keys(&self) -> Vec<KeyInfo> {
        self.key_rings
            .values()
            .map(|ring| KeyInfo {
                key_id: ring.key_id.clone(),
                current_version: ring.current_version,
                total_versions: ring.secondary_keys.len() + 1,
                active_versions: ring
                    .secondary_keys
                    .iter()
                    .filter(|k| k.metadata.is_active())
                    .count()
                    + 1,
                deprecated_versions: ring
                    .secondary_keys
                    .iter()
                    .filter(|k| k.metadata.status == KeyStatus::Deprecated)
                    .count(),
                created_at: ring.created_at,
                last_rotated_at: ring.last_rotated_at,
            })
            .collect()
    }

    pub fn get_rotation_status(&self) -> Vec<RotationStatus> {
        self.schedules
            .values()
            .map(|schedule| {
                let key_ring = self.key_rings.get(&schedule.key_id);
                let next_rotation = schedule.next_rotation;
                let days_until = schedule.days_until_rotation();

                RotationStatus {
                    key_id: schedule.key_id.clone(),
                    current_version: key_ring.map(|r| r.current_version).unwrap_or(0),
                    rotation_interval_days: schedule.rotation_interval_days,
                    last_rotation: schedule.last_rotation,
                    next_rotation,
                    days_until_rotation: days_until,
                    is_overdue: schedule.is_rotation_due(),
                    auto_rotate: schedule.auto_rotate,
                }
            })
            .collect()
    }

    pub fn set_rotation_interval(
        &mut self,
        key_id: &str,
        interval_days: u32,
    ) -> Result<(), ConfigError> {
        let schedule = self.schedules.get_mut(key_id).ok_or_else(|| {
            ConfigError::FormatDetectionFailed(format!("Key ring '{}' not found", key_id))
        })?;

        schedule.rotation_interval_days = interval_days;
        schedule.next_rotation = schedule
            .last_rotation
            .saturating_add(interval_days as u64 * 86400);

        Ok(())
    }

    pub fn plan_rotation(
        &self,
        target_version: u32,
        key_id: Option<String>,
    ) -> Result<RotationPlan, ConfigError> {
        let key_id = key_id.unwrap_or_else(|| self.default_key_id.clone());

        let key_ring = self.key_rings.get(&key_id).ok_or_else(|| {
            ConfigError::FormatDetectionFailed(format!("Key ring '{}' not found", key_id))
        })?;

        if target_version <= key_ring.current_version {
            return Err(ConfigError::FormatDetectionFailed(
                "Target version must be greater than current version".to_string(),
            ));
        }

        Ok(RotationPlan::new(
            key_id,
            key_ring.current_version,
            target_version,
        ))
    }

    pub fn get_key_by_version(
        &self,
        key_id: &str,
        version: u32,
    ) -> Result<Option<&KeyBundle>, ConfigError> {
        let key_ring = self.key_rings.get(key_id).ok_or_else(|| {
            ConfigError::FormatDetectionFailed(format!("Key ring '{}' not found", key_id))
        })?;

        Ok(key_ring.get_key_by_version(version))
    }

    pub fn deprecate_version(&mut self, key_id: &str, version: u32) -> Result<(), ConfigError> {
        let key_ring = self.key_rings.get_mut(key_id).ok_or_else(|| {
            ConfigError::FormatDetectionFailed(format!("Key ring '{}' not found", key_id))
        })?;

        if version == key_ring.current_version {
            return Err(ConfigError::FormatDetectionFailed(
                "Cannot deprecate the current active version".to_string(),
            ));
        }

        key_ring.deactivate_version(version);
        Ok(())
    }

    pub fn cleanup_old_keys(
        &mut self,
        key_id: &str,
        keep_versions: u32,
    ) -> Result<u32, ConfigError> {
        let key_ring = self.key_rings.get_mut(key_id).ok_or_else(|| {
            ConfigError::FormatDetectionFailed(format!("Key ring '{}' not found", key_id))
        })?;

        if key_ring.secondary_keys.len() <= keep_versions as usize {
            return Ok(0);
        }

        let initial_count = key_ring.secondary_keys.len();
        key_ring.secondary_keys.retain(|k| {
            k.metadata.status == KeyStatus::Active || k.metadata.version > keep_versions
        });

        Ok((initial_count - key_ring.secondary_keys.len()) as u32)
    }

    pub fn get_default_key_id(&self) -> &str {
        &self.default_key_id
    }

    pub fn set_default_key_id(&mut self, key_id: &str) -> Result<(), ConfigError> {
        if !self.key_rings.contains_key(key_id) {
            return Err(ConfigError::FormatDetectionFailed(format!(
                "Key ring '{}' not found",
                key_id
            )));
        }
        self.default_key_id = key_id.to_string();
        Ok(())
    }
}

#[derive(Debug, Clone, Serialize)]
pub struct RotationStatus {
    pub key_id: String,
    pub current_version: u32,
    pub rotation_interval_days: u32,
    pub last_rotation: u64,
    pub next_rotation: u64,
    pub days_until_rotation: i64,
    pub is_overdue: bool,
    pub auto_rotate: bool,
}

fn now_timestamp() -> u64 {
    SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .unwrap_or(Duration::ZERO)
        .as_secs()
}
