use crate::AppService;
use crate::error::AppError;
use chrono::{DateTime, Utc};
use models::projects::project;
use models::projects::project_activity;
use models::users::user;
use models::workspaces::workspace;
use models::workspaces::workspace_membership;
use sea_orm::*;
use serde::{Deserialize, Serialize};
use session::Session;
use utoipa::IntoParams;

#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct WorkspaceInfoResponse {
    pub id: Uuid,
    pub slug: String,
    pub name: String,
    pub description: Option<String>,
    pub avatar_url: Option<String>,
    pub plan: String,
    pub billing_email: Option<String>,
    pub member_count: i64,
    pub my_role: Option<String>,
    pub created_at: chrono::DateTime<Utc>,
    pub updated_at: chrono::DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct WorkspaceListItem {
    pub id: Uuid,
    pub slug: String,
    pub name: String,
    pub description: Option<String>,
    pub avatar_url: Option<String>,
    pub plan: String,
    pub my_role: String,
    pub created_at: chrono::DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct WorkspaceListResponse {
    pub workspaces: Vec<WorkspaceListItem>,
    pub total: u64,
}

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

#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct WorkspaceProjectItem {
    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>,
}

#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct WorkspaceProjectsResponse {
    pub projects: Vec<WorkspaceProjectItem>,
    pub total: u64,
    pub page: u64,
    pub per_page: u64,
}

#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct WorkspaceActivityItem {
    pub id: i64,
    pub project_name: String,
    pub event_type: String,
    pub title: String,
    pub content: Option<String>,
    pub actor_name: String,
    pub actor_avatar: Option<String>,
    pub created_at: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
pub struct WorkspaceStatsResponse {
    pub project_count: i64,
    pub member_count: i64,
    pub my_role: Option<String>,
    pub recent_activities: Vec<WorkspaceActivityItem>,
}

impl AppService {
    /// Get workspace info by slug. Returns error if user is not a member.
    pub async fn workspace_info(
        &self,
        ctx: &Session,
        slug: String,
    ) -> Result<WorkspaceInfoResponse, AppError> {
        let user_uid = ctx.user();
        let ws = self.utils_find_workspace_by_slug(slug.clone()).await?;

        let my_role = if let Some(uid) = user_uid {
            workspace_membership::Entity::find()
                .filter(workspace_membership::Column::WorkspaceId.eq(ws.id))
                .filter(workspace_membership::Column::UserId.eq(uid))
                .filter(workspace_membership::Column::Status.eq("active"))
                .one(&self.db)
                .await?
                .map(|m| m.role)
        } else {
            None
        };

        let member_count = workspace_membership::Entity::find()
            .filter(workspace_membership::Column::WorkspaceId.eq(ws.id))
            .filter(workspace_membership::Column::Status.eq("active"))
            .count(&self.db)
            .await?;

        Ok(WorkspaceInfoResponse {
            id: ws.id,
            slug: ws.slug,
            name: ws.name,
            description: ws.description,
            avatar_url: ws.avatar_url,
            plan: ws.plan,
            billing_email: ws.billing_email,
            member_count: member_count as i64,
            my_role,
            created_at: ws.created_at,
            updated_at: ws.updated_at,
        })
    }

