use actix_web::{HttpResponse, web};
use serde::Serialize;
use service::{
    AppService, Pagination,
    users::relation::{
        UserRelationCard, UserRelationCounts, UserRelationStatus,
    },
};
use session::Session;

use crate::error::ApiError;

fn ok_json<T: Serialize>(data: T) -> Result<HttpResponse, ApiError> {
    Ok(HttpResponse::Ok().json(data))
}

#[utoipa::path(
    post,
    path = "/api/v1/users/{username}/follow",
    params(("username" = String, Path)),
    responses((status = 200, body = UserRelationStatus)),
    tag = "users"
)]
pub async fn follow_user(
    service: web::Data<AppService>,
    session: Session,
    username: web::Path<String>,
) -> Result<HttpResponse, ApiError> {
    let result = service
        .users_follow_by_username(&session, &username)
        .await?;
    ok_json(result)
}

#[utoipa::path(
    post,
    path = "/api/v1/users/{username}/unfollow",
    params(("username" = String, Path)),
    responses((status = 200, body = UserRelationStatus)),
    tag = "users"
)]
pub async fn unfollow_user(
    service: web::Data<AppService>,
    session: Session,
    username: web::Path<String>,
) -> Result<HttpResponse, ApiError> {
    let result = service
        .users_unfollow_by_username(&session, &username)
        .await?;
    ok_json(result)
}

#[utoipa::path(
    post,
    path = "/api/v1/users/{username}/block",
    params(("username" = String, Path)),
    responses((status = 200, body = UserRelationStatus)),
    tag = "users"
)]
pub async fn block_user(
    service: web::Data<AppService>,
    session: Session,
    username: web::Path<String>,
) -> Result<HttpResponse, ApiError> {
    let result = service.users_block_by_username(&session, &username).await?;
    ok_json(result)
}

#[utoipa::path(
    post,
    path = "/api/v1/users/{username}/unblock",
    params(("username" = String, Path)),
    responses((status = 200, body = UserRelationStatus)),
    tag = "users"
)]
pub async fn unblock_user(
    service: web::Data<AppService>,
    session: Session,
    username: web::Path<String>,
) -> Result<HttpResponse, ApiError> {
    let result = service
        .users_unblock_by_username(&session, &username)
        .await?;
    ok_json(result)
}

#[utoipa::path(
    get,
    path = "/api/v1/users/{username}/relation",
    params(("username" = String, Path)),
    responses((status = 200, body = UserRelationStatus)),
    tag = "users"
)]
pub async fn relation_status(
    service: web::Data<AppService>,
    session: Session,
    username: web::Path<String>,
) -> Result<HttpResponse, ApiError> {
    let result = service
        .users_relation_status_by_username(&session, &username)
        .await?;
    ok_json(result)
}

#[utoipa::path(
    get,
    path = "/api/v1/users/{username}/followers",
    params(("username" = String, Path), Pagination),
    responses((status = 200, body = Vec<UserRelationCard>)),
    tag = "users"
)]
pub async fn followers(
    service: web::Data<AppService>,
    session: Session,
    username: web::Path<String>,
    pagination: web::Query<Pagination>,
) -> Result<HttpResponse, ApiError> {
    let result = service
        .users_followers_by_username(
            Some(&session),
            &username,
            pagination.into_inner(),
        )
        .await?;
    ok_json(result)
}

#[utoipa::path(
    get,
    path = "/api/v1/users/{username}/following",
    params(("username" = String, Path), Pagination),
    responses((status = 200, body = Vec<UserRelationCard>)),
    tag = "users"
)]
pub async fn following(
    service: web::Data<AppService>,
    session: Session,
    username: web::Path<String>,
    pagination: web::Query<Pagination>,
) -> Result<HttpResponse, ApiError> {
    let result = service
        .users_following_by_username(
            Some(&session),
            &username,
            pagination.into_inner(),
        )
        .await?;
    ok_json(result)
}

#[utoipa::path(
    get,
    path = "/api/v1/users/blocked",
    responses((status = 200, body = Vec<UserRelationCard>)),
    tag = "users"
)]
pub async fn blocked_list(
    service: web::Data<AppService>,
    session: Session,
) -> Result<HttpResponse, ApiError> {
    let result = service.users_blocked(&session).await?;
    ok_json(result)
}

#[utoipa::path(
    get,
    path = "/api/v1/users/{username}/relation-counts",
    params(("username" = String, Path)),
    responses((status = 200, body = UserRelationCounts)),
    tag = "users"
)]
pub async fn relation_counts(
    service: web::Data<AppService>,
    username: web::Path<String>,
) -> Result<HttpResponse, ApiError> {
    let result = service.users_relation_counts_by_username(&username).await?;
    ok_json(result)
}
