use crate::AppService;
use crate::error::AppError;
use chrono::{DateTime, Utc};
use sea_orm::*;
use serde::{Deserialize, Serialize};
use session::Session;
use utoipa::ToSchema;
use uuid::Uuid;

#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
pub struct UserInfoExternal {
    pub user_uid: Uuid,
    pub username: String,
    pub display_name: String,
    pub avatar_url: Option<String>,
    pub master_email: Option<String>,
    pub timezone: String,
    pub language: String,
    pub website_url: Option<String>,
    pub organization: Option<String>,
    pub last_sign_in_at: Option<DateTime<Utc>>,

    pub is_owner: bool,
    pub is_subscribe: bool,

    pub total_projects: u64,
    pub total_repos: u64,
}

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

        let preferences = models::users::user_preferences::Entity::find()
            .filter(models::users::user_preferences::Column::User.eq(user.uid))
            .one(&self.db)
            .await?;

        let master_email = models::users::user_email::Entity::find()
            .filter(models::users::user_email::Column::User.eq(user.uid))
            .one(&self.db)
            .await?
            .map(|email| email.email);

        let is_subscribe = if let Some(current_uid) = context.user() {
            let relation = models::users::user_relation::Entity::find()
                .filter(models::users::user_relation::Column::User.eq(current_uid))
                .filter(models::users::user_relation::Column::Target.eq(user.uid))
                .filter(models::users::user_relation::Column::RelationType.eq("follow"))
                .one(&self.db)
                .await?;
            relation.is_some()
        } else {
            false
        };

        // Get user's project memberships to find repos
        let user_project_ids: Vec<uuid::Uuid> = models::projects::project_members::Entity::find()
            .filter(models::projects::project_members::Column::User.eq(user.uid))
            .select_only()
            .column(models::projects::project_members::Column::Project)
            .into_tuple::<uuid::Uuid>()
            .all(&self.db)
            .await?;

        // Count projects created by user
        let total_projects = models::projects::project::Entity::find()
            .filter(models::projects::project::Column::CreatedBy.eq(user.uid))
            .count(&self.db)
            .await?;

        // Count repos in user's projects
        let total_repos = models::repos::repo::Entity::find()
            .filter(models::repos::repo::Column::Project.is_in(user_project_ids))
            .count(&self.db)
            .await?;

        Ok(UserInfoExternal {
            user_uid: user.uid,
            username: user.username,
            display_name: user.display_name.unwrap_or_default(),
            avatar_url: user.avatar_url,
            master_email,
            timezone: preferences
                .as_ref()
                .map(|p| p.timezone.clone())
                .unwrap_or_else(|| "UTC".to_string()),
            language: preferences
                .as_ref()
                .map(|p| p.language.clone())
                .unwrap_or_else(|| "en".to_string()),
            website_url: user.website_url,
            organization: user.organization,
            last_sign_in_at: user.last_sign_in_at,
            is_owner: context.user().map(|u| u == user.uid).unwrap_or(false),
            is_subscribe,
            total_projects,
            total_repos,
        })
    }
}
