use actix_web::{HttpRequest, HttpResponse, web};
use channel::ChannelBus;
use channel::http::{WsHandler, WsInMessage};
use serde::Deserialize;
use uuid::Uuid;

use super::rest::{channel_err, created_json, extract_user, ok_json};
use crate::error::ApiError;

#[derive(Debug, Deserialize, utoipa::ToSchema)]
pub struct RoomCreateRequest {
    pub workspace: Uuid,
    pub room_name: String,
    pub public: bool,
    pub category: Option<Uuid>,
    pub ai_enabled: Option<bool>,
    pub channel_type: Option<String>,
}

#[derive(Debug, Deserialize, utoipa::ToSchema)]
pub struct RoomUpdateRequest {
    pub room_name: Option<String>,
    pub public: Option<bool>,
    pub category: Option<Uuid>,
    pub ai_enabled: Option<bool>,
}

#[derive(Debug, Deserialize, utoipa::ToSchema)]
pub struct AccessRequest {
    pub user: Uuid,
}

#[derive(Debug, Deserialize, utoipa::ToSchema)]
pub struct CategoryCreateRequest {
    pub name: String,
    pub position: Option<i32>,
}

#[derive(Debug, Deserialize, utoipa::ToSchema)]
pub struct CategoryUpdateRequest {
    pub name: Option<String>,
    pub position: Option<i32>,
}
#[utoipa::path(
    get,
    path = "/api/v1/ws/rooms",
    responses((status = 200, description = "List of rooms")),
    tag = "channel",
)]
pub async fn list_rooms(
    req: HttpRequest,
    bus: web::Data<ChannelBus>,
) -> Result<HttpResponse, ApiError> {
    let user_id = extract_user(&req)?;
    let rooms = bus.list_user_rooms(user_id).await.map_err(channel_err)?;
    let categories = bus
        .list_user_categories(user_id)
        .await
        .map_err(channel_err)?;
    let workspace_id = if let Some(r) = rooms.first() {
        Some(r.workspace_id)
    } else {
        bus.first_workspace_id(user_id).await.unwrap_or(None)
    };
    Ok(HttpResponse::Ok().json(serde_json::json!({
        "rooms": rooms,
        "categories": categories,
        "workspace_id": workspace_id,
    })))
}

#[utoipa::path(
    post,
    path = "/api/v1/ws/rooms/{room_id}/subscribe",
    responses((status = 204, description = "Subscribed, user room cache refreshed")),
    tag = "channel",
)]
pub async fn subscribe(
    req: HttpRequest,
    room_id: web::Path<Uuid>,
    bus: web::Data<ChannelBus>,
) -> Result<HttpResponse, ApiError> {
    let user_id = extract_user(&req)?;
    let msg = WsInMessage::Subscribe {
        room: room_id.into_inner(),
    };
    let result = WsHandler::handle(&bus, user_id, msg)
        .await
        .map_err(channel_err)?;
    Ok(ok_json(result))
}

#[utoipa::path(
    delete,
    path = "/api/v1/ws/rooms/{room_id}/subscribe",
    responses((status = 204, description = "Unsubscribed")),
    tag = "channel",
)]
pub async fn unsubscribe(
    req: HttpRequest,
    room_id: web::Path<Uuid>,
    bus: web::Data<ChannelBus>,
) -> Result<HttpResponse, ApiError> {
    let user_id = extract_user(&req)?;
    let msg = WsInMessage::Unsubscribe {
        room: room_id.into_inner(),
    };
    let result = WsHandler::handle(&bus, user_id, msg)
        .await
        .map_err(channel_err)?;
    Ok(ok_json(result))
}

#[utoipa::path(
    get,
    path = "/api/v1/ws/rooms/{room_id}",
    responses((status = 200, description = "Room info")),
    tag = "channel",
)]
pub async fn room_get(
    req: HttpRequest,
    room_id: web::Path<Uuid>,
    bus: web::Data<ChannelBus>,
) -> Result<HttpResponse, ApiError> {
    let user_id = extract_user(&req)?;
    let msg = WsInMessage::RoomGet {
        room: room_id.into_inner(),
    };
    let result = WsHandler::handle(&bus, user_id, msg)
        .await
        .map_err(channel_err)?;
    Ok(ok_json(result))
}

#[utoipa::path(
    post,
    path = "/api/v1/ws/rooms",
    request_body = RoomCreateRequest,
    responses((status = 201, description = "Room created")),
    tag = "channel",
)]
pub async fn room_create(
    req: HttpRequest,
    body: web::Json<RoomCreateRequest>,
    bus: web::Data<ChannelBus>,
) -> Result<HttpResponse, ApiError> {
    let user_id = extract_user(&req)?;
    let msg = WsInMessage::RoomCreate {
        workspace: body.workspace,
        room_name: body.room_name.clone(),
        public: body.public,
        category: body.category,
        ai_enabled: body.ai_enabled,
        channel_type: body.channel_type.clone(),
    };
    let result = WsHandler::handle(&bus, user_id, msg)
        .await
        .map_err(channel_err)?;
    Ok(created_json(result))
}

