package service

import (
	"context"
	"sort"

	"github.com/pingcap/agent-git-service/internal/db"
	"github.com/pingcap/agent-git-service/internal/service/internal/repositorymutation"
)

// acquireOrderedRepositoryMutationSessions acquires one private writer session
// per exact repository identity in immutable-ID order. Cross-repository
// operations use this single ordering rule instead of nesting independently
// acquired writers.
func (s *Service) acquireOrderedRepositoryMutationSessions(ctx context.Context, repositories ...db.Repository) (map[uint]*repositorymutation.Session, func(), error) {
	if s == nil || s.repositoryMutations == nil || len(repositories) == 0 {
		return nil, nil, ErrRepositoryMigrationConflict
	}
	ordered := append([]db.Repository(nil), repositories...)
	sort.Slice(ordered, func(i, j int) bool {
		if ordered[i].ID != ordered[j].ID {
			return ordered[i].ID < ordered[j].ID
		}
		return ordered[i].FullName < ordered[j].FullName
	})
	deduped := ordered[:0]
	names := make(map[string]uint, len(ordered))
	for _, repository := range ordered {
		if repository.ID == 0 || repository.FullName == "" {
			return nil, nil, ErrRepositoryMigrationConflict
		}
		if existingID, ok := names[repository.FullName]; ok && existingID != repository.ID {
			return nil, nil, ErrRepositoryMigrationConflict
		}
		names[repository.FullName] = repository.ID
		if len(deduped) > 0 && deduped[len(deduped)-1].ID == repository.ID {
			if deduped[len(deduped)-1].FullName != repository.FullName {
				return nil, nil, ErrRepositoryMigrationConflict
			}
			continue
		}
		deduped = append(deduped, repository)
	}

	acquired := make([]*repositorymutation.Session, 0, len(deduped))
	release := func() {
		for index := len(acquired) - 1; index >= 0; index-- {
			_ = acquired[index].Release()
		}
	}
	sessions := make(map[uint]*repositorymutation.Session, len(deduped))
	for _, repository := range deduped {
		session, err := s.repositoryMutations.AcquireSession(ctx, repository.ID, repository.FullName)
		if err != nil {
			release()
			return nil, nil, repositoryMutationError(err)
		}
		acquired = append(acquired, session)
		sessions[repository.ID] = session
	}
	return sessions, release, nil
}
