use crate::{ApiResponse, error::ApiError};
use actix_web::{HttpResponse, Result, web};
use room::ws_context::WsUserContext;
use service::AppService;
use session::Session;
use utoipa::IntoParams;
use uuid::Uuid;

#[derive(Debug, serde::Deserialize, IntoParams, utoipa::ToSchema)]
pub struct ReactionRequest {
    pub emoji: String,
}

#[derive(Debug, serde::Deserialize, IntoParams)]
pub struct MessageSearchQuery {
    pub q: String,
    pub limit: Option<u64>,
    pub offset: Option<u64>,
}

#[derive(Debug, serde::Deserialize, IntoParams)]
pub struct ReactionBatchQuery {
    /// Comma-separated list of message IDs
    pub message_ids: String,
}

#[utoipa::path(
    post,
    path = "/api/rooms/{room_id}/messages/{message_id}/reactions",
    params(
        ("room_id" = Uuid, Path),
        ("message_id" = Uuid, Path),
    ),
    request_body = ReactionRequest,
    responses(
        (status = 200, description = "Add reaction", body = ApiResponse<room::MessageReactionsResponse>),
        (status = 401, description = "Unauthorized"),
        (status = 403, description = "Forbidden"),
        (status = 404, description = "Not found"),
    ),
    tag = "Room"
)]
pub async fn reaction_add(
    service: web::Data<AppService>,
    session: Session,
    path: web::Path<(Uuid, Uuid)>,
    body: web::Json<ReactionRequest>,
) -> Result<HttpResponse, ApiError> {
    let (_room_id, message_id) = path.into_inner();
    let user_id = session
        .user()
        .ok_or_else(|| ApiError::from(service::error::AppError::Unauthorized))?;
    let ctx = WsUserContext::new(user_id);
    let resp = service
        .room
        .message_reaction_add(message_id, body.into_inner().emoji, &ctx)
        .await
        .map_err(ApiError::from)?;
    Ok(ApiResponse::ok(resp).to_response())
}

#[utoipa::path(
    delete,
    path = "/api/rooms/{room_id}/messages/{message_id}/reactions/{emoji}",
    params(
        ("room_id" = Uuid, Path),
        ("message_id" = Uuid, Path),
        ("emoji" = String, Path),
    ),
    responses(
        (status = 200, description = "Remove reaction", body = ApiResponse<room::MessageReactionsResponse>),
        (status = 401, description = "Unauthorized"),
        (status = 403, description = "Forbidden"),
        (status = 404, description = "Not found"),
    ),
    tag = "Room"
)]
pub async fn reaction_remove(
    service: web::Data<AppService>,
    session: Session,
    path: web::Path<(Uuid, Uuid, String)>,
) -> Result<HttpResponse, ApiError> {
    let (_room_id, message_id, emoji) = path.into_inner();
    let user_id = session
        .user()
        .ok_or_else(|| ApiError::from(service::error::AppError::Unauthorized))?;
    let ctx = WsUserContext::new(user_id);
    let resp = service
        .room
        .message_reaction_remove(message_id, emoji, &ctx)
        .await
        .map_err(ApiError::from)?;
    Ok(ApiResponse::ok(resp).to_response())
}

#[utoipa::path(
    get,
    path = "/api/rooms/{room_id}/messages/{message_id}/reactions",
    params(
        ("room_id" = Uuid, Path),
        ("message_id" = Uuid, Path),
    ),
    responses(
        (status = 200, description = "Get reactions", body = ApiResponse<room::MessageReactionsResponse>),
        (status = 401, description = "Unauthorized"),
        (status = 404, description = "Not found"),
    ),
    tag = "Room"
)]
pub async fn reaction_get(
    service: web::Data<AppService>,
    session: Session,
    path: web::Path<(Uuid, Uuid)>,
) -> Result<HttpResponse, ApiError> {
    let (_room_id, message_id) = path.into_inner();
    let user_id = session
        .user()
        .ok_or_else(|| ApiError::from(service::error::AppError::Unauthorized))?;
    let ctx = WsUserContext::new(user_id);
    let resp = service
        .room
        .message_reactions_get(message_id, &ctx)
        .await
        .map_err(ApiError::from)?;
    Ok(ApiResponse::ok(resp).to_response())
}

#[utoipa::path(
    get,
    path = "/api/rooms/{room_id}/messages/reactions/batch",
    params(
        ("room_id" = Uuid, Path),
        ("message_ids" = Vec<Uuid>, Query, description = "List of message IDs to fetch reactions for"),
    ),
    responses(
        (status = 200, description = "Batch get reactions", body = ApiResponse<Vec<room::MessageReactionsResponse>>),
        (status = 401, description = "Unauthorized"),
    ),
    tag = "Room"
)]
pub async fn reaction_batch(
    service: web::Data<AppService>,
    session: Session,
    path: web::Path<Uuid>,
    query: web::Query<ReactionBatchQuery>,
) -> Result<HttpResponse, ApiError> {
    let room_id = path.into_inner();
    let user_id = session
        .user()
        .ok_or_else(|| ApiError::from(service::error::AppError::Unauthorized))?;
    let ctx = WsUserContext::new(user_id);
    let message_ids: Vec<Uuid> = query
        .message_ids
        .split(',')
        .filter_map(|s| Uuid::parse_str(s.trim()).ok())
        .collect();
    let resp = service
        .room
        .message_reactions_batch(room_id, message_ids, &ctx)
        .await
        .map_err(ApiError::from)?;
    Ok(ApiResponse::ok(resp).to_response())
}

#[utoipa::path(
    get,
    path = "/api/rooms/{room_id}/messages/search",
    params(
        ("room_id" = Uuid, Path),
        ("q" = String, Query),
        ("limit" = Option<u64>, Query),
        ("offset" = Option<u64>, Query),
    ),
    responses(
        (status = 200, description = "Search messages", body = ApiResponse<room::MessageSearchResponse>),
        (status = 401, description = "Unauthorized"),
        (status = 404, description = "Not found"),
    ),
    tag = "Room"
)]
pub async fn message_search(
    service: web::Data<AppService>,
    session: Session,
    path: web::Path<Uuid>,
    query: web::Query<MessageSearchQuery>,
) -> Result<HttpResponse, ApiError> {
    let room_id = path.into_inner();
    let user_id = session
        .user()
        .ok_or_else(|| ApiError::from(service::error::AppError::Unauthorized))?;
    let ctx = WsUserContext::new(user_id);
    let req = room::types::RoomMessageSearchRequest {
        q: query.q.clone(),
        start_time: None,
        end_time: None,
        sender_id: None,
        content_type: None,
        limit: query.limit,
        offset: query.offset,
    };
    let resp = service
        .room
        .room_message_search(room_id, req, &ctx)
        .await
        .map_err(ApiError::from)?;
    Ok(ApiResponse::ok(resp).to_response())
}
