use crate::error::ApiError;
use actix_web::Result;
use room::ws_context::WsUserContext;
use service::AppService;
use std::sync::Arc;
use uuid::Uuid;

pub struct WsRequestHandler {
    service: Arc<AppService>,
    user_id: Uuid,
}

impl WsRequestHandler {
    pub fn new(service: Arc<AppService>, user_id: Uuid) -> Self {
        Self { service, user_id }
    }

    pub fn service(&self) -> &Arc<AppService> {
        &self.service
    }

    pub async fn handle(&self, request: WsRequest) -> WsResponse {
        let request_id = request.request_id;
        let action_str = request.action.to_string();
        match self.handle_action(request).await {
            Ok(data) => WsResponse::success(request_id, &action_str, data),
            Err(err) => WsResponse::from_api_error(request_id, &action_str, err),
        }
    }

    async fn handle_action(&self, request: WsRequest) -> Result<WsResponseData, ApiError> {
        let params = request.params();
        let ctx = WsUserContext::new(self.user_id);
        match request.action {
            WsAction::RoomList => {
                let project_name = params.project_name.clone().ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "project_name required".into(),
                    ))
                })?;
                let rooms = self
                    .service
                    .room
                    .room_list(project_name, params.only_public, &ctx)
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::room_list(rooms))
            }
            WsAction::RoomGet => {
                let room_id = params.room_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "room_id required".into(),
                    ))
                })?;
                let room = self
                    .service
                    .room
                    .room_get(room_id, &ctx)
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::room(room))
            }
            WsAction::RoomCreate => {
                let project_name = params.project_name.clone().ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "project_name required".into(),
                    ))
                })?;
                let room = self
                    .service
                    .room
                    .room_create(
                        project_name,
                        room::RoomCreateRequest {
                            room_name: params.room_name.clone().unwrap_or_default(),
                            public: params.room_public.unwrap_or(false),
                            category: params.room_category,
                        },
                        &ctx,
                    )
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::room(room))
            }
            WsAction::RoomUpdate => {
                let room_id = params.room_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "room_id required".into(),
                    ))
                })?;
                let room = self
                    .service
                    .room
                    .room_update(
                        room_id,
                        room::RoomUpdateRequest {
                            room_name: params.room_name.clone(),
                            public: params.room_public,
                            category: params.room_category,
                        },
                        &ctx,
                    )
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::room(room))
            }
            WsAction::RoomDelete => {
                let room_id = params.room_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "room_id required".into(),
                    ))
                })?;
                self.service
                    .room
                    .room_delete(room_id, &ctx)
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::bool(true))
            }
            WsAction::CategoryList => {
                let project_name = params.project_name.clone().ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "project_name required".into(),
                    ))
                })?;
                let categories = self
                    .service
                    .room
                    .room_category_list(project_name, &ctx)
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::category_list(categories))
            }
            WsAction::CategoryCreate => {
                let project_name = params.project_name.clone().ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "project_name required".into(),
                    ))
                })?;
                let category = self
                    .service
                    .room
                    .room_category_create(
                        project_name,
                        room::RoomCategoryCreateRequest {
                            name: params.name.clone().unwrap_or_default(),
                            position: params.position,
                        },
                        &ctx,
                    )
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::category(category))
            }
            WsAction::CategoryUpdate => {
                let category_id = params.category_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "category_id required".into(),
                    ))
                })?;
                let category = self
                    .service
                    .room
                    .room_category_update(
                        category_id,
                        room::RoomCategoryUpdateRequest {
                            name: params.name.clone(),
                            position: params.position,
                        },
                        &ctx,
                    )
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::category(category))
            }
            WsAction::CategoryDelete => {
                let category_id = params.category_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "category_id required".into(),
                    ))
                })?;
                self.service
                    .room
                    .room_category_delete(category_id, &ctx)
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::bool(true))
            }
            WsAction::MessageList => {
                let room_id = params.room_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "room_id required".into(),
                    ))
                })?;
                let messages = self
                    .service
                    .room
                    .room_message_list(
                        room_id,
                        params.before_seq,
                        params.after_seq,
                        params.limit,
                        &ctx,
                    )
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::message_list(messages))
            }
            WsAction::MessageCreate => {
                let room_id = params.room_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "room_id required".into(),
                    ))
                })?;
                let message = self
                    .service
                    .room
                    .room_message_create(
                        room_id,
                        room::RoomMessageCreateRequest {
                            content: params.content.clone().unwrap_or_default(),
                            content_type: params.content_type.clone(),
                            thread: params.thread_id,
                            in_reply_to: params.in_reply_to,
                            attachment_ids: params.attachment_ids.clone().unwrap_or_default(),
                        },
                        &ctx,
                    )
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::message(message))
            }
            WsAction::MessageUpdate => {
                let message_id = params.message_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "message_id required".into(),
                    ))
                })?;
                let message = self
                    .service
                    .room
                    .room_message_update(
                        message_id,
                        room::RoomMessageUpdateRequest {
                            content: params.content.clone().unwrap_or_default(),
                        },
                        &ctx,
                    )
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::message(message))
            }
            WsAction::MessageRevoke => {
                let message_id = params.message_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "message_id required".into(),
                    ))
                })?;
                let message = self
                    .service
                    .room
                    .room_message_revoke(message_id, &ctx)
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::message(message))
            }
            WsAction::MessageGet => {
                let message_id = params.message_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "message_id required".into(),
                    ))
                })?;
                let message = self
                    .service
                    .room
                    .room_message_get(message_id, &ctx)
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::message(message))
            }
            WsAction::MemberList => {
                let room_id = params.room_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "room_id required".into(),
                    ))
                })?;
                let members = self
                    .service
                    .room
                    .room_member_list(room_id, &ctx)
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::member_list(members))
            }
            WsAction::MemberAdd => {
                let room_id = params.room_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "room_id required".into(),
                    ))
                })?;
                let user_id = params.user_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "user_id required".into(),
                    ))
                })?;
                let member = self
                    .service
                    .room
                    .room_member_add(
                        room_id,
                        room::RoomMemberAddRequest {
                            user_id,
                            role: params.role.clone(),
                        },
                        &ctx,
                    )
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::member(member))
            }
            WsAction::MemberRemove => {
                let room_id = params.room_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "room_id required".into(),
                    ))
                })?;
                let user_id = params.user_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "user_id required".into(),
                    ))
                })?;
                self.service
                    .room
                    .room_member_remove(room_id, user_id, &ctx)
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::bool(true))
            }
            WsAction::MemberLeave => {
                let room_id = params.room_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "room_id required".into(),
                    ))
                })?;
                self.service
                    .room
                    .room_member_leave(room_id, &ctx)
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::bool(true))
            }
            WsAction::MemberSetReadSeq => {
                let room_id = params.room_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "room_id required".into(),
                    ))
                })?;
                let last_read_seq = params.last_read_seq.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "last_read_seq required".into(),
                    ))
                })?;
                let member = self
                    .service
                    .room
                    .room_member_set_read_seq(
                        room_id,
                        room::RoomMemberReadSeqRequest { last_read_seq },
                        &ctx,
                    )
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::member(member))
            }
            WsAction::MemberUpdateRole => {
                let room_id = params.room_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "room_id required".into(),
                    ))
                })?;
                let user_id = params.user_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "user_id required".into(),
                    ))
                })?;
                let role = params.role.clone().ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest("role required".into()))
                })?;
                let member = self
                    .service
                    .room
                    .room_member_update_role(
                        room_id,
                        room::RoomMemberRoleUpdateRequest { user_id, role },
                        &ctx,
                    )
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::member(member))
            }
            WsAction::PinList => {
                let room_id = params.room_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "room_id required".into(),
                    ))
                })?;
                let pins = self
                    .service
                    .room
                    .room_pin_list(room_id, &ctx)
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::pin_list(pins))
            }
            WsAction::PinAdd => {
                let message_id = params.message_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "message_id required".into(),
                    ))
                })?;
                let pin = self
                    .service
                    .room
                    .room_pin_add(message_id, &ctx)
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::pin(pin))
            }
            WsAction::PinRemove => {
                let message_id = params.message_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "message_id required".into(),
                    ))
                })?;
                self.service
                    .room
                    .room_pin_remove(message_id, &ctx)
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::bool(true))
            }
            WsAction::ThreadList => {
                let room_id = params.room_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "room_id required".into(),
                    ))
                })?;
                let threads = self
                    .service
                    .room
                    .room_thread_list(room_id, &ctx)
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::thread_list(threads))
            }
            WsAction::ThreadCreate => {
                let room_id = params.room_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "room_id required".into(),
                    ))
                })?;
                let parent_seq = params.parent_seq.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "parent_seq required".into(),
                    ))
                })?;
                let thread = self
                    .service
                    .room
                    .room_thread_create(room_id, room::RoomThreadCreateRequest { parent_seq }, &ctx)
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::thread(thread))
            }
            WsAction::ThreadMessages => {
                let thread_id = params.thread_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "thread_id required".into(),
                    ))
                })?;
                let messages = self
                    .service
                    .room
                    .room_thread_messages(
                        thread_id,
                        params.before_seq,
                        params.after_seq,
                        params.limit,
                        &ctx,
                    )
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::message_list(messages))
            }
            WsAction::ReactionAdd => {
                let message_id = params.message_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "message_id required".into(),
                    ))
                })?;
                let emoji = params.emoji.clone().ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "emoji required".into(),
                    ))
                })?;
                let reactions = self
                    .service
                    .room
                    .message_reaction_add(message_id, emoji, &ctx)
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::reaction_list(reactions))
            }
            WsAction::ReactionRemove => {
                let message_id = params.message_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "message_id required".into(),
                    ))
                })?;
                let emoji = params.emoji.clone().ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "emoji required".into(),
                    ))
                })?;
                let reactions = self
                    .service
                    .room
                    .message_reaction_remove(message_id, emoji, &ctx)
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::reaction_list(reactions))
            }
            WsAction::ReactionGet => {
                let message_id = params.message_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "message_id required".into(),
                    ))
                })?;
                let reactions = self
                    .service
                    .room
                    .message_reactions_get(message_id, &ctx)
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::reaction_list(reactions))
            }
            WsAction::ReactionListBatch => {
                let room_id = params.room_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "room_id required".into(),
                    ))
                })?;
                let message_ids = params.message_ids.clone().ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "message_ids required".into(),
                    ))
                })?;
                let results = self
                    .service
                    .room
                    .message_reactions_batch(room_id, message_ids, &ctx)
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::reaction_list_batch(results))
            }
            WsAction::MessageSearch => {
                let room_id = params.room_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "room_id required".into(),
                    ))
                })?;
                let query = params.query.clone().ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "query required".into(),
                    ))
                })?;
                let result = self
                    .service
                    .room
                    .message_search(room_id, &query, params.limit, params.offset, &ctx)
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::search_result(result))
            }
            WsAction::MessageEditHistory => {
                let message_id = params.message_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "message_id required".into(),
                    ))
                })?;
                let history = self
                    .service
                    .room
                    .get_message_edit_history(message_id, &ctx)
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::edit_history(history))
            }
            WsAction::AiList => {
                let room_id = params.room_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "room_id required".into(),
                    ))
                })?;
                let configs = self
                    .service
                    .room
                    .room_ai_list(room_id, &ctx)
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::ai_list(configs))
            }
            WsAction::AiUpsert => {
                let room_id = params.room_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "room_id required".into(),
                    ))
                })?;
                let model = params.model.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "model required".into(),
                    ))
                })?;
                let config = self
                    .service
                    .room
                    .room_ai_upsert(
                        room_id,
                        room::RoomAiUpsertRequest {
                            model,
                            version: params.model_version,
                            history_limit: params.history_limit,
                            system_prompt: params.system_prompt.clone(),
                            temperature: params.temperature,
                            max_tokens: params.max_tokens,
                            use_exact: params.use_exact,
                            think: params.think,
                            stream: params.stream,
                            min_score: params.min_score,
                            agent_type: None,
                        },
                        &ctx,
                    )
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::ai_config(config))
            }
            WsAction::AiDelete => {
                let room_id = params.room_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "room_id required".into(),
                    ))
                })?;
                let model_id = params.model_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "model_id required".into(),
                    ))
                })?;
                self.service
                    .room
                    .room_ai_delete(room_id, model_id, &ctx)
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::bool(true))
            }
            WsAction::NotificationList => {
                let notifications = self
                    .service
                    .room
                    .notification_list(params.only_unread, params.archived, params.limit, &ctx)
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::notification_list(notifications))
            }
            WsAction::NotificationMarkRead => {
                let notification_id = params.notification_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "notification_id required".into(),
                    ))
                })?;
                self.service
                    .room
                    .notification_mark_read(notification_id, &ctx)
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::bool(true))
            }
            WsAction::NotificationMarkAllRead => {
                let count = self
                    .service
                    .room
                    .notification_mark_all_read(&ctx)
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::u64(count))
            }
            WsAction::NotificationArchive => {
                let notification_id = params.notification_id.ok_or_else(|| {
                    ApiError::from(service::error::AppError::BadRequest(
                        "notification_id required".into(),
                    ))
                })?;
                self.service
                    .room
                    .notification_archive(notification_id, &ctx)
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::bool(true))
            }
            WsAction::MentionList => {
                let mentions = self
                    .service
                    .room
                    .get_mention_notifications(params.limit, &ctx)
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::mention_list(mentions))
            }
            WsAction::MentionReadAll => {
                self.service
                    .room
                    .mark_mention_notifications_read(&ctx)
                    .await
                    .map_err(ApiError::from)?;
                Ok(WsResponseData::bool(true))
            }
            WsAction::SubscribeRoom => Ok(WsResponseData::subscribed(params.room_id, None)),
            WsAction::UnsubscribeRoom => Ok(WsResponseData::bool(true)),
            WsAction::SubscribeProject => Ok(WsResponseData::subscribed(None, None)),
            WsAction::UnsubscribeProject => Ok(WsResponseData::bool(true)),
            // TypingStart/TypingStop are handled directly in ws_universal.rs
            // (interception point sends response there, so this arm is never reached)
            WsAction::TypingStart | WsAction::TypingStop => Ok(WsResponseData::bool(true)),
        }
    }
}

use super::ws_types::{WsAction, WsRequest, WsResponse, WsResponseData};
