use crate::error::RoomError;
use crate::service::RoomService;
use crate::ws_context::WsUserContext;
use chrono::Utc;
use models::projects::project_members;
use models::rooms::{RoomMemberRole, room_member};
use models::users::user as user_model;
use sea_orm::*;
use uuid::Uuid;

impl RoomService {
    pub async fn room_member_list(
        &self,
        room_id: Uuid,
        ctx: &WsUserContext,
    ) -> Result<Vec<super::RoomMemberResponse>, RoomError> {
        let user_id = ctx.user_id;
        self.require_room_member(room_id, user_id).await?;

        let members = room_member::Entity::find()
            .filter(room_member::Column::Room.eq(room_id))
            .all(&self.db)
            .await?;

        let user_ids: Vec<Uuid> = members.iter().map(|m| m.user).collect();
        let users: std::collections::HashMap<Uuid, super::UserInfo> = if !user_ids.is_empty() {
            use sea_orm::ColumnTrait;
            user_model::Entity::find()
                .filter(user_model::Column::Uid.is_in(user_ids))
                .all(&self.db)
                .await?
                .into_iter()
                .map(|u| {
                    (
                        u.uid,
                        super::UserInfo {
                            uid: u.uid,
                            username: u.username,
                            avatar_url: u.avatar_url,
                        },
                    )
                })
                .collect()
        } else {
            std::collections::HashMap::new()
        };

        let responses = members
            .into_iter()
            .map(|m| super::RoomMemberResponse {
                room: m.room,
                user: m.user,
                user_info: users.get(&m.user).cloned(),
                role: m.role.to_string(),
                first_msg_in: m.first_msg_in,
                joined_at: m.joined_at,
                last_read_seq: m.last_read_seq,
                do_not_disturb: m.do_not_disturb,
                dnd_start_hour: m.dnd_start_hour,
                dnd_end_hour: m.dnd_end_hour,
            })
            .collect();
        Ok(responses)
    }

    pub async fn room_member_add(
        &self,
        room_id: Uuid,
        request: super::RoomMemberAddRequest,
        ctx: &WsUserContext,
    ) -> Result<super::RoomMemberResponse, RoomError> {
        let actor_id = ctx.user_id;
        let room_model = self.find_room_or_404(room_id).await?;
        self.require_room_admin(room_id, actor_id).await?;

        let target_project_member = project_members::Entity::find()
            .filter(project_members::Column::Project.eq(room_model.project))
            .filter(project_members::Column::User.eq(request.user_id))
            .one(&self.db)
            .await?;
        if target_project_member.is_none() {
            return Err(RoomError::NoPower);
        }

        if let Some(existing) = self.find_room_member(room_id, request.user_id).await? {
            let user_info = user_model::Entity::find()
                .filter(user_model::Column::Uid.eq(request.user_id))
                .one(&self.db)
                .await
                .ok()
                .flatten()
                .map(|u| super::UserInfo {
                    uid: u.uid,
                    username: u.username,
                    avatar_url: u.avatar_url,
                });
            let mut response = super::RoomMemberResponse::from(existing);
            response.user_info = user_info;
            return Ok(response);
        }

        let role = if let Some(role) = request.role {
            Self::parse_room_member_role(&role.to_lowercase())?
        } else {
            RoomMemberRole::Member
        };

        let created = room_member::ActiveModel {
            room: Set(room_id),
            user: Set(request.user_id),
            role: Set(role),
            first_msg_in: Set(None),
            joined_at: Set(Some(Utc::now())),
            last_read_seq: Set(None),
            do_not_disturb: Set(false),
            dnd_start_hour: Set(None),
            dnd_end_hour: Set(None),
        }
        .insert(&self.db)
        .await?;

        drop(self.room_manager.subscribe(room_id, request.user_id).await);

        self.publish_room_event(
            room_model.project,
            super::RoomEventType::MemberJoined,
            Some(room_id),
            None,
            None,
            None,
        )
        .await;

        let _ = self
            .notification_create(super::NotificationCreateRequest {
                notification_type: super::NotificationType::Invitation,
                user_id: request.user_id,
                title: format!("你已被邀请加入房间 {}", room_model.room_name),
                content: None,
                room_id: Some(room_id),
                project_id: room_model.project,
                related_message_id: None,
                related_user_id: Some(actor_id),
                related_room_id: Some(room_id),
                metadata: None,
                expires_at: None,
            })
            .await;

        let created_response = {
            let user_info = user_model::Entity::find()
                .filter(user_model::Column::Uid.eq(request.user_id))
                .one(&self.db)
                .await
                .ok()
                .flatten()
                .map(|u| super::UserInfo {
                    uid: u.uid,
                    username: u.username,
                    avatar_url: u.avatar_url,
                });
            let mut r = super::RoomMemberResponse::from(created);
            r.user_info = user_info;
            r
        };
        Ok(created_response)
    }

