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,
}

#[utoipa::path(
    get, path = "/api/v1/workspace/{wk}/repos/{repo}/git/readme",
    params(WkRepoPath),
    responses((status = 200, description = "README content", body = Option<service::git::readme::ReadmeDto>)),
    security(("session" = []))
)]
pub async fn get_readme(
    session: Session,
    service: web::Data<AppService>,
    path: web::Path<WkRepoPath>,
) -> Result<HttpResponse, ApiError> {
    let WkRepoPath { wk, repo } = path.into_inner();
    let data = service.git_repo_readme(&session, &wk, &repo).await?;
    ok_json(data)
}

#[utoipa::path(
    get, path = "/api/v1/workspace/{wk}/repos/{repo}/git/diff/branches",
    params(("wk" = String, Path), ("repo" = String, Path), ("old_branch" = String, Query), ("new_branch" = String, Query)),
    responses((status = 200, description = "Branch diff", body = dto::DiffResultDto)),
    security(("session" = []))
)]
pub async fn diff_branches(
    session: Session,
    service: web::Data<AppService>,
    path: web::Path<WkRepoPath>,
    query: web::Query<BranchDiffQuery>,
) -> Result<HttpResponse, ApiError> {
    let WkRepoPath { wk, repo } = path.into_inner();
    let q = query.into_inner();

    let old_info = service
        .git_branch_info(&session, &wk, &repo, q.old_branch.clone())
        .await?;
    let new_info = service
        .git_branch_info(&session, &wk, &repo, q.new_branch.clone())
        .await?;

    let old_oid = old_info
        .branch
        .and_then(|b| b.oid)
        .map(|o| o.value)
        .unwrap_or_default();
    let new_oid = new_info
        .branch
        .and_then(|b| b.oid)
        .map(|o| o.value)
        .unwrap_or_default();

    if old_oid.is_empty() || new_oid.is_empty() {
        return Err(service::error::AppError::NotFound(
            "could not resolve one or both branches".to_string(),
        )
        .into());
    }

    let proto_resp = service
        .git_diff_patch(&session, &wk, &repo, old_oid, new_oid, None)
        .await?;

    let data: dto::DiffResultDto = proto_resp.result.unwrap_or_default().into();
    ok_json(data)
}

#[derive(Deserialize, utoipa::IntoParams)]
pub struct BranchDiffQuery {
    pub old_branch: String,
    pub new_branch: String,
}
