use actix_web::{HttpRequest, HttpResponse, web};
use channel::ChannelBus;
use channel::http::{WsHandler, WsInMessage};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
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 ReadReceiptRequest {
    pub last_read_seq: i64,
}

#[derive(Debug, Deserialize, utoipa::ToSchema)]
pub struct DndRequest {
    pub do_not_disturb: Option<bool>,
    pub dnd_start_hour: Option<i16>,
    pub dnd_end_hour: Option<i16>,
}

#[derive(Debug, Deserialize, utoipa::ToSchema)]
pub struct NotificationMarkAllReadRequest {
    pub workspace_id: Option<Uuid>,
}

#[derive(Debug, Deserialize, utoipa::ToSchema)]
pub struct PresenceUpdateRequest {
    #[schema(example = "online")]
    pub status: String,
}

#[derive(Debug, Deserialize, utoipa::ToSchema)]
pub struct CustomStatusRequest {
    pub emoji: Option<String>,
    pub text: Option<String>,
    pub expires_at: Option<DateTime<Utc>>,
}

#[derive(Debug, Deserialize, utoipa::ToSchema)]
pub struct InviteCreateRequest {
    pub workspace: Uuid,
    pub room: Option<Uuid>,
    pub max_uses: Option<i32>,
    pub expires_at: Option<DateTime<Utc>>,
}

#[derive(Debug, Deserialize, utoipa::ToSchema)]
pub struct InviteAcceptRequest {
    pub code: String,
}

#[derive(Debug, Deserialize, utoipa::ToSchema)]
pub struct BanCreateRequest {
    pub user: Uuid,
    pub reason: Option<String>,
    pub expires_at: Option<DateTime<Utc>>,
}

#[utoipa::path(
    post,
    path = "/api/v1/ws/rooms/{room_id}/read-receipt",
    request_body = ReadReceiptRequest,
    responses((status = 200, description = "Read receipt saved")),
    tag = "channel",
)]
pub async fn read_receipt(
    req: HttpRequest,
    room_id: web::Path<Uuid>,
    body: web::Json<ReadReceiptRequest>,
    bus: web::Data<ChannelBus>,
) -> Result<HttpResponse, ApiError> {
    let user_id = extract_user(&req)?;
    let msg = WsInMessage::ReadReceipt {
        room: room_id.into_inner(),
        last_read_seq: body.last_read_seq,
    };
    let result = WsHandler::handle(&bus, user_id, msg)
        .await
        .map_err(channel_err)?;
    Ok(ok_json(result))
}

#[utoipa::path(
    patch,
    path = "/api/v1/ws/rooms/{room_id}/dnd",
    request_body = DndRequest,
    responses((status = 204, description = "DND updated")),
    tag = "channel",
)]
pub async fn dnd_update(
    req: HttpRequest,
    room_id: web::Path<Uuid>,
    body: web::Json<DndRequest>,
    bus: web::Data<ChannelBus>,
) -> Result<HttpResponse, ApiError> {
    let user_id = extract_user(&req)?;
    let msg = WsInMessage::StateUpdateDnd {
        room: room_id.into_inner(),
        do_not_disturb: body.do_not_disturb,
        dnd_start_hour: body.dnd_start_hour,
        dnd_end_hour: body.dnd_end_hour,
    };
    let result = WsHandler::handle(&bus, user_id, msg)
        .await
        .map_err(channel_err)?;
    Ok(ok_json(result))
}

#[utoipa::path(
    patch,
    path = "/api/v1/ws/notifications/{id}/read",
    responses((status = 204, description = "Notification marked read")),
    tag = "channel",
)]
pub async fn notification_mark_read(
    req: HttpRequest,
    id: web::Path<Uuid>,
    bus: web::Data<ChannelBus>,
) -> Result<HttpResponse, ApiError> {
    let user_id = extract_user(&req)?;
    let msg = WsInMessage::NotificationMarkRead {
        id: 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/notifications/read-all",
    request_body = NotificationMarkAllReadRequest,
    responses((status = 204, description = "All notifications marked read")),
    tag = "channel",
)]
pub async fn notification_mark_all_read(
    req: HttpRequest,
    body: web::Json<NotificationMarkAllReadRequest>,
    bus: web::Data<ChannelBus>,
) -> Result<HttpResponse, ApiError> {
    let user_id = extract_user(&req)?;
    let msg = WsInMessage::NotificationMarkAllRead {
        workspace_id: body.workspace_id,
    };
    let result = WsHandler::handle(&bus, user_id, msg)
        .await
        .map_err(channel_err)?;
    Ok(ok_json(result))
}

#[utoipa::path(
    delete,
    path = "/api/v1/ws/notifications/{id}",
    responses((status = 204, description = "Notification archived")),
    tag = "channel",
)]
pub async fn notification_archive(
    req: HttpRequest,
    id: web::Path<Uuid>,
    bus: web::Data<ChannelBus>,
) -> Result<HttpResponse, ApiError> {
    let user_id = extract_user(&req)?;
    let msg = WsInMessage::NotificationArchive {
        id: 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/presence",
    request_body = PresenceUpdateRequest,
    responses((status = 204, description = "Presence updated")),
    tag = "channel",
)]
pub async fn presence_update(
    req: HttpRequest,
    body: web::Json<PresenceUpdateRequest>,
    bus: web::Data<ChannelBus>,
) -> Result<HttpResponse, ApiError> {
    let user_id = extract_user(&req)?;
    let status: channel::event::presence::UserPresenceStatus =
        serde_json::from_value(serde_json::Value::String(body.status.clone()))
            .map_err(|e| {
                ApiError(service::error::AppError::BadRequest(e.to_string()))
            })?;
    let msg = WsInMessage::PresenceUpdate { status };
    let result = WsHandler::handle(&bus, user_id, msg)
        .await
        .map_err(channel_err)?;
    Ok(ok_json(result))
}

