package rbac

import (
	"fmt"
	"slices"
	"strings"

	"github.com/bluesky-social/indigo/atproto/syntax"
	"tangled.org/core/api/tangled"
)

// SetRepoOwner sets owner to given repo
func (e *Enforcer) SetRepoOwner(owner syntax.DID, repo syntax.DID) error {
	return e.setRoleForUser(owner.String(), "repo:owner", repo.String())
}

// DeleteRepo deletes all role assignments scoped to the repo.
func (e *Enforcer) DeleteRepo(repo syntax.DID) error {
	if repo == "" {
		// casbin treats an empty field value as match-anything. so just in case.
		return fmt.Errorf("DeleteRepo: empty repo did")
	}
	_, err := e.e.RemoveFilteredGroupingPolicy(2, repo.String())
	return err
}

// AddRepoCollaborator adds new collaborator to the repo
func (e *Enforcer) AddRepoCollaborator(user syntax.DID, repo syntax.DID) error {
	_, err := e.e.AddRoleForUser(user.String(), "repo:collaborator", repo.String())
	return err
}

// RemoveRepoCollaborator removes the collaborator from the repo.
// This won't remove inherited roles like repository owner.
func (e *Enforcer) RemoveRepoCollaborator(user syntax.DID, repo syntax.DID) error {
	_, err := e.e.DeleteRoleForUser(user.String(), "repo:collaborator", repo.String())
	return err
}

func (e *Enforcer) GetRepoCollaborators(repo syntax.DID) ([]syntax.DID, error) {
	var collaborators []syntax.DID
	members, err := e.e.GetImplicitUsersForRole("repo:collaborator", repo.String())
	if err != nil {
		return nil, err
	}
	for _, m := range members {
		if !strings.HasPrefix(m, "did:") { // skip non-user subjects like 'repo:owner'
			continue
		}
		collaborators = append(collaborators, syntax.DID(m))
	}

	slices.Sort(collaborators)
	return slices.Compact(collaborators), nil
}

func (e *Enforcer) IsRepoOwner(user syntax.DID, repo syntax.DID) (bool, error) {
	return e.e.HasRoleForUser(user.String(), "repo:owner", repo.String())
}

func (e *Enforcer) IsRepoCollaborator(user syntax.DID, repo syntax.DID) (bool, error) {
	return e.hasImplicitRoleForUser(user.String(), "repo:collaborator", repo.String())
}

func (e *Enforcer) IsRepoSecretsAllowed(user syntax.DID, repo syntax.DID) (bool, error) {
	return e.e.Enforce(user.String(), repo.String(), TnglSpindle, tangled.RepoAddSecretNSID)
}

func (e *Enforcer) IsRepoCiTriggerAllowed(user syntax.DID, repo syntax.DID) (bool, error) {
	return e.e.Enforce(user.String(), repo.String(), TnglSpindle, tangled.CiTriggerPipelineNSID)
}
