use crate::AppService;
use crate::error::AppError;
use base64::Engine;
use base64::engine::general_purpose;
use chrono::Utc;
use models::users::{user_activity_log, user_ssh_key};
use sea_orm::*;
use serde::{Deserialize, Serialize};
use session::Session;
use sha2::{Digest, Sha256};
use uuid::Uuid;

#[derive(Deserialize, Serialize, Clone, Debug, utoipa::ToSchema)]
pub struct AddSshKeyParams {
    pub title: String,

    pub public_key: String,
}

#[derive(Deserialize, Serialize, Clone, Debug, utoipa::ToSchema)]
pub struct UpdateSshKeyParams {
    pub title: Option<String>,

    pub expires_at: Option<chrono::DateTime<Utc>>,
}

#[derive(Deserialize, Serialize, Clone, Debug, utoipa::ToSchema)]
pub struct SshKeyResponse {
    pub id: i64,
    pub user_uid: Uuid,
    pub title: String,
    pub fingerprint: String,
    pub key_type: String,
    pub key_bits: Option<i32>,
    pub is_verified: bool,
    pub last_used_at: Option<chrono::DateTime<Utc>>,
    pub expires_at: Option<chrono::DateTime<Utc>>,
    pub is_revoked: bool,
    pub created_at: chrono::DateTime<Utc>,
    pub updated_at: chrono::DateTime<Utc>,
}

#[derive(Deserialize, Serialize, Clone, Debug, utoipa::ToSchema)]
pub struct SshKeyListResponse {
    pub keys: Vec<SshKeyResponse>,
    pub total: usize,
}

#[derive(Debug)]
pub struct ParsedSshKey {
    pub key_type: String,
    pub key_data: Vec<u8>,
    pub key_bits: Option<i32>,
    pub comment: Option<String>,
}

impl AppService {
    pub async fn user_add_ssh_key(
        &self,
        context: &Session,
        params: AddSshKeyParams,
    ) -> Result<SshKeyResponse, AppError> {
        let user_uid = context.user().ok_or(AppError::Unauthorized)?;

        let public_key = params.public_key.trim().to_string();

        let parsed = self.user_parse_ssh_public_key(&public_key)?;

        let fingerprint = self.user_generate_ssh_fingerprint(&parsed.key_data)?;

        let existing: Option<user_ssh_key::Model> = user_ssh_key::Entity::find()
            .filter(user_ssh_key::Column::Fingerprint.eq(&fingerprint))
            .one(&self.db)
            .await?;

        if existing.is_some() {
            return Err(AppError::BadRequest("SSH key already exists".to_string()));
        }

        let count = user_ssh_key::Entity::find()
            .filter(user_ssh_key::Column::User.eq(user_uid))
            .filter(user_ssh_key::Column::IsRevoked.eq(false))
            .count(&self.db)
            .await?;

        if count >= 50 {
            return Err(AppError::BadRequest("Too many SSH keys".to_string()));
        }

        let now = Utc::now();
        let ssh_key_model = user_ssh_key::ActiveModel {
            user: Set(user_uid),
            title: Set(params.title.clone()),
            public_key: Set(public_key),
            fingerprint: Set(fingerprint),
            key_type: Set(parsed.key_type.clone()),
            key_bits: Set(parsed.key_bits),
            is_verified: Set(false),
            last_used_at: Set(None),
            expires_at: Set(None),
            is_revoked: Set(false),
            created_at: Set(now),
            updated_at: Set(now),
            ..Default::default()
        };

        let created_key = ssh_key_model.insert(&self.db).await?;

        let _ = user_activity_log::ActiveModel {
            user_uid: Set(Some(user_uid)),
            action: Set("ssh_key_add".to_string()),
            ip_address: Set(context.ip_address()),
            user_agent: Set(context.user_agent()),
            details: Set(serde_json::json!({
                "key_id": created_key.id,
                "key_title": params.title,
                "key_type": parsed.key_type,
                "fingerprint": created_key.fingerprint
            })),
            created_at: Set(now),
            ..Default::default()
        }
        .insert(&self.db)
        .await;

        Ok(self.user_model_to_response(created_key))
    }