#[utoipa::path(
    post,
    path = "/api/v1/ws/custom-status",
    request_body = CustomStatusRequest,
    responses((status = 204, description = "Custom status updated")),
    tag = "channel",
)]
pub async fn custom_status_update(
    req: HttpRequest,
    body: web::Json<CustomStatusRequest>,
    bus: web::Data<ChannelBus>,
) -> Result<HttpResponse, ApiError> {
    let user_id = extract_user(&req)?;
    let msg = WsInMessage::CustomStatusUpdate {
        emoji: body.emoji.clone(),
        text: body.text.clone(),
        expires_at: body.expires_at,
    };
    let result = WsHandler::handle(&bus, user_id, msg)
        .await
        .map_err(channel_err)?;
    Ok(ok_json(result))
}

#[utoipa::path(
    post,
    path = "/api/v1/ws/invites",
    request_body = InviteCreateRequest,
    responses((status = 201, description = "Invite created")),
    tag = "channel",
)]
pub async fn invite_create(
    req: HttpRequest,
    body: web::Json<InviteCreateRequest>,
    bus: web::Data<ChannelBus>,
) -> Result<HttpResponse, ApiError> {
    let user_id = extract_user(&req)?;
    let msg = WsInMessage::InviteCreate {
        workspace: body.workspace,
        room: body.room,
        max_uses: body.max_uses,
        expires_at: body.expires_at,
    };
    let result = WsHandler::handle(&bus, user_id, msg)
        .await
        .map_err(channel_err)?;
    Ok(created_json(result))
}

#[utoipa::path(
    post,
    path = "/api/v1/ws/invites/accept",
    request_body = InviteAcceptRequest,
    responses((status = 200, description = "Invite accepted")),
    tag = "channel",
)]
pub async fn invite_accept(
    req: HttpRequest,
    body: web::Json<InviteAcceptRequest>,
    bus: web::Data<ChannelBus>,
) -> Result<HttpResponse, ApiError> {
    let user_id = extract_user(&req)?;
    let msg = WsInMessage::InviteAccept {
        code: body.code.clone(),
    };
    let result = WsHandler::handle(&bus, user_id, msg)
        .await
        .map_err(channel_err)?;
    Ok(ok_json(result))
}

#[utoipa::path(
    delete,
    path = "/api/v1/ws/invites/{id}",
    responses((status = 204, description = "Invite revoked")),
    tag = "channel",
)]
pub async fn invite_revoke(
    req: HttpRequest,
    id: web::Path<Uuid>,
    bus: web::Data<ChannelBus>,
) -> Result<HttpResponse, ApiError> {
    let user_id = extract_user(&req)?;
    let msg = WsInMessage::InviteRevoke {
        id: 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/workspaces/{workspace_id}/bans",
    request_body = BanCreateRequest,
    responses((status = 201, description = "User banned")),
    tag = "channel",
)]
pub async fn ban_create(
    req: HttpRequest,
    workspace_id: web::Path<Uuid>,
    body: web::Json<BanCreateRequest>,
    bus: web::Data<ChannelBus>,
) -> Result<HttpResponse, ApiError> {
    let user_id = extract_user(&req)?;
    let msg = WsInMessage::BanCreate {
        workspace: workspace_id.into_inner(),
        user: body.user,
        reason: body.reason.clone(),
        expires_at: body.expires_at,
    };
    let result = WsHandler::handle(&bus, user_id, msg)
        .await
        .map_err(channel_err)?;
    Ok(ok_json(result))
}

#[utoipa::path(
    delete,
    path = "/api/v1/ws/workspaces/{workspace_id}/bans/{user_id}",
    responses((status = 204, description = "User unbanned")),
    tag = "channel",
)]
pub async fn ban_remove(
    req: HttpRequest,
    path: web::Path<(Uuid, Uuid)>,
    bus: web::Data<ChannelBus>,
) -> Result<HttpResponse, ApiError> {
    let user_id = extract_user(&req)?;
    let (workspace, target_user) = path.into_inner();
    let msg = WsInMessage::BanRemove {
        workspace,
        user: target_user,
    };
    let result = WsHandler::handle(&bus, user_id, msg)
        .await
        .map_err(channel_err)?;
    Ok(ok_json(result))
}

#[derive(Debug, Serialize, utoipa::ToSchema)]
pub struct RoomMember {
    pub id: Uuid,
    pub username: String,
    pub display_name: String,
    pub avatar_url: String,
}

#[utoipa::path(
    get,
    path = "/api/v1/ws/workspaces/{workspace_id}/members",
    responses((status = 200, description = "Workspace members list")),
    tag = "channel",
)]
pub async fn list_workspace_members(
    req: HttpRequest,
    workspace_id: web::Path<Uuid>,
    bus: web::Data<ChannelBus>,
) -> Result<HttpResponse, ApiError> {
    let _user_id = extract_user(&req)?;
    let workspace = workspace_id.into_inner();

    let members = bus
        .list_workspace_members(workspace)
        .await
        .map_err(channel_err)?;
    let result: Vec<RoomMember> = members
        .into_iter()
        .map(|(id, username, display_name, avatar_url)| RoomMember {
            id,
            username,
            display_name,
            avatar_url,
        })
        .collect();

    Ok(HttpResponse::Ok().json(result))
}
