use crate::AppService;
use crate::error::AppError;
use chrono::{DateTime, Utc};
use models::projects::{project, project_activity};
use models::users::{user, user_activity_log};
use sea_orm::*;
use serde::{Deserialize, Serialize};
use session::Session;
use utoipa::ToSchema;
use uuid::Uuid;

#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct UserActivityItem {
    pub id: i64,
    pub activity_type: String, // "auth" | "project"
    pub action: String,
    pub title: String,
    pub resource_type: Option<String>,
    pub resource_name: Option<String>,
    pub metadata: Option<serde_json::Value>,
    pub created_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct UserActivityResponse {
    pub items: Vec<UserActivityItem>,
    pub total: u64,
    pub page: u64,
    pub per_page: u64,
}

#[derive(Debug, Clone, Deserialize, Serialize, utoipa::IntoParams)]
pub struct UserActivityQuery {
    pub page: Option<u64>,
    pub per_page: Option<u64>,
}

impl AppService {
    pub async fn get_user_activity(
        &self,
        context: Session,
        username: String,
        query: UserActivityQuery,
    ) -> Result<UserActivityResponse, AppError> {
        let target_user = user::Entity::find()
            .filter(user::Column::Username.eq(&username))
            .one(&self.db)
            .await?
            .ok_or(AppError::UserNotFound)?;

        let current_uid = context.user();
        let is_owner = current_uid
            .map(|uid| uid == target_user.uid)
            .unwrap_or(false);

        let page = std::cmp::Ord::max(query.page.unwrap_or(1), 1);
        let per_page = std::cmp::Ord::min(std::cmp::Ord::max(query.per_page.unwrap_or(20), 1), 100);
        let offset = (page - 1) * per_page;

        // User's auth activity log entries
        let auth_logs: Vec<(i64, String, DateTime<Utc>, serde_json::Value)> =
            user_activity_log::Entity::find()
                .filter(user_activity_log::Column::UserUid.eq(target_user.uid))
                .order_by_desc(user_activity_log::Column::CreatedAt)
                .select_only()
                .column(user_activity_log::Column::Id)
                .column(user_activity_log::Column::Action)
                .column(user_activity_log::Column::CreatedAt)
                .column(user_activity_log::Column::Details)
                .into_tuple()
                .all(&self.db)
                .await?;

        // User's project activity (where user is the actor)
        let proj_activity = project_activity::Entity::find()
            .filter(project_activity::Column::Actor.eq(target_user.uid))
            .order_by_desc(project_activity::Column::CreatedAt)
            .all(&self.db)
            .await?;

        // Privacy filter: non-owners only see public project activity
        let proj_ids: Vec<Uuid> = proj_activity.iter().map(|a| a.project).collect();
        let proj_map: std::collections::HashMap<Uuid, project::Model> = project::Entity::find()
            .filter(project::Column::Id.is_in(proj_ids))
            .all(&self.db)
            .await?
            .into_iter()
            .map(|p| (p.id, p))
            .collect();

        // Build combined activity list
        let mut items: Vec<UserActivityItem> = Vec::new();

        // Auth events
        for (id, action, created_at, metadata) in auth_logs {
            let title = format_action_title(&action);
            items.push(UserActivityItem {
                id,
                activity_type: "auth".to_string(),
                action,
                title,
                resource_type: None,
                resource_name: None,
                metadata: if metadata != serde_json::Value::Null {
                    Some(metadata)
                } else {
                    None
                },
                created_at,
            });
        }

        // Project events
        for activity in proj_activity {
            // Privacy filter
            if !is_owner {
                if let Some(proj) = proj_map.get(&activity.project) {
                    if proj.is_public == false {
                        continue;
                    }
                } else {
                    continue;
                }
            }
            items.push(UserActivityItem {
                id: activity.id,
                activity_type: "project".to_string(),
                action: activity.event_type,
                title: activity.title,
                resource_type: Some("project".to_string()),
                resource_name: proj_map
                    .get(&activity.project)
                    .map(|p| p.name.clone()),
                metadata: activity.metadata,
                created_at: activity.created_at,
            });
        }

        // Sort by created_at DESC
        items.sort_by(|a, b| b.created_at.cmp(&a.created_at));

        let total = items.len() as u64;
        let page_items: Vec<UserActivityItem> = items.into_iter().skip(offset as usize).take(per_page as usize).collect();

        Ok(UserActivityResponse {
            items: page_items,
            total,
            page,
            per_page,
        })
    }
}

fn format_action_title(action: &str) -> String {
    match action {
        "login" => "Signed in".to_string(),
        "logout" => "Signed out".to_string(),
        "register" => "Created account".to_string(),
        "password_change" => "Changed password".to_string(),
        "password_reset" => "Reset password".to_string(),
        "2fa_enabled" => "Enabled 2FA".to_string(),
        "2fa_disabled" => "Disabled 2FA".to_string(),
        "ssh_key_add" => "Added SSH key".to_string(),
        "ssh_key_delete" => "Removed SSH key".to_string(),
        "ssh_key_update" => "Updated SSH key".to_string(),
        "ssh_key_revoke" => "Revoked SSH key".to_string(),
        "access_key_create" => "Created access token".to_string(),
        "access_key_delete" => "Deleted access token".to_string(),
        "avatar_upload" => "Updated avatar".to_string(),
        "profile_update" => "Updated profile".to_string(),
        _ => format!("Activity: {}", action),
    }
}