    pub async fn user_list_ssh_keys(
        &self,
        context: &Session,
    ) -> Result<SshKeyListResponse, AppError> {
        let user_uid = context.user().ok_or(AppError::Unauthorized)?;

        let keys: Vec<user_ssh_key::Model> = user_ssh_key::Entity::find()
            .filter(user_ssh_key::Column::User.eq(user_uid))
            .order_by_desc(user_ssh_key::Column::CreatedAt)
            .all(&self.db)
            .await?;

        let total = keys.len();
        let keys = keys
            .into_iter()
            .map(|k| self.user_model_to_response(k))
            .collect();

        Ok(SshKeyListResponse { keys, total })
    }

    pub async fn user_get_ssh_key(
        &self,
        context: &Session,
        key_id: i64,
    ) -> Result<SshKeyResponse, AppError> {
        let user_uid = context.user().ok_or(AppError::Unauthorized)?;

        let key: user_ssh_key::Model = user_ssh_key::Entity::find_by_id(key_id)
            .filter(user_ssh_key::Column::User.eq(user_uid))
            .one(&self.db)
            .await?
            .ok_or(AppError::NotFound("SSH key not found".to_string()))?;

        Ok(self.user_model_to_response(key))
    }

    pub async fn user_update_ssh_key(
        &self,
        context: &Session,
        key_id: i64,
        params: UpdateSshKeyParams,
    ) -> Result<SshKeyResponse, AppError> {
        let user_uid = context.user().ok_or(AppError::Unauthorized)?;

        let key: user_ssh_key::Model = user_ssh_key::Entity::find_by_id(key_id)
            .filter(user_ssh_key::Column::User.eq(user_uid))
            .one(&self.db)
            .await?
            .ok_or(AppError::NotFound("SSH key not found".to_string()))?;

        let mut active_key: user_ssh_key::ActiveModel = key.into();

        let updated_title = params.title.clone();
        let updated_expires_at = params.expires_at;

        if let Some(title) = params.title {
            active_key.title = Set(title);
        }

        if let Some(expires_at) = params.expires_at {
            active_key.expires_at = Set(Some(expires_at));
        }

        active_key.updated_at = Set(Utc::now());

        let updated_key = active_key.update(&self.db).await?;

        let _ = user_activity_log::ActiveModel {
            user_uid: Set(Some(user_uid)),
            action: Set("ssh_key_update".to_string()),
            ip_address: Set(context.ip_address()),
            user_agent: Set(context.user_agent()),
            details: Set(serde_json::json!({
                "key_id": key_id,
                "updated_fields": {
                    "title": updated_title,
                    "expires_at": updated_expires_at
                }
            })),
            created_at: Set(Utc::now()),
            ..Default::default()
        }
        .insert(&self.db)
        .await;

        Ok(self.user_model_to_response(updated_key))
    }

    pub async fn user_delete_ssh_key(
        &self,
        context: &Session,
        key_id: i64,
    ) -> Result<(), AppError> {
        let user_uid = context.user().ok_or(AppError::Unauthorized)?;

        let key: user_ssh_key::Model = user_ssh_key::Entity::find_by_id(key_id)
            .filter(user_ssh_key::Column::User.eq(user_uid))
            .one(&self.db)
            .await?
            .ok_or(AppError::NotFound("SSH key not found".to_string()))?;
        let _ = user_activity_log::ActiveModel {
            user_uid: Set(Some(user_uid)),
            action: Set("ssh_key_delete".to_string()),
            ip_address: Set(context.ip_address()),
            user_agent: Set(context.user_agent()),
            details: Set(serde_json::json!({
                "key_id": key_id,
                "key_title": key.title.clone(),
                "fingerprint": key.fingerprint.clone()
            })),
            created_at: Set(Utc::now()),
            ..Default::default()
        }
        .insert(&self.db)
        .await;

        user_ssh_key::Entity::delete(key.into_active_model())
            .exec(&self.db)
            .await?;

        Ok(())
    }

    pub async fn user_revoke_ssh_key(
        &self,
        context: &Session,
        key_id: i64,
    ) -> Result<(), AppError> {
        let user_uid = context.user().ok_or(AppError::Unauthorized)?;

        let key: user_ssh_key::Model = user_ssh_key::Entity::find_by_id(key_id)
            .filter(user_ssh_key::Column::User.eq(user_uid))
            .one(&self.db)
            .await?
            .ok_or(AppError::NotFound("SSH key not found".to_string()))?;

        let mut active_key: user_ssh_key::ActiveModel = key.clone().into();
        active_key.is_revoked = Set(true);
        active_key.updated_at = Set(Utc::now());

        active_key.update(&self.db).await?;

        let _ = user_activity_log::ActiveModel {
            user_uid: Set(Some(user_uid)),
            action: Set("ssh_key_revoke".to_string()),
            ip_address: Set(context.ip_address()),
            user_agent: Set(context.user_agent()),
            details: Set(serde_json::json!({
                "key_id": key_id,
                "key_title": key.title,
                "fingerprint": key.fingerprint
            })),
            created_at: Set(Utc::now()),
            ..Default::default()
        }
        .insert(&self.db)
        .await;

        Ok(())
    }

