use actix_web::{HttpResponse, web};
use serde::{Deserialize, Serialize};
use service::AppService;
use session::Session;

use crate::error::ApiError;
use crate::git::dto;

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

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

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

#[derive(Deserialize, utoipa::IntoParams)]
pub struct TreeQuery {
    pub path: Option<String>,
    #[serde(default)]
    pub last: bool,
    #[serde(default)]
    pub resolve: bool,
}
#[utoipa::path(
    get, path = "/api/v1/workspace/{wk}/repos/{repo}/git/trees/{oid}",
    params(WkRepoTreePath, TreeQuery),
    responses((status = 200, description = "Tree entries", body = dto::TreeEntriesResponseDto)),
    security(("session" = []))
)]
pub async fn tree_entries(
    session: Session,
    service: web::Data<AppService>,
    path: web::Path<WkRepoTreePath>,
    query: web::Query<TreeQuery>,
) -> Result<HttpResponse, ApiError> {
    let WkRepoTreePath { wk, repo, oid } = path.into_inner();

    if query.resolve {
        let data: dto::ResolveTreeResponseDto = service
            .git_resolve_tree(&session, &wk, &repo, oid)
            .await?
            .into();
        return ok_json(data);
    }

    let base_path = query.path.clone().unwrap_or_default();
    let data: dto::TreeEntriesResponseDto = service
        .git_tree_entries(&session, &wk, &repo, oid, base_path, query.last)
        .await?
        .into();
    ok_json(data)
}

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

#[derive(Deserialize, utoipa::IntoParams)]
pub struct TreeEntryQuery {
    pub path: String,
}
#[utoipa::path(
    get, path = "/api/v1/workspace/{wk}/repos/{repo}/git/trees/{tree_oid}/entries",
    params(WkRepoTreeSubPath, TreeEntryQuery),
    responses((status = 200, description = "Tree entry", body = dto::TreeEntryByPathResponseDto)),
    security(("session" = []))
)]
pub async fn tree_entry_by_path(
    session: Session,
    service: web::Data<AppService>,
    path: web::Path<WkRepoTreeSubPath>,
    query: web::Query<TreeEntryQuery>,
) -> Result<HttpResponse, ApiError> {
    let WkRepoTreeSubPath { wk, repo, tree_oid } = path.into_inner();
    let data: dto::TreeEntryByPathResponseDto = service
        .git_tree_entry_by_path(
            &session,
            &wk,
            &repo,
            tree_oid,
            query.path.clone(),
        )
        .await?
        .into();
    ok_json(data)
}

#[derive(Deserialize, utoipa::IntoParams)]
pub struct WkRepoCommitPath {
    pub wk: String,
    pub repo: String,
    pub oid: String,
}
#[utoipa::path(
    get, path = "/api/v1/workspace/{wk}/repos/{repo}/git/commits/{oid}/tree",
    params(WkRepoCommitPath, TreeEntryQuery),
    responses((status = 200, description = "Tree entry", body = dto::TreeEntryByPathResponseDto)),
    security(("session" = []))
)]
pub async fn tree_entry_by_path_from_commit(
    session: Session,
    service: web::Data<AppService>,
    path: web::Path<WkRepoCommitPath>,
    query: web::Query<TreeEntryQuery>,
) -> Result<HttpResponse, ApiError> {
    let WkRepoCommitPath { wk, repo, oid } = path.into_inner();
    let data: dto::TreeEntryByPathResponseDto = service
        .git_tree_entry_by_path_from_commit(
            &session,
            &wk,
            &repo,
            oid,
            query.path.clone(),
        )
        .await?
        .into();
    ok_json(data)
}
