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

use crate::event::{UserInfo, WorkspaceInfo, ban};
use crate::{ChannelBus, ChannelResult};

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

impl WsHandler {
    pub(super) async fn ban_create(
        bus: &ChannelBus,
        _user_id: Uuid,
        workspace: Uuid,
        user: Uuid,
        reason: Option<String>,
        _expires_at: Option<chrono::DateTime<chrono::Utc>>,
    ) -> ChannelResult<Option<WsOutEvent>> {
        Self::ensure_workspace_member(bus, _user_id, workspace).await?;
        db::sqlx::query(
            "INSERT INTO user_blacklist (\"user\", black, created_at) \
             VALUES ($1, $2, now()) \
             ON CONFLICT DO NOTHING",
        )
        .bind(user)
        .bind(_user_id)
        .execute(bus.inner.db.writer())
        .await?;
        let ban_key = format!("ban:{}:{}:{}", workspace, _user_id, user);
        let ban_data = serde_json::json!({
            "workspace": workspace,
            "banned_by": _user_id,
            "reason": reason,
            "expires_at": _expires_at,
            "banned_at": Utc::now(),
        });
        bus.inner.cache.set(&ban_key, &ban_data).await?;
        let data = ban::BannedService {
            workspace: bus
                .lookup_workspace(workspace)
                .await
                .unwrap_or_else(|_| WorkspaceInfo::unknown(workspace)),
            user: bus
                .lookup_user(user)
                .await
                .unwrap_or_else(|_| UserInfo::unknown(user)),
            banned_by: bus
                .lookup_user(_user_id)
                .await
                .unwrap_or_else(|_| UserInfo::unknown(_user_id)),
            reason,
            expires_at: _expires_at,
            banned_at: Utc::now(),
        };
        bus.workspace_changed(workspace).await?;
        Ok(Some(WsOutEvent::UserBanned { data }))
    }

    pub(super) async fn ban_remove(
        bus: &ChannelBus,
        _user_id: Uuid,
        workspace: Uuid,
        user: Uuid,
    ) -> ChannelResult<Option<WsOutEvent>> {
        Self::ensure_workspace_member(bus, _user_id, workspace).await?;
        db::sqlx::query(
            "DELETE FROM user_blacklist WHERE \"user\" = $1 AND black = $2",
        )
        .bind(user)
        .bind(_user_id)
        .execute(bus.inner.db.writer())
        .await?;
        let ban_key = format!("ban:{}:{}:{}", workspace, _user_id, user);
        bus.inner.cache.remove(&ban_key).await?;
        let data = ban::UnbannedService {
            workspace: bus
                .lookup_workspace(workspace)
                .await
                .unwrap_or_else(|_| WorkspaceInfo::unknown(workspace)),
            user: bus
                .lookup_user(user)
                .await
                .unwrap_or_else(|_| UserInfo::unknown(user)),
            unbanned_by: bus
                .lookup_user(_user_id)
                .await
                .unwrap_or_else(|_| UserInfo::unknown(_user_id)),
            unbanned_at: Utc::now(),
        };
        bus.workspace_changed(workspace).await?;
        Ok(Some(WsOutEvent::UserUnbanned { data }))
    }
}
