use actix_web::{HttpResponse, web};
use serde::{Deserialize, Serialize};
use service::AppService;
use service::git::commit_status::{
    CombinedCommitStatus, CommitStatusResponse, CreateCommitStatus,
};
use session::Session;

use crate::error::ApiError;

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

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

#[derive(Deserialize, utoipa::IntoParams)]
pub struct WkRepoPath {
    pub wk: String,
    pub repo: String,
}

#[derive(Deserialize, utoipa::IntoParams)]
pub struct CommitShaPath {
    pub wk: String,
    pub repo: String,
    pub sha: String,
}

#[utoipa::path(
    get, path = "/api/v1/workspace/{wk}/repos/{repo}/commits/{sha}/statuses",
    params(CommitShaPath),
    responses((status = 200, body = Vec<CommitStatusResponse>)),
    security(("session" = []))
)]
pub async fn list_statuses(
    session: Session,
    service: web::Data<AppService>,
    path: web::Path<CommitShaPath>,
) -> Result<HttpResponse, ApiError> {
    ok_json(
        service
            .git_commit_status_list_by_name(
                &session, &path.wk, &path.repo, &path.sha,
            )
            .await?,
    )
}

#[utoipa::path(
    get, path = "/api/v1/workspace/{wk}/repos/{repo}/commits/{sha}/status",
    params(CommitShaPath),
    responses((status = 200, body = CombinedCommitStatus)),
    security(("session" = []))
)]
pub async fn combined_status(
    session: Session,
    service: web::Data<AppService>,
    path: web::Path<CommitShaPath>,
) -> Result<HttpResponse, ApiError> {
    ok_json(
        service
            .git_commit_status_combined_by_name(
                &session, &path.wk, &path.repo, &path.sha,
            )
            .await?,
    )
}

#[utoipa::path(
    post, path = "/api/v1/workspace/{wk}/repos/{repo}/statuses/{sha}",
    params(CommitShaPath),
    request_body = CreateCommitStatus,
    responses((status = 201, body = CommitStatusResponse)),
    security(("session" = []))
)]
pub async fn create_status(
    session: Session,
    service: web::Data<AppService>,
    path: web::Path<CommitShaPath>,
    body: web::Json<CreateCommitStatus>,
) -> Result<HttpResponse, ApiError> {
    let user_id = session
        .user()
        .ok_or(ApiError(service::error::AppError::Unauthorized))?;
    ok_created(
        service
            .git_commit_status_create_by_name(
                &session,
                user_id,
                &path.wk,
                &path.repo,
                &path.sha,
                body.into_inner(),
            )
            .await?,
    )
}
