use chrono::Utc;
use uuid::Uuid;

use crate::event::{RoomInfo, UserInfo, pin};
use crate::{ChannelBus, ChannelResult};

use super::WsHandler;
use super::WsOutEvent;

impl WsHandler {
    pub(super) async fn pin_add(
        bus: &ChannelBus,
        user_id: Uuid,
        room: Uuid,
        message: Uuid,
    ) -> ChannelResult<Option<WsOutEvent>> {
        Self::ensure_room_access(bus, user_id, room).await?;
        Self::ensure_message_in_room(bus, room, message).await?;
        let seq = bus.inner.seq.seq(room).await?;
        let result = db::sqlx::query(
            "INSERT INTO room_pin (room, message, seq, pinned_by, created_at) \
             VALUES ($1, $2, $3, $4, now()) \
             ON CONFLICT DO NOTHING",
        )
        .bind(room)
        .bind(message)
        .bind(seq)
        .bind(user_id)
        .execute(bus.inner.db.writer())
        .await?;
        if result.rows_affected() == 0 {
            return Ok(None);
        }
        db::sqlx::query("UPDATE room_message SET pinned = true, updated_at = now() WHERE id = $1")
            .bind(message)
            .execute(bus.inner.db.writer())
            .await?;
        let pa_room = bus
            .lookup_room(room)
            .await
            .unwrap_or_else(|_| RoomInfo::unknown(room));
        let pinned_by = bus
            .lookup_user(user_id)
            .await
            .unwrap_or_else(|_| UserInfo::unknown(user_id));
        let data = pin::PinAddedService {
            room: pa_room,
            message,
            pinned_by,
            pinned_at: Utc::now(),
        };
        bus.publish_room_event(room, "pin.added", &data).await?;
        Ok(Some(WsOutEvent::PinAdded {
            room: data.room.clone(),
            data,
        }))
    }

    pub(super) async fn pin_remove(
        bus: &ChannelBus,
        user_id: Uuid,
        room: Uuid,
        message: Uuid,
    ) -> ChannelResult<Option<WsOutEvent>> {
        Self::ensure_room_access(bus, user_id, room).await?;
        Self::ensure_message_in_room(bus, room, message).await?;
        let result = db::sqlx::query(
            "DELETE FROM room_pin WHERE room = $1 AND message = $2",
        )
        .bind(room)
        .bind(message)
        .execute(bus.inner.db.writer())
        .await?;
        if result.rows_affected() == 0 {
            return Ok(None);
        }
        db::sqlx::query("UPDATE room_message SET pinned = false, updated_at = now() WHERE id = $1")
            .bind(message)
            .execute(bus.inner.db.writer())
            .await?;
        let pr_room = bus
            .lookup_room(room)
            .await
            .unwrap_or_else(|_| RoomInfo::unknown(room));
        let removed_by = bus
            .lookup_user(user_id)
            .await
            .unwrap_or_else(|_| UserInfo::unknown(user_id));
        let data = pin::PinRemovedService {
            room: pr_room,
            message,
            removed_by,
            removed_at: Utc::now(),
        };
        bus.publish_room_event(room, "pin.removed", &data).await?;
        Ok(Some(WsOutEvent::PinRemoved {
            room: data.room.clone(),
            data,
        }))
    }
}
