use crate::AppService;
use crate::error::AppError;
use chrono::{DateTime, Utc};
use models::projects::{MemberRole, project_label, project_like, project_members, project_watch};
use models::system::label;
use models::users::user;
use sea_orm::*;
use serde::{Deserialize, Serialize};
use session::Session;
use utoipa::ToSchema;
use uuid::Uuid;

#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub struct ProjectInfoRelational {
    pub uid: Uuid,
    pub name: String,
    pub display_name: String,
    pub avatar_url: Option<String>,
    pub description: Option<String>,
    pub is_public: bool,
    pub created_at: DateTime<Utc>,
    pub updated_at: DateTime<Utc>,

    pub created_by: Uuid,
    pub created_username_name: String,
    pub created_display_name: Option<String>,
    pub created_avatar_url: Option<String>,

    pub member_count: i64,
    pub like_count: i64,
    pub watch_count: i64,
    pub keys: Vec<ProjectInfoKeyValue>,
    pub labels: Vec<ProjectInfoLabel>,
    pub role: Option<MemberRole>,
    pub is_like: bool,
    pub is_watch: bool,
}

#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub struct ProjectInfoKeyValue {
    pub key: String,
    pub value: String,
}

#[derive(Debug, Clone, Deserialize, Serialize, ToSchema)]
pub struct ProjectInfoLabel {
    pub name: String,
    pub color: String,
}

impl AppService {
    pub async fn project_info(
        &self,
        ctx: &Session,
        project_name: String,
    ) -> Result<ProjectInfoRelational, AppError> {
        let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
        let project = self.utils_find_project_by_name(project_name).await?;
        let is_member = self
            .utils_project_context_role(&ctx, project.name.clone())
            .await
            .ok();
        if !project.is_public && is_member.is_none() {
            return Err(AppError::NotFound("Project not found".to_string()));
        }

        let creator = user::Entity::find()
            .filter(user::Column::Uid.eq(project.created_by))
            .one(&self.db)
            .await?
            .ok_or(AppError::UserNotFound)?;

        let member_count = project_members::Entity::find()
            .filter(project_members::Column::Project.eq(project.id))
            .count(&self.db)
            .await?;

        let like_count = project_like::Entity::find()
            .filter(project_like::Column::Project.eq(project.id))
            .count(&self.db)
            .await?;

        let watch_count = project_watch::Entity::find()
            .filter(project_watch::Column::Project.eq(project.id))
            .count(&self.db)
            .await?;

        let labels_model = project_label::Entity::find()
            .filter(project_label::Column::Project.eq(project.id))
            .all(&self.db)
            .await?
            .iter()
            .map(|x| x.label)
            .collect::<Vec<i64>>();
        let labes = label::Entity::find()
            .filter(label::Column::Id.is_in(labels_model))
            .all(&self.db)
            .await?;

        let labels = labes
            .into_iter()
            .map(|l| ProjectInfoLabel {
                name: l.name,
                color: l.color,
            })
            .collect();
        let is_like = project_like::Entity::find()
            .filter(project_like::Column::Project.eq(project.id))
            .filter(project_like::Column::User.eq(user_uid))
            .count(&self.db)
            .await?
            > 0;
        let is_watch = project_watch::Entity::find()
            .filter(project_watch::Column::Project.eq(project.id))
            .filter(project_watch::Column::User.eq(user_uid))
            .count(&self.db)
            .await?
            > 0;

        Ok(ProjectInfoRelational {
            uid: project.id,
            name: project.name,
            display_name: project.display_name,
            avatar_url: project.avatar_url,
            description: project.description,
            is_public: project.is_public,
            created_at: project.created_at,
            updated_at: project.updated_at,
            created_by: project.created_by,
            created_username_name: creator.username,
            created_display_name: creator.display_name,
            created_avatar_url: creator.avatar_url,
            member_count: member_count as i64,
            like_count: like_count as i64,
            watch_count: watch_count as i64,
            keys: Vec::new(),
            labels,
            role: is_member,
            is_like,
            is_watch,
        })
    }
}