    /// List all workspaces the current user is a member of.
    pub async fn workspace_list(&self, ctx: &Session) -> Result<WorkspaceListResponse, AppError> {
        let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;

        let memberships = workspace_membership::Entity::find()
            .filter(workspace_membership::Column::UserId.eq(user_uid))
            .filter(workspace_membership::Column::Status.eq("active"))
            .all(&self.db)
            .await?;

        let workspace_ids: Vec<Uuid> = memberships.iter().map(|m| m.workspace_id).collect();
        let total = workspace_ids.len() as u64;

        let workspaces = workspace::Entity::find()
            .filter(workspace::Column::Id.is_in(workspace_ids.clone()))
            .filter(workspace::Column::DeletedAt.is_null())
            .all(&self.db)
            .await?;

        let items: Vec<WorkspaceListItem> = workspaces
            .into_iter()
            .filter_map(|ws| {
                let membership = memberships
                    .iter()
                    .find(|m| m.workspace_id == ws.id)
                    .cloned()?;
                Some(WorkspaceListItem {
                    id: ws.id,
                    slug: ws.slug,
                    name: ws.name,
                    description: ws.description,
                    avatar_url: ws.avatar_url,
                    plan: ws.plan,
                    my_role: membership.role,
                    created_at: ws.created_at,
                }
            })
            .collect();

        Ok(WorkspaceListResponse {
            workspaces: items,
            total,
        })
    }

    /// List projects belonging to a workspace.
    pub async fn workspace_projects(
        &self,
        ctx: &Session,
        workspace_slug: String,
        query: WorkspaceProjectsQuery,
    ) -> Result<WorkspaceProjectsResponse, AppError> {
        let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
        let ws = self.utils_find_workspace_by_slug(workspace_slug).await?;
        let _ = workspace_membership::Entity::find()
            .filter(workspace_membership::Column::WorkspaceId.eq(ws.id))
            .filter(workspace_membership::Column::UserId.eq(user_uid))
            .filter(workspace_membership::Column::Status.eq("active"))
            .one(&self.db)
            .await?
            .ok_or(AppError::NotWorkspaceMember)?;

        let page = std::cmp::max(query.page.unwrap_or(1), 1);
        let per_page = query.per_page.unwrap_or(20).clamp(1, 200);

        let paginator = project::Entity::find()
            .filter(project::Column::WorkspaceId.eq(ws.id))
            .order_by_desc(project::Column::CreatedAt)
            .paginate(&self.db, per_page);
        let total = paginator.num_items().await?;
        let rows = paginator.fetch_page(page - 1).await?;

        let projects = rows
            .into_iter()
            .map(|p| WorkspaceProjectItem {
                uid: p.id,
                name: p.name,
                display_name: p.display_name,
                avatar_url: p.avatar_url,
                description: p.description,
                is_public: p.is_public,
                created_at: p.created_at,
                updated_at: p.updated_at,
            })
            .collect();

        Ok(WorkspaceProjectsResponse {
            projects,
            total,
            page,
            per_page,
        })
    }

    /// Get workspace stats: project count, member count, recent activities.
    pub async fn workspace_stats(
        &self,
        ctx: &Session,
        workspace_slug: String,
    ) -> Result<WorkspaceStatsResponse, AppError> {
        let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
        let ws = self.utils_find_workspace_by_slug(workspace_slug).await?;

        let membership = workspace_membership::Entity::find()
            .filter(workspace_membership::Column::WorkspaceId.eq(ws.id))
            .filter(workspace_membership::Column::UserId.eq(user_uid))
            .filter(workspace_membership::Column::Status.eq("active"))
            .one(&self.db)
            .await?;

        let member_count = workspace_membership::Entity::find()
            .filter(workspace_membership::Column::WorkspaceId.eq(ws.id))
            .filter(workspace_membership::Column::Status.eq("active"))
            .count(&self.db)
            .await?;

        let project_count = project::Entity::find()
            .filter(project::Column::WorkspaceId.eq(ws.id))
            .count(&self.db)
            .await?;

        // Get recent activities across all workspace projects
        let workspace_projects = project::Entity::find()
            .filter(project::Column::WorkspaceId.eq(ws.id))
            .all(&self.db)
            .await?;
        let project_ids: Vec<Uuid> = workspace_projects.iter().map(|p| p.id).collect();
        let project_names: std::collections::HashMap<Uuid, String> = workspace_projects
            .into_iter()
            .map(|p| (p.id, p.name))
            .collect();

        let recent_activities = if project_ids.is_empty() {
            Vec::new()
        } else {
            let activities = project_activity::Entity::find()
                .filter(project_activity::Column::Project.is_in(project_ids.clone()))
                .filter(project_activity::Column::IsPrivate.eq(false))
                .order_by_desc(project_activity::Column::CreatedAt)
                .limit(10)
                .all(&self.db)
                .await?;

            // Collect actor IDs
            let actor_ids: Vec<Uuid> = activities.iter().map(|a| a.actor).collect();
            let actors = user::Entity::find()
                .filter(user::Column::Uid.is_in(actor_ids))
                .all(&self.db)
                .await?;
            let actor_map: std::collections::HashMap<Uuid, (String, Option<String>)> = actors
                .into_iter()
                .map(|u| {
                    (
                        u.uid,
                        (
                            u.display_name.or(Some(u.username)).unwrap_or_default(),
                            u.avatar_url,
                        ),
                    )
                })
                .collect();

            activities
                .into_iter()
                .map(|a| {
                    let (actor_name, actor_avatar) = actor_map
                        .get(&a.actor)
                        .cloned()
                        .unwrap_or_else(|| ("Unknown".to_string(), None));
                    WorkspaceActivityItem {
                        id: a.id,
                        project_name: project_names.get(&a.project).cloned().unwrap_or_default(),
                        event_type: a.event_type,
                        title: a.title,
                        content: a.content,
                        actor_name,
                        actor_avatar,
                        created_at: a.created_at,
                    }
                })
                .collect()
        };

        Ok(WorkspaceStatsResponse {
            project_count: project_count as i64,
            member_count: member_count as i64,
            my_role: membership.map(|m| m.role),
            recent_activities,
        })
    }
}

use uuid::Uuid;
