use db::database::AppDatabase;
use models::projects::project_members;
use models::rooms::room;
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};
use uuid::Uuid;

use crate::error::RoomError;

pub async fn check_room_access(
    db: &AppDatabase,
    room_id: Uuid,
    user_id: Uuid,
) -> Result<(), RoomError> {
    let room = room::Entity::find_by_id(room_id)
        .one(db)
        .await?
        .ok_or_else(|| RoomError::NotFound("Room not found".to_string()))?;

    if room.public {
        return Ok(());
    }

    if require_room_member(db, room_id, user_id).await.is_ok() {
        return Ok(());
    }

    check_project_member(db, room.project, user_id).await?;

    Ok(())
}

pub async fn check_project_member(
    db: &AppDatabase,
    project_id: Uuid,
    user_id: Uuid,
) -> Result<(), RoomError> {
    let member = project_members::Entity::find()
        .filter(project_members::Column::Project.eq(project_id))
        .filter(project_members::Column::User.eq(user_id))
        .one(db)
        .await?;

    if member.is_some() {
        Ok(())
    } else {
        Err(RoomError::NoPower)
    }
}

pub async fn require_room_member(
    db: &AppDatabase,
    room_id: Uuid,
    user_id: Uuid,
) -> Result<(), RoomError> {
    use models::rooms::room_member::{Column as RmCol, Entity as RoomMember};

    let member = RoomMember::find()
        .filter(RmCol::Room.eq(room_id))
        .filter(RmCol::User.eq(user_id))
        .one(db)
        .await?;

    member
        .ok_or_else(|| RoomError::NotFound("Room member not found".to_string()))
        .map(|_| ())
}

pub async fn find_room_or_404(db: &AppDatabase, room_id: Uuid) -> Result<room::Model, RoomError> {
    room::Entity::find_by_id(room_id)
        .one(db)
        .await?
        .ok_or_else(|| RoomError::NotFound("Room not found".to_string()))
}
