use crate::AppService;
use crate::error::AppError;
use chrono::Utc;
use models::projects::{MemberRole, project_audit_log, project_members};
use models::rooms::{room, room_member, RoomMemberRole};
use models::users::user;
use sea_orm::*;
use sea_orm::sea_query::OnConflict;
use serde::{Deserialize, Serialize};
use session::Session;
use utoipa::ToSchema;
use uuid::Uuid;

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, ToSchema)]
pub struct MemberInfo {
    pub user_id: Uuid,
    pub username: String,
    pub display_name: Option<String>,
    pub avatar_url: Option<String>,
    pub scope: MemberRole,
    pub joined_at: chrono::DateTime<Utc>,
}

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

#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
pub struct UpdateMemberRoleRequest {
    pub user_id: Uuid,
    pub scope: MemberRole,
}

impl AppService {
    pub async fn project_get_members(
        &self,
        project_name: String,
        page: Option<u64>,
        per_page: Option<u64>,
        ctx: &Session,
    ) -> Result<MemberListResponse, AppError> {
        let user_uid = ctx.user().ok_or(AppError::Unauthorized)?;
        let project = self
            .utils_find_project_by_name(project_name.clone())
            .await?;

        let _requester_member = project_members::Entity::find()
            .filter(project_members::Column::Project.eq(project.id))
            .filter(project_members::Column::User.eq(user_uid))
            .one(&self.db)
            .await?;

        let page = page.unwrap_or(1);
        let per_page = per_page.unwrap_or(20);

        let members = project_members::Entity::find()
            .filter(project_members::Column::Project.eq(project.id))
            .order_by_asc(project_members::Column::JoinedAt)
            .paginate(&self.db, per_page)
            .fetch_page(page - 1)
            .await?;

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

        let user_ids: Vec<Uuid> = members.iter().map(|m| m.user).collect();

        let users_data = if user_ids.is_empty() {
            vec![]
        } else {
            user::Entity::find()
                .filter(user::Column::Uid.is_in(user_ids))
                .all(&self.db)
                .await?
        };

        let member_infos: Vec<MemberInfo> = members
            .into_iter()
            .filter_map(|member| {
                let role = member.scope_role().ok()?;
                users_data
                    .iter()
                    .find(|u| u.uid == member.user)
                    .map(|user| MemberInfo {
                        user_id: user.uid,
                        username: user.username.clone(),
                        display_name: user.display_name.clone(),
                        avatar_url: user.avatar_url.clone(),
                        scope: role.clone(),
                        joined_at: member.joined_at,
                    })
            })
            .collect();

        Ok(MemberListResponse {
            members: member_infos,
            total,
            page,
            per_page,
        })
    }

    pub async fn project_update_member_role(
        &self,
        project_name: String,
        request: UpdateMemberRoleRequest,
        ctx: &Session,
    ) -> Result<(), AppError> {
        let actor_uid = ctx.user().ok_or(AppError::Unauthorized)?;
        let project = self
            .utils_find_project_by_name(project_name.clone())
            .await?;

        let actor_member = project_members::Entity::find()
            .filter(project_members::Column::Project.eq(project.id))
            .filter(project_members::Column::User.eq(actor_uid))
            .one(&self.db)
            .await?
            .ok_or(AppError::PermissionDenied)?;

        let actor_role = actor_member
            .scope_role()
            .map_err(|_| AppError::RoleParseError)?;

        if actor_role != MemberRole::Owner && actor_role != MemberRole::Admin {
            return Err(AppError::NoPower);
        }

        let target_member = project_members::Entity::find()
            .filter(project_members::Column::Project.eq(project.id))
            .filter(project_members::Column::User.eq(request.user_id))
            .one(&self.db)
            .await?
            .ok_or(AppError::NotFound("Member not found".to_string()))?;

        let target_role = target_member
            .scope_role()
            .map_err(|_| AppError::RoleParseError)?;

        if target_role == MemberRole::Owner {
            return Err(AppError::NoPower);
        }

        if request.scope == MemberRole::Admin && actor_role != MemberRole::Owner {
            return Err(AppError::NoPower);
        }

        if request.scope == MemberRole::Owner {
            return Err(AppError::NoPower);
        }

        let mut active_member: project_members::ActiveModel = target_member.into();
        active_member.scope = Set(request.scope.to_string());
        active_member.update(&self.db).await?;

        let actor_username = user::Entity::find_by_id(actor_uid)
            .one(&self.db)
            .await
            .ok()
            .flatten()
            .map(|u| u.username)
            .unwrap_or_default();
        let target_username = user::Entity::find_by_id(request.user_id)
            .one(&self.db)
            .await
            .ok()
            .flatten()
            .map(|u| u.username)
            .unwrap_or_default();
        let _ = self
            .project_log_activity(
                project.id,
                None,
                actor_uid,
                super::activity::ActivityLogParams {
                    event_type: "member_role_change".to_string(),
                    title: format!(
                        "{} changed {}'s role to {}",
                        actor_username, target_username, request.scope
                    ),
                    repo_id: None,
                    content: None,
                    event_id: None,
                    event_sub_id: None,
                    metadata: Some(serde_json::json!({
                        "target_user_id": request.user_id.to_string(),
                        "new_role": request.scope.to_string(),
                    })),
                    is_private: false,
                },
            )
            .await;

        let log = project_audit_log::ActiveModel {
            project: Set(project.id),
            actor: Set(actor_uid),
            action: Set("update_member_role".to_string()),
            details: Set(Some(serde_json::json!({
                "project_name": project.name,
                "target_user_id": request.user_id,
                "new_role": request.scope.to_string(),
            }))),
            created_at: Set(Utc::now()),
            ..Default::default()
        };
        log.insert(&self.db).await?;

        Ok(())
    }