#[utoipa::path(
    patch,
    path = "/api/v1/ws/rooms/{room_id}",
    request_body = RoomUpdateRequest,
    responses((status = 200, description = "Room updated")),
    tag = "channel",
)]
pub async fn room_update(
    req: HttpRequest,
    room_id: web::Path<Uuid>,
    body: web::Json<RoomUpdateRequest>,
    bus: web::Data<ChannelBus>,
) -> Result<HttpResponse, ApiError> {
    let user_id = extract_user(&req)?;
    let msg = WsInMessage::RoomUpdate {
        room: room_id.into_inner(),
        room_name: body.room_name.clone(),
        public: body.public,
        category: body.category,
        ai_enabled: body.ai_enabled,
    };
    let result = WsHandler::handle(&bus, user_id, msg)
        .await
        .map_err(channel_err)?;
    Ok(ok_json(result))
}

#[utoipa::path(
    delete,
    path = "/api/v1/ws/rooms/{room_id}",
    responses((status = 204, description = "Room deleted")),
    tag = "channel",
)]
pub async fn room_delete(
    req: HttpRequest,
    room_id: web::Path<Uuid>,
    bus: web::Data<ChannelBus>,
) -> Result<HttpResponse, ApiError> {
    let user_id = extract_user(&req)?;
    let msg = WsInMessage::RoomDelete {
        room: room_id.into_inner(),
    };
    let result = WsHandler::handle(&bus, user_id, msg)
        .await
        .map_err(channel_err)?;
    Ok(ok_json(result))
}

#[utoipa::path(
    post,
    path = "/api/v1/ws/rooms/{room_id}/members",
    request_body = AccessRequest,
    responses((status = 204, description = "Access granted")),
    tag = "channel",
)]
pub async fn access_grant(
    req: HttpRequest,
    room_id: web::Path<Uuid>,
    body: web::Json<AccessRequest>,
    bus: web::Data<ChannelBus>,
) -> Result<HttpResponse, ApiError> {
    let user_id = extract_user(&req)?;
    let msg = WsInMessage::AccessGrant {
        room: room_id.into_inner(),
        user: body.user,
    };
    let result = WsHandler::handle(&bus, user_id, msg)
        .await
        .map_err(channel_err)?;
    Ok(ok_json(result))
}

#[utoipa::path(
    delete,
    path = "/api/v1/ws/rooms/{room_id}/members/{user_id}",
    responses((status = 204, description = "Access revoked")),
    tag = "channel",
)]
pub async fn access_revoke(
    req: HttpRequest,
    path: web::Path<(Uuid, Uuid)>,
    bus: web::Data<ChannelBus>,
) -> Result<HttpResponse, ApiError> {
    let user_id = extract_user(&req)?;
    let (room, target_user) = path.into_inner();
    let msg = WsInMessage::AccessRevoke {
        room,
        user: target_user,
    };
    let result = WsHandler::handle(&bus, user_id, msg)
        .await
        .map_err(channel_err)?;
    Ok(ok_json(result))
}

#[utoipa::path(
    post,
    path = "/api/v1/ws/workspaces/{workspace_id}/categories",
    request_body = CategoryCreateRequest,
    responses((status = 201, description = "Category created")),
    tag = "channel",
)]
pub async fn category_create(
    req: HttpRequest,
    workspace_id: web::Path<Uuid>,
    body: web::Json<CategoryCreateRequest>,
    bus: web::Data<ChannelBus>,
) -> Result<HttpResponse, ApiError> {
    let user_id = extract_user(&req)?;
    let msg = WsInMessage::CategoryCreate {
        workspace: workspace_id.into_inner(),
        name: body.name.clone(),
        position: body.position,
    };
    let result = WsHandler::handle(&bus, user_id, msg)
        .await
        .map_err(channel_err)?;
    Ok(created_json(result))
}

#[utoipa::path(
    patch,
    path = "/api/v1/ws/categories/{category_id}",
    request_body = CategoryUpdateRequest,
    responses((status = 200, description = "Category updated")),
    tag = "channel",
)]
pub async fn category_update(
    req: HttpRequest,
    category_id: web::Path<Uuid>,
    body: web::Json<CategoryUpdateRequest>,
    bus: web::Data<ChannelBus>,
) -> Result<HttpResponse, ApiError> {
    let user_id = extract_user(&req)?;
    let msg = WsInMessage::CategoryUpdate {
        id: category_id.into_inner(),
        name: body.name.clone(),
        position: body.position,
    };
    let result = WsHandler::handle(&bus, user_id, msg)
        .await
        .map_err(channel_err)?;
    Ok(ok_json(result))
}

#[utoipa::path(
    delete,
    path = "/api/v1/ws/categories/{category_id}",
    responses((status = 204, description = "Category deleted")),
    tag = "channel",
)]
pub async fn category_delete(
    req: HttpRequest,
    category_id: web::Path<Uuid>,
    bus: web::Data<ChannelBus>,
) -> Result<HttpResponse, ApiError> {
    let user_id = extract_user(&req)?;
    let msg = WsInMessage::CategoryDelete {
        id: category_id.into_inner(),
    };
    let result = WsHandler::handle(&bus, user_id, msg)
        .await
        .map_err(channel_err)?;
    Ok(ok_json(result))
}
