use db::sqlx;
use model::repos::RepoRefModel;
use session::Session;
use uuid::Uuid;

use crate::AppService;
use crate::error::AppError;

#[derive(Debug, Clone, serde::Serialize, utoipa::ToSchema)]
pub struct GitRefResponse {
    pub name: String,
    pub kind: String,
    pub target_sha: String,
    pub is_default: bool,
    pub is_protected: bool,
}

impl AppService {
    pub async fn git_ref_list_by_name(
        &self,
        ctx: &Session,
        wk: &str,
        repo: &str,
    ) -> Result<Vec<GitRefResponse>, AppError> {
        let repo = self.git_require_member(ctx, wk, repo).await?;
        self.git_ref_list(repo.id).await
    }
    pub async fn git_ref_get_by_name(
        &self,
        ctx: &Session,
        wk: &str,
        repo: &str,
        ref_name: &str,
    ) -> Result<GitRefResponse, AppError> {
        let repo = self.git_require_member(ctx, wk, repo).await?;
        self.git_ref_get(repo.id, ref_name).await
    }
}

impl AppService {
    pub async fn git_ref_list(
        &self,
        repo_id: Uuid,
    ) -> Result<Vec<GitRefResponse>, AppError> {
        let refs = sqlx::query_as::<_, RepoRefModel>(
            "SELECT id, repo, name, kind, target_sha, is_default, is_protected, \
             created_at, updated_at FROM repo_ref WHERE repo = $1 ORDER BY name",
        )
        .bind(repo_id)
        .fetch_all(self.db.reader())
        .await
        .map_err(|e| AppError::DatabaseError(e.to_string()))?;

        Ok(refs
            .into_iter()
            .map(|r| GitRefResponse {
                name: r.name,
                kind: r.kind,
                target_sha: r.target_sha,
                is_default: r.is_default,
                is_protected: r.is_protected,
            })
            .collect())
    }

    pub async fn git_ref_get(
        &self,
        repo_id: Uuid,
        ref_name: &str,
    ) -> Result<GitRefResponse, AppError> {
        let r = sqlx::query_as::<_, RepoRefModel>(
            "SELECT id, repo, name, kind, target_sha, is_default, is_protected, \
             created_at, updated_at FROM repo_ref WHERE repo = $1 AND name = $2",
        )
        .bind(repo_id)
        .bind(ref_name)
        .fetch_optional(self.db.reader())
        .await
        .map_err(|e| AppError::DatabaseError(e.to_string()))?
        .ok_or_else(|| AppError::NotFound("ref not found".to_string()))?;

        Ok(GitRefResponse {
            name: r.name,
            kind: r.kind,
            target_sha: r.target_sha,
            is_default: r.is_default,
            is_protected: r.is_protected,
        })
    }
}