    pub async fn user_verify_ssh_key_by_fingerprint(
        &self,
        fingerprint: &str,
    ) -> Result<user_ssh_key::Model, AppError> {
        let key: user_ssh_key::Model = user_ssh_key::Entity::find()
            .filter(user_ssh_key::Column::Fingerprint.eq(fingerprint))
            .filter(user_ssh_key::Column::IsRevoked.eq(false))
            .one(&self.db)
            .await?
            .ok_or(AppError::NotFound("SSH key not found".to_string()))?;

        if let Some(expires_at) = key.expires_at {
            let now = Utc::now();
            if now > expires_at {
                return Err(AppError::Unauthorized); // AppError::SshKeyExpired replaced with generic Unauthorized
            }
        }

        Ok(key)
    }

    pub async fn user_touch_ssh_key_last_used(&self, key_id: i64) -> Result<(), AppError> {
        let key: user_ssh_key::Model = user_ssh_key::Entity::find_by_id(key_id)
            .one(&self.db)
            .await?
            .ok_or(AppError::NotFound("SSH key not found".to_string()))?;

        let mut active_key: user_ssh_key::ActiveModel = key.into();
        active_key.last_used_at = Set(Some(Utc::now()));
        active_key.updated_at = Set(Utc::now());

        active_key.update(&self.db).await?;

        Ok(())
    }

    fn user_parse_ssh_public_key(&self, public_key: &str) -> Result<ParsedSshKey, AppError> {
        let parts: Vec<&str> = public_key.split_whitespace().collect();
        if parts.len() < 2 {
            return Err(AppError::BadRequest("Invalid SSH key format".to_string()));
        }
        let key_type = parts[0];
        let key_data_base64 = parts[1];
        let comment: Option<String> = parts.get(2).map(|s| (*s).to_string());
        let normalized_key_type = match key_type {
            "ssh-rsa" => "rsa",
            "ssh-ed25519" => "ed25519",
            "ecdsa-sha2-nistp256" | "ecdsa-sha2-nistp384" | "ecdsa-sha2-nistp521" => "ecdsa",
            "ssh-dss" => "dsa",
            _ => return Err(AppError::BadRequest("Unsupported SSH key type".to_string())),
        };
        let key_data = general_purpose::STANDARD
            .decode(key_data_base64)
            .map_err(|_| AppError::BadRequest("Invalid SSH key format".to_string()))?;
        let key_bits = if normalized_key_type == "rsa" {
            self.user_calculate_rsa_key_bits(&key_data)
        } else {
            None
        };

        Ok(ParsedSshKey {
            key_type: normalized_key_type.to_string(),
            key_data,
            key_bits,
            comment,
        })
    }

    fn user_generate_ssh_fingerprint(&self, key_data: &[u8]) -> Result<String, AppError> {
        let mut hasher = Sha256::new();
        hasher.update(key_data);
        let hash = hasher.finalize();
        let fingerprint = format!("SHA256:{}", general_purpose::STANDARD_NO_PAD.encode(&hash));

        Ok(fingerprint)
    }

    fn user_calculate_rsa_key_bits(&self, key_data: &[u8]) -> Option<i32> {
        if key_data.len() < 256 {
            Some(1024)
        } else if key_data.len() < 512 {
            Some(2048)
        } else if key_data.len() < 1024 {
            Some(4096)
        } else {
            Some(8192)
        }
    }

    fn user_model_to_response(&self, model: user_ssh_key::Model) -> SshKeyResponse {
        SshKeyResponse {
            id: model.id,
            user_uid: model.user,
            title: model.title,
            fingerprint: model.fingerprint,
            key_type: model.key_type,
            key_bits: model.key_bits,
            is_verified: model.is_verified,
            last_used_at: model.last_used_at,
            expires_at: model.expires_at,
            is_revoked: model.is_revoked,
            created_at: model.created_at,
            updated_at: model.updated_at,
        }
    }
}
