use crate::{ApiResponse, error::ApiError};
use actix_web::{HttpResponse, Result, web};
use service::AppService;
use service::workspace::members::{
    MyWorkspaceInvitation, PendingInvitationInfo, WorkspaceAcceptBySlugParams,
    WorkspaceInviteAcceptParams, WorkspaceInviteParams, WorkspaceMembersResponse,
};
use session::Session;
use uuid::Uuid;

#[derive(serde::Deserialize, utoipa::IntoParams)]
pub struct MembersQuery {
    pub page: Option<u64>,
    pub per_page: Option<u64>,
}

#[utoipa::path(
    get,
    path = "/api/workspaces/{slug}/members",
    params(
        ("slug" = String, Path),
        ("page" = Option<u64>, Query),
        ("per_page" = Option<u64>, Query),
    ),
    responses(
        (status = 200, description = "List workspace members", body = ApiResponse<WorkspaceMembersResponse>),
        (status = 401, description = "Unauthorized"),
        (status = 403, description = "Not a member"),
        (status = 404, description = "Workspace not found"),
    ),
    tag = "Workspace"
)]
pub async fn workspace_members(
    service: web::Data<AppService>,
    session: Session,
    path: web::Path<String>,
    query: web::Query<MembersQuery>,
) -> Result<HttpResponse, ApiError> {
    let slug = path.into_inner();
    let resp = service
        .workspace_members(&session, slug, query.page, query.per_page)
        .await?;
    Ok(ApiResponse::ok(resp).to_response())
}

#[derive(serde::Deserialize, utoipa::ToSchema)]
pub struct UpdateRoleParams {
    pub user_id: Uuid,
    pub role: String,
}

#[utoipa::path(
    patch,
    path = "/api/workspaces/{slug}/members/role",
    params(("slug" = String, Path)),
    request_body = UpdateRoleParams,
    responses(
        (status = 200, description = "Update member role"),
        (status = 401, description = "Unauthorized"),
        (status = 403, description = "Permission denied"),
        (status = 404, description = "Workspace or member not found"),
    ),
    tag = "Workspace"
)]
pub async fn workspace_update_member_role(
    service: web::Data<AppService>,
    session: Session,
    path: web::Path<String>,
    body: web::Json<UpdateRoleParams>,
) -> Result<HttpResponse, ApiError> {
    let slug = path.into_inner();
    service
        .workspace_update_member_role(&session, slug, body.user_id, body.role.clone())
        .await?;
    Ok(ApiResponse::ok(serde_json::json!({ "success": true })).to_response())
}

#[utoipa::path(
    delete,
    path = "/api/workspaces/{slug}/members/{user_id}",
    params(
        ("slug" = String, Path),
        ("user_id" = Uuid, Path),
    ),
    responses(
        (status = 200, description = "Remove member"),
        (status = 401, description = "Unauthorized"),
        (status = 403, description = "Permission denied"),
        (status = 404, description = "Member not found"),
    ),
    tag = "Workspace"
)]
pub async fn workspace_remove_member(
    service: web::Data<AppService>,
    session: Session,
    path: web::Path<(String, Uuid)>,
) -> Result<HttpResponse, ApiError> {
    let (slug, user_id) = path.into_inner();
    service
        .workspace_remove_member(&session, slug, user_id)
        .await?;
    Ok(ApiResponse::ok(serde_json::json!({ "success": true })).to_response())
}

#[utoipa::path(
    get,
    path = "/api/workspaces/{slug}/invitations",
    params(("slug" = String, Path)),
    responses(
        (status = 200, description = "List pending invitations", body = ApiResponse<Vec<PendingInvitationInfo>>),
        (status = 401, description = "Unauthorized"),
        (status = 403, description = "Permission denied"),
        (status = 404, description = "Workspace not found"),
    ),
    tag = "Workspace"
)]
pub async fn workspace_pending_invitations(
    service: web::Data<AppService>,
    session: Session,
    path: web::Path<String>,
) -> Result<HttpResponse, ApiError> {
    let slug = path.into_inner();
    let resp = service
        .workspace_pending_invitations(&session, slug)
        .await?;
    Ok(ApiResponse::ok(resp).to_response())
}

