use db::sqlx;
use serde::{Deserialize, Serialize};
use session::Session;
use utoipa::ToSchema;

use crate::{AppService, Pagination, error::AppError};

#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ContributorDto {
    pub name: String,
    pub email: String,
    #[schema(value_type = Option<String>)]
    pub user_id: Option<uuid::Uuid>,
    pub commit_count: i64,
}

impl AppService {
    pub async fn git_repo_contributors(
        &self,
        ctx: &Session,
        wk_name: &str,
        repo_name: &str,
        pagination: Pagination,
    ) -> Result<Vec<ContributorDto>, AppError> {
        let repo = self.git_require_member(ctx, wk_name, repo_name).await?;

        let offset = pagination.offset() as i64;
        let limit = pagination.limit() as i64;

        let rows = sqlx::query_as::<_, (String, String, Option<uuid::Uuid>, Option<i64>)>(
            "SELECT rc.name, rc.email, rc.user, COUNT(rco.id)::bigint AS commit_count \
             FROM repo_committer rc \
             LEFT JOIN repo_commit rco ON rc.id = rco.author \
             WHERE rc.repo = $1 \
             GROUP BY rc.id, rc.name, rc.email, rc.user \
             ORDER BY commit_count DESC \
             OFFSET $2 LIMIT $3",
        )
        .bind(repo.id)
        .bind(offset)
        .bind(limit)
        .fetch_all(self.db.reader())
        .await
        .map_err(|e| AppError::DatabaseError(e.to_string()))?;

        Ok(rows
            .into_iter()
            .map(|(name, email, user_id, commit_count)| ContributorDto {
                name,
                email,
                user_id,
                commit_count: commit_count.unwrap_or(0),
            })
            .collect())
    }
}
