package kb

import (
	"context"
	"strings"

	"github.com/tidbcloud/AgentTeam/internal/store/kb"
)

func (s *KBService) UpdateKnowledgeBasePermissionRepositoryOverrides(ctx context.Context, workspaceID, subject, kbID, grantID string, input []KBPermissionRepositoryOverrideMutation) (KBPermissionGrant, error) {
	base, err := s.GetKnowledgeBase(ctx, workspaceID, subject, kbID)
	if err != nil {
		return KBPermissionGrant{}, err
	}
	if !s.canManageKnowledgeBase(ctx, base, subject) {
		return KBPermissionGrant{}, forbidden("knowledge base requires manage access")
	}
	grant, err := s.store.GetKnowledgeBaseGrantByID(ctx, workspaceID, kbID, strings.TrimSpace(grantID))
	if err != nil {
		return KBPermissionGrant{}, mapStoreNotFound(err)
	}
	if err := s.applyKnowledgeBasePermissionRepositoryOverrides(ctx, base, grant, subject, input); err != nil {
		return KBPermissionGrant{}, err
	}
	if err := s.reconcileKnowledgeBaseGrantRepositoryAccess(ctx, base, grant, subject); err != nil {
		return KBPermissionGrant{}, err
	}
	access, err := s.knowledgeBaseRepositoryAccessForGrant(ctx, strings.TrimSpace(workspaceID), grant.ID)
	if err != nil {
		return KBPermissionGrant{}, err
	}
	converted := serviceGrantFromStoreWithRepositoryAccess(grant, access)
	if err := s.decoratePermissionGrantSourceRepositoryAccess(ctx, base, &converted, access); err != nil {
		return KBPermissionGrant{}, err
	}
	return converted, nil
}

func (s *KBService) applyKnowledgeBasePermissionRepositoryOverrides(ctx context.Context, base kb.KnowledgeBase, grant kb.KnowledgeBaseGrant, actor string, input []KBPermissionRepositoryOverrideMutation) error {
	if len(input) == 0 {
		return nil
	}
	if grant.SubjectType != "user" {
		return invalidArgument("repository overrides only apply to user grants")
	}
	repos, err := s.listKnowledgeBaseSourceRepositoryMappings(ctx, base.WorkspaceID, base.ID)
	if err != nil {
		return err
	}
	eligible := make(map[string]struct{})
	for _, repo := range eligibleHumanRepositoryAccessRepos(repos) {
		eligible[repositoryOverrideKey(repo.ResourceID, repo.RepoRole)] = struct{}{}
	}
	existingOverrides, err := s.store.ListKnowledgeBaseSourceRepositoryAccessOverrides(ctx, base.WorkspaceID, base.ID, []string{grant.ID})
	if err != nil {
		return err
	}
	existingBySourceRepo := make(map[string]string, len(existingOverrides))
	for _, row := range existingOverrides {
		existingBySourceRepo[repositoryOverrideKey(row.SourceID, row.RepoRole)] = kb.NormalizeKnowledgeBaseRepositoryAccessProfile(row.RepositoryAccessProfile)
	}
	now := s.now()
	for _, item := range input {
		sourceID := strings.TrimSpace(item.SourceID)
		repoRole := strings.TrimSpace(item.RepoRole)
		if sourceID == "" || repoRole == "" {
			return invalidArgument("repository override source id and repo role are required")
		}
		if _, ok := eligible[repositoryOverrideKey(sourceID, repoRole)]; !ok {
			return invalidArgument("repository override target is not an eligible source repository")
		}
		key := repositoryOverrideKey(sourceID, repoRole)
		previousProfile, hadPrevious := existingBySourceRepo[key]
		if item.RepositoryAccessProfile == nil {
			if err := s.store.DeleteKnowledgeBaseSourceRepositoryAccessOverride(ctx, base.WorkspaceID, base.ID, grant.ID, sourceID, repoRole); err != nil {
				return err
			}
			if hadPrevious {
				if err := s.auditRepositoryAccessOverrideIntent(ctx, grant, actor, sourceID, repoRole, previousProfile, "", true); err != nil {
					return err
				}
				delete(existingBySourceRepo, key)
			}
			continue
		}
		profile, err := normalizeKBRepositoryAccessProfile(*item.RepositoryAccessProfile)
		if err != nil {
			return err
		}
		if _, err := s.store.UpsertKnowledgeBaseSourceRepositoryAccessOverride(ctx, kb.KnowledgeBaseSourceRepositoryAccessOverride{
			ID:                      kb.KnowledgeBaseSourceRepositoryAccessOverrideID(base.WorkspaceID, base.ID, grant.ID, sourceID, repoRole),
			WorkspaceID:             base.WorkspaceID,
			KnowledgeBaseID:         base.ID,
			GrantID:                 grant.ID,
			SourceID:                sourceID,
			RepoRole:                repoRole,
			SubjectType:             grant.SubjectType,
			SubjectID:               grant.SubjectID,
			RepositoryAccessProfile: profile,
			CreatedBy:               strings.TrimSpace(actor),
			UpdatedBy:               strings.TrimSpace(actor),
			CreatedAt:               now,
			UpdatedAt:               now,
		}); err != nil {
			return err
		}
		if err := s.auditRepositoryAccessOverrideIntent(ctx, grant, actor, sourceID, repoRole, previousProfile, profile, false); err != nil {
			return err
		}
		existingBySourceRepo[key] = profile
	}
	return nil
}
