use crate::{ApiResponse, error::ApiError};
use actix_web::{HttpResponse, Result, web};
use service::AppService;
use service::project::board::{
    BoardResponse, BoardWithColumnsResponse, CardResponse, ColumnResponse, CreateBoardParams,
    CreateCardParams, CreateColumnParams, MoveCardParams, UpdateBoardParams, UpdateCardParams,
    UpdateColumnParams,
};
use session::Session;
use uuid::Uuid;

#[utoipa::path(
    get,
    path = "/api/projects/{project_name}/boards",
    params(
        ("project_name" = String, Path, description = "Project name"),
    ),
    responses(
        (status = 401, description = "Unauthorized"),
        (status = 200, description = "List boards", body = ApiResponse<Vec<BoardResponse>>),
    ),
    tag = "Project"
)]
pub async fn board_list(
    service: web::Data<AppService>,
    session: Session,
    path: web::Path<String>,
) -> Result<HttpResponse, ApiError> {
    let project_name = path.into_inner();
    let boards = service.board_list(project_name, &session).await?;
    Ok(ApiResponse::ok(boards).to_response())
}

#[utoipa::path(
    get,
    path = "/api/projects/{project_name}/boards/{board_id}",
    params(
        ("project_name" = String, Path),
        ("board_id" = Uuid, Path),
    ),
    responses(
        (status = 401, description = "Unauthorized"),
        (status = 200, description = "Get board with columns and cards", body = ApiResponse<BoardWithColumnsResponse>),
        (status = 404, description = "Not found"),
    ),
    tag = "Project"
)]
pub async fn board_get(
    service: web::Data<AppService>,
    session: Session,
    path: web::Path<(String, Uuid)>,
) -> Result<HttpResponse, ApiError> {
    let (project_name, board_id) = path.into_inner();
    let board = service.board_get(project_name, board_id, &session).await?;
    Ok(ApiResponse::ok(board).to_response())
}

#[utoipa::path(
    post,
    path = "/api/projects/{project_name}/boards",
    params(
        ("project_name" = String, Path),
    ),
    request_body = CreateBoardParams,
    responses(
        (status = 401, description = "Unauthorized"),
        (status = 200, description = "Create board", body = ApiResponse<BoardResponse>),
    ),
    tag = "Project"
)]
pub async fn board_create(
    service: web::Data<AppService>,
    session: Session,
    path: web::Path<String>,
    body: web::Json<CreateBoardParams>,
) -> Result<HttpResponse, ApiError> {
    let project_name = path.into_inner();
    let board = service
        .board_create(project_name, body.into_inner(), &session)
        .await?;
    Ok(ApiResponse::ok(board).to_response())
}

#[utoipa::path(
    patch,
    path = "/api/projects/{project_name}/boards/{board_id}",
    params(
        ("project_name" = String, Path),
        ("board_id" = Uuid, Path),
    ),
    request_body = UpdateBoardParams,
    responses(
        (status = 401, description = "Unauthorized"),
        (status = 200, description = "Update board", body = ApiResponse<BoardResponse>),
        (status = 404, description = "Not found"),
    ),
    tag = "Project"
)]
pub async fn board_update(
    service: web::Data<AppService>,
    session: Session,
    path: web::Path<(String, Uuid)>,
    body: web::Json<UpdateBoardParams>,
) -> Result<HttpResponse, ApiError> {
    let (project_name, board_id) = path.into_inner();
    let board = service
        .board_update(project_name, board_id, body.into_inner(), &session)
        .await?;
    Ok(ApiResponse::ok(board).to_response())
}

#[utoipa::path(
    delete,
    path = "/api/projects/{project_name}/boards/{board_id}",
    params(
        ("project_name" = String, Path),
        ("board_id" = Uuid, Path),
    ),
    responses(
        (status = 401, description = "Unauthorized"),
        (status = 200, description = "Delete board"),
        (status = 404, description = "Not found"),
    ),
    tag = "Project"
)]
pub async fn board_delete(
    service: web::Data<AppService>,
    session: Session,
    path: web::Path<(String, Uuid)>,
) -> Result<HttpResponse, ApiError> {
    let (project_name, board_id) = path.into_inner();
    service
        .board_delete(project_name, board_id, &session)
        .await?;
    Ok(crate::api_success())
}

#[utoipa::path(
    post,
    path = "/api/projects/{project_name}/boards/{board_id}/columns",
    params(
        ("project_name" = String, Path),
        ("board_id" = Uuid, Path),
    ),
    request_body = CreateColumnParams,
    responses(
        (status = 401, description = "Unauthorized"),
        (status = 200, description = "Create column", body = ApiResponse<ColumnResponse>),
        (status = 404, description = "Board not found"),
    ),
    tag = "Project"
)]
pub async fn column_create(
    service: web::Data<AppService>,
    session: Session,
    path: web::Path<(String, Uuid)>,
    body: web::Json<CreateColumnParams>,
) -> Result<HttpResponse, ApiError> {
    let (project_name, board_id) = path.into_inner();
    let column = service
        .column_create(project_name, board_id, body.into_inner(), &session)
        .await?;
    Ok(ApiResponse::ok(column).to_response())
}