    pub async fn project_remove_member(
        &self,
        project_name: String,
        user_id: Uuid,
        ctx: &Session,
    ) -> Result<(), AppError> {
        let actor_uid = ctx.user().ok_or(AppError::Unauthorized)?;
        let project = self
            .utils_find_project_by_name(project_name.clone())
            .await?;

        let actor_member = project_members::Entity::find()
            .filter(project_members::Column::Project.eq(project.id))
            .filter(project_members::Column::User.eq(actor_uid))
            .one(&self.db)
            .await?
            .ok_or(AppError::PermissionDenied)?;

        let actor_role = actor_member
            .scope_role()
            .map_err(|_| AppError::RoleParseError)?;

        if actor_role != MemberRole::Owner && actor_role != MemberRole::Admin {
            return Err(AppError::NoPower);
        }

        let target_member = project_members::Entity::find()
            .filter(project_members::Column::Project.eq(project.id))
            .filter(project_members::Column::User.eq(user_id))
            .one(&self.db)
            .await?
            .ok_or(AppError::NotFound("Member not found".to_string()))?;

        let target_role = target_member
            .scope_role()
            .map_err(|_| AppError::RoleParseError)?;

        if target_role == MemberRole::Owner {
            return Err(AppError::NoPower);
        }

        if actor_role == MemberRole::Admin && target_role == MemberRole::Admin {
            return Err(AppError::NoPower);
        }

        project_members::Entity::delete_many()
            .filter(project_members::Column::Project.eq(project.id))
            .filter(project_members::Column::User.eq(user_id))
            .exec(&self.db)
            .await?;

        let actor_username = user::Entity::find_by_id(actor_uid)
            .one(&self.db)
            .await
            .ok()
            .flatten()
            .map(|u| u.username)
            .unwrap_or_default();
        let target_username = user::Entity::find_by_id(user_id)
            .one(&self.db)
            .await
            .ok()
            .flatten()
            .map(|u| u.username)
            .unwrap_or_default();
        let _ = self
            .project_log_activity(
                project.id,
                None,
                actor_uid,
                super::activity::ActivityLogParams {
                    event_type: "member_remove".to_string(),
                    title: format!(
                        "{} removed {} from the project",
                        actor_username, target_username
                    ),
                    repo_id: None,
                    content: None,
                    event_id: None,
                    event_sub_id: None,
                    metadata: Some(serde_json::json!({
                        "removed_user_id": user_id.to_string(),
                    })),
                    is_private: false,
                },
            )
            .await;

        let log = project_audit_log::ActiveModel {
            project: Set(project.id),
            actor: Set(actor_uid),
            action: Set("remove_member".to_string()),
            details: Set(Some(serde_json::json!({
                "project_name": project.name,
                "removed_user_id": user_id,
            }))),
            created_at: Set(Utc::now()),
            ..Default::default()
        };
        log.insert(&self.db).await?;

        Ok(())
    }

    /// Add a user to all rooms in a project (used automatically when a user joins a project).
    pub async fn add_user_to_all_project_rooms(
        db: &impl ConnectionTrait,
        project_id: Uuid,
        user_id: Uuid,
    ) -> Result<(), AppError> {
        let rooms: Vec<room::Model> = room::Entity::find()
            .filter(room::Column::Project.eq(project_id))
            .all(db)
            .await?;

        if rooms.is_empty() {
            return Ok(());
        }

        let now = Utc::now();
        let values: Vec<room_member::ActiveModel> = rooms
            .iter()
            .map(|r| room_member::ActiveModel {
                room: Set(r.id),
                user: Set(user_id),
                role: Set(RoomMemberRole::Member),
                first_msg_in: Set(None),
                joined_at: Set(Some(now)),
                last_read_seq: Set(None),
                do_not_disturb: Set(false),
                dnd_start_hour: Set(None),
                dnd_end_hour: Set(None),
            })
            .collect();

        room_member::Entity::insert_many(values)
            .on_conflict(OnConflict::new().do_nothing().to_owned())
            .exec(db)
            .await?;

        Ok(())
    }
}
