use chrono::Utc;
use db::database::AppDatabase;
use models::projects::project_members;
use queue::ProjectRoomEvent;
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};
use uuid::Uuid;

use crate::error::RoomError;

pub fn notify_project_members(
    db: AppDatabase,
    project_id: Uuid,
    notification_type: crate::NotificationType,
    title: String,
    content: Option<String>,
    related_room_id: Option<Uuid>,
) {
    let notification_type_inner = notification_type;
    let title_inner = title;
    let content_inner = content;
    let related_room_id_inner = related_room_id;
    let project_id_inner = project_id;

    tokio::spawn(async move {
        let members = match project_members::Entity::find()
            .filter(project_members::Column::Project.eq(project_id_inner))
            .all(&db)
            .await
        {
            Ok(m) => m,
            Err(e) => {
                tracing::error!(project_id = %project_id_inner, error = %e,
                    "notify_project_members: failed to fetch members");
                return;
            }
        };

        for member in members {
            let user_id = member.user;
            if let Err(e) = create_notification_sync(
                &db,
                notification_type_inner,
                user_id,
                title_inner.clone(),
                content_inner.clone(),
                related_room_id_inner,
                project_id_inner,
            )
            .await
            {
                tracing::warn!(user_id = %user_id, project_id = %project_id_inner, error = %e,
                    "notify_project_members: failed to create notification for user");
            }
        }
    });
}

async fn create_notification_sync(
    db: &AppDatabase,
    notification_type: crate::NotificationType,
    user_id: Uuid,
    title: String,
    content: Option<String>,
    related_room_id: Option<Uuid>,
    project_id: Uuid,
) -> Result<(), RoomError> {
    use models::rooms::room_notifications;
    use sea_orm::{ActiveModelTrait, Set};

    let notification_type_model = match notification_type {
        crate::NotificationType::Mention => room_notifications::NotificationType::Mention,
        crate::NotificationType::Invitation => room_notifications::NotificationType::Invitation,
        crate::NotificationType::RoleChange => room_notifications::NotificationType::RoleChange,
        crate::NotificationType::RoomCreated => room_notifications::NotificationType::RoomCreated,
        crate::NotificationType::RoomDeleted => room_notifications::NotificationType::RoomDeleted,
        crate::NotificationType::SystemAnnouncement => {
            room_notifications::NotificationType::SystemAnnouncement
        }
        crate::NotificationType::ProjectInvitation => {
            room_notifications::NotificationType::ProjectInvitation
        }
        crate::NotificationType::WorkspaceInvitation => {
            room_notifications::NotificationType::WorkspaceInvitation
        }
    };

    let _model = room_notifications::ActiveModel {
        id: Set(Uuid::now_v7()),
        room: Set(related_room_id),
        project: Set(Some(project_id)),
        user_id: Set(Some(user_id)),
        notification_type: Set(notification_type_model),
        related_message_id: Set(None),
        related_user_id: Set(None),
        related_room_id: Set(related_room_id),
        title: Set(title),
        content: Set(content),
        metadata: Set(None),
        is_read: Set(false),
        is_archived: Set(false),
        created_at: Set(Utc::now()),
        read_at: Set(None),
        expires_at: Set(None),
    }
    .insert(db)
    .await
    .map_err(|e| RoomError::Database(e))?;

    Ok(())
}

pub fn publish_room_event(
    queue: &queue::MessageProducer,
    project_id: Uuid,
    event_type: crate::RoomEventType,
    room_id: Option<Uuid>,
    message_id: Option<Uuid>,
    seq: Option<i64>,
) {
    let event = ProjectRoomEvent {
        event_type: event_type.as_str().into(),
        project_id,
        room_id,
        category_id: None,
        message_id,
        seq,
        timestamp: Utc::now(),
    };
    // Fire-and-forget — caller doesn't need to await.
    let queue = queue.clone();
    tokio::spawn(async move {
        queue.publish_project_room_event(project_id, event).await;
    });
}