#[utoipa::path(
    patch,
    path = "/api/projects/{project_name}/columns/{column_id}",
    params(
        ("project_name" = String, Path),
        ("column_id" = Uuid, Path),
    ),
    request_body = UpdateColumnParams,
    responses(
        (status = 401, description = "Unauthorized"),
        (status = 200, description = "Update column", body = ApiResponse<ColumnResponse>),
        (status = 404, description = "Not found"),
    ),
    tag = "Project"
)]
pub async fn column_update(
    service: web::Data<AppService>,
    session: Session,
    path: web::Path<(String, Uuid)>,
    body: web::Json<UpdateColumnParams>,
) -> Result<HttpResponse, ApiError> {
    let (project_name, column_id) = path.into_inner();
    let column = service
        .column_update(project_name, column_id, body.into_inner(), &session)
        .await?;
    Ok(ApiResponse::ok(column).to_response())
}

#[utoipa::path(
    delete,
    path = "/api/projects/{project_name}/columns/{column_id}",
    params(
        ("project_name" = String, Path),
        ("column_id" = Uuid, Path),
    ),
    responses(
        (status = 401, description = "Unauthorized"),
        (status = 200, description = "Delete column"),
        (status = 404, description = "Not found"),
    ),
    tag = "Project"
)]
pub async fn column_delete(
    service: web::Data<AppService>,
    session: Session,
    path: web::Path<(String, Uuid)>,
) -> Result<HttpResponse, ApiError> {
    let (project_name, column_id) = path.into_inner();
    service
        .column_delete(project_name, column_id, &session)
        .await?;
    Ok(crate::api_success())
}

#[utoipa::path(
    post,
    path = "/api/projects/{project_name}/cards",
    params(
        ("project_name" = String, Path),
    ),
    request_body = CreateCardParams,
    responses(
        (status = 401, description = "Unauthorized"),
        (status = 200, description = "Create card", body = ApiResponse<CardResponse>),
    ),
    tag = "Project"
)]
pub async fn card_create(
    service: web::Data<AppService>,
    session: Session,
    path: web::Path<String>,
    body: web::Json<CreateCardParams>,
) -> Result<HttpResponse, ApiError> {
    let project_name = path.into_inner();
    let card = service
        .card_create(project_name, body.into_inner(), &session)
        .await?;
    Ok(ApiResponse::ok(card).to_response())
}

#[utoipa::path(
    patch,
    path = "/api/projects/{project_name}/cards/{card_id}",
    params(
        ("project_name" = String, Path),
        ("card_id" = Uuid, Path),
    ),
    request_body = UpdateCardParams,
    responses(
        (status = 401, description = "Unauthorized"),
        (status = 200, description = "Update card", body = ApiResponse<CardResponse>),
        (status = 404, description = "Not found"),
    ),
    tag = "Project"
)]
pub async fn card_update(
    service: web::Data<AppService>,
    session: Session,
    path: web::Path<(String, Uuid)>,
    body: web::Json<UpdateCardParams>,
) -> Result<HttpResponse, ApiError> {
    let (project_name, card_id) = path.into_inner();
    let card = service
        .card_update(project_name, card_id, body.into_inner(), &session)
        .await?;
    Ok(ApiResponse::ok(card).to_response())
}

#[utoipa::path(
    post,
    path = "/api/projects/{project_name}/cards/{card_id}/move",
    params(
        ("project_name" = String, Path),
        ("card_id" = Uuid, Path),
    ),
    request_body = MoveCardParams,
    responses(
        (status = 401, description = "Unauthorized"),
        (status = 200, description = "Move card", body = ApiResponse<CardResponse>),
        (status = 404, description = "Not found"),
    ),
    tag = "Project"
)]
pub async fn card_move(
    service: web::Data<AppService>,
    session: Session,
    path: web::Path<(String, Uuid)>,
    body: web::Json<MoveCardParams>,
) -> Result<HttpResponse, ApiError> {
    let (project_name, card_id) = path.into_inner();
    let card = service
        .card_move(project_name, card_id, body.into_inner(), &session)
        .await?;
    Ok(ApiResponse::ok(card).to_response())
}

#[utoipa::path(
    delete,
    path = "/api/projects/{project_name}/cards/{card_id}",
    params(
        ("project_name" = String, Path),
        ("card_id" = Uuid, Path),
    ),
    responses(
        (status = 401, description = "Unauthorized"),
        (status = 200, description = "Delete card"),
        (status = 404, description = "Not found"),
    ),
    tag = "Project"
)]
pub async fn card_delete(
    service: web::Data<AppService>,
    session: Session,
    path: web::Path<(String, Uuid)>,
) -> Result<HttpResponse, ApiError> {
    let (project_name, card_id) = path.into_inner();
    service.card_delete(project_name, card_id, &session).await?;
    Ok(crate::api_success())
}
