use crate::AppService;
use crate::error::AppError;
use models::projects::{MemberRole, project_members};
use models::repos::{repo, repo_history_name};
use sea_orm::*;
use session::Session;

impl AppService {
    pub async fn utils_find_repo(
        &self,
        namespace: String,
        repo_name: String,
        context: &Session,
    ) -> Result<repo::Model, AppError> {
        let project = self.utils_find_project_by_name(namespace).await?;
        // Propagate DB errors — silently treating them as "not found" would mask infrastructure issues.
        let repo = repo::Entity::find()
            .filter(repo::Column::RepoName.eq(repo_name.clone()))
            .filter(repo::Column::Project.eq(project.id))
            .one(&self.db)
            .await?;

        let repo = match repo {
            Some(r) => r,
            // Fall back to historical repo names (renamed repos).
            None => {
                let hist = repo_history_name::Entity::find()
                    .filter(repo_history_name::Column::Name.eq(repo_name))
                    .filter(repo_history_name::Column::Project.eq(project.id))
                    .one(&self.db)
                    .await?
                    .ok_or(AppError::RepoNotFound)?;
                repo::Entity::find()
                    .filter(repo::Column::Id.eq(hist.repo))
                    .filter(repo::Column::Project.eq(project.id))
                    .one(&self.db)
                    .await?
                    .ok_or(AppError::RepoNotFound)?
            }
        };

        if repo.is_private {
            if let Some(user_uid) = context.user() {
                let project_member = project_members::Entity::find()
                    .filter(project_members::Column::Project.eq(repo.project))
                    .filter(project_members::Column::User.eq(user_uid))
                    .one(&self.db)
                    .await?;
                if project_member.is_none() {
                    return Err(AppError::RepoForBidAccess);
                }
            } else {
                return Err(AppError::RepoForBidAccess);
            }
        }

        Ok(repo)
    }

    pub async fn utils_check_repo_admin(
        &self,
        namespace: String,
        repo_name: String,
        context: &Session,
    ) -> Result<repo::Model, AppError> {
        let user_uid = context.user().ok_or(AppError::Unauthorized)?;
        let repo = self.utils_find_repo(namespace, repo_name, context).await?;

        let member = project_members::Entity::find()
            .filter(project_members::Column::Project.eq(repo.project))
            .filter(project_members::Column::User.eq(user_uid))
            .one(&self.db)
            .await?
            .ok_or(AppError::NoPower)?;

        let role = member.scope_role().map_err(|_| AppError::RoleParseError)?;
        if role == MemberRole::Admin || role == MemberRole::Owner {
            Ok(repo)
        } else {
            Err(AppError::NoPower)
        }
    }
}