    pub async fn room_member_update_role(
        &self,
        room_id: Uuid,
        request: super::RoomMemberRoleUpdateRequest,
        ctx: &WsUserContext,
    ) -> Result<super::RoomMemberResponse, RoomError> {
        let actor_id = ctx.user_id;
        let actor = self.require_room_admin(room_id, actor_id).await?;
        let target = self
            .find_room_member(room_id, request.user_id)
            .await?
            .ok_or_else(|| RoomError::NotFound("Room member not found".to_string()))?;

        if target.role == RoomMemberRole::Owner {
            return Err(RoomError::NoPower);
        }

        let new_role = Self::parse_room_member_role(&request.role.to_lowercase())?;
        if matches!(new_role, RoomMemberRole::Owner) {
            return Err(RoomError::NoPower);
        }
        if actor.role != RoomMemberRole::Owner && matches!(new_role, RoomMemberRole::Admin) {
            return Err(RoomError::NoPower);
        }

        let old_role = target.role.clone();
        let new_role_cloned = new_role.clone();

        let mut active: room_member::ActiveModel = target.into();
        active.role = Set(new_role);
        let updated = active.update(&self.db).await?;

        let room = self.find_room_or_404(room_id).await?;
        let _ = self
            .notification_create(super::NotificationCreateRequest {
                notification_type: super::NotificationType::RoleChange,
                user_id: request.user_id,
                title: format!(
                    "你在房间 {} 的角色已变更为 {}",
                    room.room_name, new_role_cloned
                ),
                content: None,
                room_id: Some(room_id),
                project_id: room.project,
                related_message_id: None,
                related_user_id: Some(actor_id),
                related_room_id: Some(room_id),
                metadata: Some(serde_json::json!({
                    "old_role": old_role.to_string(),
                    "new_role": new_role_cloned.to_string(),
                })),
                expires_at: None,
            })
            .await;

        let updated_response = {
            let user_info = user_model::Entity::find()
                .filter(user_model::Column::Uid.eq(request.user_id))
                .one(&self.db)
                .await
                .ok()
                .flatten()
                .map(|u| super::UserInfo {
                    uid: u.uid,
                    username: u.username,
                    avatar_url: u.avatar_url,
                });
            let mut r = super::RoomMemberResponse::from(updated);
            r.user_info = user_info;
            r
        };
        Ok(updated_response)
    }

    pub async fn room_member_remove(
        &self,
        room_id: Uuid,
        user_id: Uuid,
        ctx: &WsUserContext,
    ) -> Result<(), RoomError> {
        let actor_id = ctx.user_id;
        let actor = self.require_room_admin(room_id, actor_id).await?;
        let target = self
            .find_room_member(room_id, user_id)
            .await?
            .ok_or_else(|| RoomError::NotFound("Room member not found".to_string()))?;

        if target.role == RoomMemberRole::Owner {
            return Err(RoomError::NoPower);
        }
        if actor.role == RoomMemberRole::Admin && target.role == RoomMemberRole::Admin {
            return Err(RoomError::NoPower);
        }

        room_member::Entity::delete_by_id((room_id, user_id))
            .exec(&self.db)
            .await?;

        self.room_manager.unsubscribe(room_id, user_id).await;

        let room = self.find_room_or_404(room_id).await?;
        self.publish_room_event(
            room.project,
            super::RoomEventType::MemberRemoved,
            Some(room_id),
            None,
            None,
            None,
        )
        .await;

        Ok(())
    }

    pub async fn room_member_set_read_seq(
        &self,
        room_id: Uuid,
        request: super::RoomMemberReadSeqRequest,
        ctx: &WsUserContext,
    ) -> Result<super::RoomMemberResponse, RoomError> {
        let user_id = ctx.user_id;
        let member = self.require_room_member_model(room_id, user_id).await?;

        let mut active: room_member::ActiveModel = member.into();
        active.last_read_seq = Set(Some(request.last_read_seq));
        let updated = active.update(&self.db).await?;

        let room = self.find_room_or_404(room_id).await?;
        self.publish_room_event(
            room.project,
            super::RoomEventType::ReadReceipt,
            Some(room_id),
            None,
            Some(user_id),
            Some(request.last_read_seq),
        )
        .await;

        let updated_response = {
            let user_info = user_model::Entity::find()
                .filter(user_model::Column::Uid.eq(user_id))
                .one(&self.db)
                .await
                .ok()
                .flatten()
                .map(|u| super::UserInfo {
                    uid: u.uid,
                    username: u.username,
                    avatar_url: u.avatar_url,
                });
            let mut r = super::RoomMemberResponse::from(updated);
            r.user_info = user_info;
            r
        };
        Ok(updated_response)
    }

    pub async fn room_member_update_dnd(
        &self,
        room_id: Uuid,
        request: super::RoomMemberUpdateDndRequest,
        ctx: &WsUserContext,
    ) -> Result<super::RoomMemberResponse, RoomError> {
        let user_id = ctx.user_id;
        let member = self.require_room_member_model(room_id, user_id).await?;

        let mut active: room_member::ActiveModel = member.into();
        if let Some(dnd) = request.do_not_disturb {
            active.do_not_disturb = Set(dnd);
        }
        if let Some(start) = request.dnd_start_hour {
            if !(0..=23).contains(&start) {
                return Err(RoomError::BadRequest("dnd_start_hour must be 0-23".into()));
            }
            active.dnd_start_hour = Set(Some(start));
        }
        if let Some(end) = request.dnd_end_hour {
            if !(0..=23).contains(&end) {
                return Err(RoomError::BadRequest("dnd_end_hour must be 0-23".into()));
            }
            active.dnd_end_hour = Set(Some(end));
        }
        let updated = active.update(&self.db).await?;
        let updated_response = {
            let user_info = user_model::Entity::find()
                .filter(user_model::Column::Uid.eq(user_id))
                .one(&self.db)
                .await
                .ok()
                .flatten()
                .map(|u| super::UserInfo {
                    uid: u.uid,
                    username: u.username,
                    avatar_url: u.avatar_url,
                });
            let mut r = super::RoomMemberResponse::from(updated);
            r.user_info = user_info;
            r
        };
        Ok(updated_response)
    }
}
