use uuid::Uuid;

use crate::event::{RoomInfo, UserInfo};
use crate::{
    ChannelBus, ChannelError, ChannelResult,
    search::{SearchEngine, SearchQuery},
};

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

impl WsHandler {
    pub(super) async fn search(
        bus: &ChannelBus,
        user_id: Uuid,
        q: String,
        room: Option<Uuid>,
        limit: Option<u64>,
        offset: Option<u64>,
    ) -> ChannelResult<Option<WsOutEvent>> {
        if let Some(room_id) = room {
            Self::ensure_room_access(bus, user_id, room_id).await?;
        } else {
            return Err(ChannelError::Validation(
                "room is required for websocket search".to_string(),
            ));
        }
        let engine = SearchEngine::new(bus.inner.db.clone());
        let result = engine
            .search(SearchQuery {
                query: q.clone(),
                room_id: room,
                user_id: None,
                limit: limit.unwrap_or(50),
                offset: offset.unwrap_or(0),
            })
            .await?;

        let author_ids: Vec<Uuid> =
            result.hits.iter().map(|h| h.sender_id).collect();
        let message_ids: Vec<Uuid> =
            result.hits.iter().map(|h| h.message_id).collect();
        let user_map = bus.lookup_users(&author_ids).await.unwrap_or_default();
        let reactions =
            Self::reaction_groups_for_messages(bus, user_id, &message_ids)
                .await
                .unwrap_or_default();

        let search_room = match room {
            Some(r) => Some(
                bus.lookup_room(r)
                    .await
                    .unwrap_or_else(|_| RoomInfo::unknown(r)),
            ),
            None => None,
        };
        let search_msg_room = search_room
            .clone()
            .unwrap_or_else(|| RoomInfo::unknown(room.unwrap_or_default()));
        let data = crate::event::search::SearchResultService {
            q,
            room: search_room,
            messages: result
                .hits
                .into_iter()
                .map(|h| {
                    let sender = user_map
                        .get(&h.sender_id)
                        .cloned()
                        .unwrap_or_else(|| UserInfo::unknown(h.sender_id));
                    crate::event::search::SearchMessageHitService {
                        message: crate::event::message::MessageNewService {
                            id: h.message_id,
                            seq: 0,
                            room: search_msg_room.clone(),
                            sender_type: "user".to_string(),
                            sender,
                            thread: None,
                            in_reply_to: None,
                            content: h.content.clone(),
                            content_type: "text".to_string(),
                            pinned: false,
                            system_type: None,
                            metadata: serde_json::Value::Null,
                            thinking_content: None,
                            thinking_is_chunked: None,
                            send_at: h.send_at,
                            reactions: reactions
                                .get(&h.message_id)
                                .cloned()
                                .unwrap_or_default(),
                        },
                        highlighted_content: h.highlighted,
                    }
                })
                .collect(),
            total: result.total as i64,
            took_ms: 0,
        };

        Ok(Some(WsOutEvent::SearchResult { data }))
    }
}
