use crate::AppService;
use crate::error::AppError;
use base64::Engine;
use chrono::Utc;
use models::users::{user_activity_log, user_token};
use sea_orm::*;
use serde::{Deserialize, Serialize};
use session::Session;
use uuid::Uuid;

#[derive(Deserialize, Serialize, Clone, Debug, utoipa::ToSchema)]
pub struct CreateAccessKeyParams {
    pub name: String,
    pub scopes: Vec<String>,
    pub expires_at: Option<chrono::DateTime<Utc>>,
}

#[derive(Deserialize, Serialize, Clone, Debug, utoipa::ToSchema)]
pub struct AccessKeyResponse {
    pub id: i64,
    pub name: String,
    pub access_key: Option<String>,
    pub scopes: Vec<String>,
    pub expires_at: Option<chrono::DateTime<Utc>>,
    pub is_revoked: bool,
    pub created_at: chrono::DateTime<Utc>,
}

#[derive(Deserialize, Serialize, Clone, Debug, utoipa::ToSchema)]
pub struct AccessKeyListResponse {
    pub access_keys: Vec<AccessKeyResponse>,
    pub total: usize,
}

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

        let access_key = self.user_generate_access_key();
        let access_key_hash = self.user_hash_access_key(&access_key);

        let access_key_model = user_token::ActiveModel {
            user: Set(user_uid),
            name: Set(params.name.clone()),
            token_hash: Set(access_key_hash),
            scopes: Set(serde_json::to_value(params.scopes.clone()).unwrap()),
            expires_at: Set(params.expires_at),
            is_revoked: Set(false),
            created_at: Set(Utc::now()),
            updated_at: Set(Utc::now()),
            ..Default::default()
        };

        let created_access_key = access_key_model.insert(&self.db).await?;

        let _ = user_activity_log::ActiveModel {
            user_uid: Set(Some(user_uid)),
            action: Set("access_key_create".to_string()),
            ip_address: Set(context.ip_address()),
            user_agent: Set(context.user_agent()),
            details: Set(serde_json::json!({
                "access_key_name": params.name.clone(),
                "access_key_id": created_access_key.id,
                "scopes": params.scopes.clone()
            })),
            created_at: Set(Utc::now()),
            ..Default::default()
        }
        .insert(&self.db)
        .await;

        let scopes: Vec<String> =
            serde_json::from_value(created_access_key.scopes).unwrap_or_default();

        Ok(AccessKeyResponse {
            id: created_access_key.id,
            name: created_access_key.name,
            access_key: Some(access_key),
            scopes,
            expires_at: created_access_key.expires_at,
            is_revoked: created_access_key.is_revoked,
            created_at: created_access_key.created_at,
        })
    }

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

        let access_keys = user_token::Entity::find()
            .filter(user_token::Column::User.eq(user_uid))
            .order_by_desc(user_token::Column::CreatedAt)
            .all(&self.db)
            .await?;

        let total = access_keys.len();
        let access_keys = access_keys
            .into_iter()
            .map(|ak| {
                let scopes: Vec<String> = serde_json::from_value(ak.scopes).unwrap_or_default();
                AccessKeyResponse {
                    id: ak.id,
                    name: ak.name,
                    access_key: None,
                    scopes,
                    expires_at: ak.expires_at,
                    is_revoked: ak.is_revoked,
                    created_at: ak.created_at,
                }
            })
            .collect();

        Ok(AccessKeyListResponse { access_keys, total })
    }

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

        let access_key = user_token::Entity::find_by_id(access_key_id)
            .filter(user_token::Column::User.eq(user_uid))
            .one(&self.db)
            .await?
            .ok_or(AppError::NotFound("Access key not found".to_string()))?;

        let mut active_access_key: user_token::ActiveModel = access_key.clone().into();
        active_access_key.is_revoked = Set(true);
        active_access_key.updated_at = Set(Utc::now());

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

        let _ = user_activity_log::ActiveModel {
            user_uid: Set(Some(user_uid)),
            action: Set("access_key_revoke".to_string()),
            ip_address: Set(context.ip_address()),
            user_agent: Set(context.user_agent()),
            details: Set(serde_json::json!({
                "access_key_name": access_key.name,
                "access_key_id": access_key_id
            })),
            created_at: Set(Utc::now()),
            ..Default::default()
        }
        .insert(&self.db)
        .await;

        Ok(())
    }

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

        let access_key = user_token::Entity::find_by_id(access_key_id)
            .filter(user_token::Column::User.eq(user_uid))
            .one(&self.db)
            .await?
            .ok_or(AppError::NotFound("Access key not found".to_string()))?;

        let _ = user_activity_log::ActiveModel {
            user_uid: Set(Some(user_uid)),
            action: Set("access_key_delete".to_string()),
            ip_address: Set(context.ip_address()),
            user_agent: Set(context.user_agent()),
            details: Set(serde_json::json!({
                "access_key_name": access_key.name.clone(),
                "access_key_id": access_key_id
            })),
            created_at: Set(Utc::now()),
            ..Default::default()
        }
        .insert(&self.db)
        .await;

        user_token::Entity::delete(access_key.into_active_model())
            .exec(&self.db)
            .await?;

        Ok(())
    }

    pub async fn user_verify_access_key(&self, access_key: String) -> Result<Uuid, AppError> {
        let access_key_hash = self.user_hash_access_key(&access_key);

        let access_key_model = user_token::Entity::find()
            .filter(user_token::Column::TokenHash.eq(access_key_hash))
            .filter(user_token::Column::IsRevoked.eq(false))
            .one(&self.db)
            .await?
            .ok_or(AppError::Unauthorized)?;

        if let Some(expires_at) = access_key_model.expires_at {
            if expires_at < Utc::now() {
                return Err(AppError::Unauthorized);
            }
        }

        Ok(access_key_model.user)
    }

    fn user_generate_access_key(&self) -> String {
        use std::time::{SystemTime, UNIX_EPOCH};
        let now = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_nanos();
        let chars: Vec<char> = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
            .chars()
            .collect();
        let mut access_key = String::with_capacity(68);
        access_key.push_str("gda_");
        let mut state = now as u64;
        for _ in 0..64 {
            state = state.wrapping_mul(1103515245).wrapping_add(12345);
            access_key.push(chars[(state as usize) % chars.len()]);
        }
        access_key
    }

    fn user_hash_access_key(&self, access_key: &str) -> String {
        use sha2::{Digest, Sha256};
        let mut hasher = Sha256::new();
        hasher.update(access_key.as_bytes());
        format!(
            "{}",
            base64::prelude::BASE64_STANDARD.encode(&hasher.finalize().to_vec())
        )
    }
}