#[utoipa::path(
    delete,
    path = "/api/workspaces/{slug}/invitations/{user_id}",
    params(
        ("slug" = String, Path),
        ("user_id" = Uuid, Path),
    ),
    responses(
        (status = 200, description = "Cancel invitation"),
        (status = 401, description = "Unauthorized"),
        (status = 403, description = "Permission denied"),
        (status = 404, description = "Invitation not found"),
    ),
    tag = "Workspace"
)]
pub async fn workspace_cancel_invitation(
    service: web::Data<AppService>,
    session: Session,
    path: web::Path<(String, Uuid)>,
) -> Result<HttpResponse, ApiError> {
    let (slug, user_id) = path.into_inner();
    service
        .workspace_cancel_invitation(&session, slug, user_id)
        .await?;
    Ok(ApiResponse::ok(serde_json::json!({ "success": true })).to_response())
}

#[utoipa::path(
    post,
    path = "/api/workspaces/{slug}/invitations",
    params(("slug" = String, Path)),
    request_body = WorkspaceInviteParams,
    responses(
        (status = 200, description = "Send invitation"),
        (status = 401, description = "Unauthorized"),
        (status = 403, description = "Permission denied"),
        (status = 404, description = "User not found"),
        (status = 409, description = "Already a member"),
    ),
    tag = "Workspace"
)]
pub async fn workspace_invite_member(
    service: web::Data<AppService>,
    session: Session,
    path: web::Path<String>,
    body: web::Json<WorkspaceInviteParams>,
) -> Result<HttpResponse, ApiError> {
    let slug = path.into_inner();
    service
        .workspace_invite_member(&session, slug, body.into_inner())
        .await?;
    Ok(ApiResponse::ok(serde_json::json!({ "success": true })).to_response())
}

#[utoipa::path(
    post,
    path = "/api/workspaces/invitations/accept",
    request_body = WorkspaceInviteAcceptParams,
    responses(
        (status = 200, description = "Accept invitation", body = ApiResponse<service::workspace::info::WorkspaceInfoResponse>),
        (status = 400, description = "Invalid or expired token"),
        (status = 401, description = "Unauthorized"),
        (status = 409, description = "Already accepted"),
    ),
    tag = "Workspace"
)]
pub async fn workspace_accept_invitation(
    service: web::Data<AppService>,
    session: Session,
    body: web::Json<WorkspaceInviteAcceptParams>,
) -> Result<HttpResponse, ApiError> {
    let ws = service
        .workspace_accept_invitation(&session, body.into_inner())
        .await?;
    let resp = service.workspace_info(&session, ws.slug).await?;
    Ok(ApiResponse::ok(resp).to_response())
}

/// List all pending workspace invitations for the current user.
#[utoipa::path(
    get,
    path = "/api/workspaces/me/invitations",
    responses(
        (status = 200, description = "List my workspace invitations", body = ApiResponse<Vec<MyWorkspaceInvitation>>),
        (status = 401, description = "Unauthorized"),
    ),
    tag = "Workspace"
)]
pub async fn workspace_my_invitations(
    service: web::Data<AppService>,
    session: Session,
) -> Result<HttpResponse, ApiError> {
    let resp = service.workspace_my_pending_invitations(&session).await?;
    Ok(ApiResponse::ok(resp).to_response())
}

/// Accept a workspace invitation by slug.
#[utoipa::path(
    post,
    path = "/api/workspaces/invitations/accept-by-slug",
    request_body = WorkspaceAcceptBySlugParams,
    responses(
        (status = 200, description = "Accept invitation", body = ApiResponse<service::workspace::info::WorkspaceInfoResponse>),
        (status = 400, description = "Invalid or expired token"),
        (status = 401, description = "Unauthorized"),
        (status = 404, description = "Invitation not found"),
        (status = 409, description = "Already accepted"),
    ),
    tag = "Workspace"
)]
pub async fn workspace_accept_invitation_by_slug(
    service: web::Data<AppService>,
    session: Session,
    body: web::Json<WorkspaceAcceptBySlugParams>,
) -> Result<HttpResponse, ApiError> {
    let ws = service
        .workspace_accept_invitation_by_slug(&session, body.into_inner())
        .await?;
    let resp = service.workspace_info(&session, ws.slug).await?;
    Ok(ApiResponse::ok(resp).to_response())
}
