package service

import (
	"context"

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

func (s *Service) WriteRepositoryFile(ctx context.Context, repository db.Repository, branch, path, message string, content []byte, expectedBlobSHA string) (string, error) {
	if s == nil || s.repositoryMutations == nil {
		return "", ErrRepositoryMigrationConflict
	}
	session, err := s.repositoryMutations.AcquireSession(ctx, repository.ID, repository.FullName)
	if err != nil {
		return "", repositoryMutationError(err)
	}
	defer func() { _ = session.Release() }()
	sha, err := session.WriteFile(ctx, branch, path, message, content, expectedBlobSHA)
	return sha, repositoryMutationError(err)
}

func (s *Service) DeleteRepositoryFile(ctx context.Context, repository db.Repository, branch, path, message, expectedBlobSHA string) (string, error) {
	if s == nil || s.repositoryMutations == nil {
		return "", ErrRepositoryMigrationConflict
	}
	session, err := s.repositoryMutations.AcquireSession(ctx, repository.ID, repository.FullName)
	if err != nil {
		return "", repositoryMutationError(err)
	}
	defer func() { _ = session.Release() }()
	sha, err := session.DeleteFile(ctx, branch, path, message, expectedBlobSHA)
	return sha, repositoryMutationError(err)
}

func (s *Service) setPullRequestHeadReference(ctx context.Context, baseRepository, headRepository db.Repository, headSHA string, number int) error {
	sessions, release, err := s.acquireOrderedRepositoryMutationSessions(ctx, baseRepository, headRepository)
	if err != nil {
		return err
	}
	defer release()
	return s.setPullRequestHeadReferenceWithSessions(ctx, sessions, baseRepository, headRepository, headSHA, number)
}

func (s *Service) setPullRequestHeadReferenceWithSessions(ctx context.Context, sessions map[uint]*repositorymutation.Session, baseRepository, headRepository db.Repository, headSHA string, number int) error {
	baseSession := sessions[baseRepository.ID]
	headSession := sessions[headRepository.ID]
	if baseSession == nil || headSession == nil {
		return ErrRepositoryMigrationConflict
	}
	return repositoryMutationError(baseSession.SetPullRequestHead(ctx, headSession, headSHA, number))
}

func (s *Service) CreateRepositoryTag(ctx context.Context, repository db.Repository, tagName, message, sha string) error {
	if s == nil || s.repositoryMutations == nil {
		return ErrRepositoryMigrationConflict
	}
	session, err := s.repositoryMutations.AcquireSession(ctx, repository.ID, repository.FullName)
	if err != nil {
		return repositoryMutationError(err)
	}
	defer func() { _ = session.Release() }()
	return repositoryMutationError(session.CreateTagIfNotExists(ctx, tagName, message, sha))
}

func (s *Service) CreateRepositoryRef(ctx context.Context, repository db.Repository, ref, sha string) error {
	if s == nil || s.repositoryMutations == nil {
		return ErrRepositoryMigrationConflict
	}
	session, err := s.repositoryMutations.AcquireSession(ctx, repository.ID, repository.FullName)
	if err != nil {
		return repositoryMutationError(err)
	}
	defer func() { _ = session.Release() }()
	return repositoryMutationError(session.CreateReference(ctx, ref, sha))
}

func (s *Service) UpdateRepositoryRef(ctx context.Context, repository db.Repository, ref, sha string, force bool) error {
	if s == nil || s.repositoryMutations == nil {
		return ErrRepositoryMigrationConflict
	}
	session, err := s.repositoryMutations.AcquireSession(ctx, repository.ID, repository.FullName)
	if err != nil {
		return repositoryMutationError(err)
	}
	defer func() { _ = session.Release() }()
	return repositoryMutationError(session.UpdateReference(ctx, ref, sha, force))
}

func (s *Service) SetRepositoryRef(ctx context.Context, repository db.Repository, ref, sha string) error {
	if s == nil || s.repositoryMutations == nil {
		return ErrRepositoryMigrationConflict
	}
	session, err := s.repositoryMutations.AcquireSession(ctx, repository.ID, repository.FullName)
	if err != nil {
		return repositoryMutationError(err)
	}
	defer func() { _ = session.Release() }()
	return repositoryMutationError(session.SetReference(ctx, ref, sha))
}

func (s *Service) DeleteRepositoryRef(ctx context.Context, repository db.Repository, ref string) error {
	if s == nil || s.repositoryMutations == nil {
		return ErrRepositoryMigrationConflict
	}
	session, err := s.repositoryMutations.AcquireSession(ctx, repository.ID, repository.FullName)
	if err != nil {
		return repositoryMutationError(err)
	}
	defer func() { _ = session.Release() }()
	return repositoryMutationError(session.DeleteReference(ctx, ref))
}

func (s *Service) CreateRepositoryBranchFromOID(ctx context.Context, repository db.Repository, branch, oid string) error {
	if s == nil || s.repositoryMutations == nil {
		return ErrRepositoryMigrationConflict
	}
	session, err := s.repositoryMutations.AcquireSession(ctx, repository.ID, repository.FullName)
	if err != nil {
		return repositoryMutationError(err)
	}
	defer func() { _ = session.Release() }()
	return repositoryMutationError(session.CreateBranchFromOID(ctx, branch, oid))
}

func (s *Service) CreateRepositoryBranch(ctx context.Context, repositoryID uint, fullName, branch, fromBranch string) error {
	if s == nil || s.repositoryMutations == nil {
		return ErrRepositoryMigrationConflict
	}
	session, err := s.repositoryMutations.AcquireSession(ctx, repositoryID, fullName)
	if err != nil {
		return repositoryMutationError(err)
	}
	defer func() { _ = session.Release() }()
	return repositoryMutationError(session.CreateBranch(ctx, branch, fromBranch))
}

func (s *Service) CreateRepositoryBlobObject(ctx context.Context, repository db.Repository, content []byte) (gitstore.GitBlobObject, error) {
	if s == nil || s.repositoryMutations == nil {
		return gitstore.GitBlobObject{}, ErrRepositoryMigrationConflict
	}
	session, err := s.repositoryMutations.AcquireSession(ctx, repository.ID, repository.FullName)
	if err != nil {
		return gitstore.GitBlobObject{}, repositoryMutationError(err)
	}
	defer func() { _ = session.Release() }()
	object, err := session.CreateBlobObject(ctx, content)
	return object, repositoryMutationError(err)
}

func (s *Service) CreateRepositoryTreeObject(ctx context.Context, repository db.Repository, options gitstore.CreateTreeOptions) (gitstore.GitTreeObject, error) {
	return s.createRepositoryTreeObject(ctx, repository.ID, repository.FullName, options)
}

func (s *Service) createRepositoryTreeObject(ctx context.Context, repositoryID uint, fullName string, options gitstore.CreateTreeOptions) (gitstore.GitTreeObject, error) {
	if s == nil || s.repositoryMutations == nil || s.Git == nil {
		return gitstore.GitTreeObject{}, ErrRepositoryMigrationConflict
	}
	session, err := s.repositoryMutations.AcquireSession(ctx, repositoryID, fullName)
	if err != nil {
		return gitstore.GitTreeObject{}, repositoryMutationError(err)
	}
	defer func() { _ = session.Release() }()
	sha, err := session.CreateTreeObject(ctx, options)
	if err != nil {
		return gitstore.GitTreeObject{}, repositoryMutationError(err)
	}
	object, err := s.Git.GetGitTree(ctx, fullName, sha, false)
	return object, repositoryMutationError(err)
}

func (s *Service) CreateRepositoryCommitObject(ctx context.Context, repository db.Repository, options gitstore.CreateCommitOptions) (gitstore.GitCommitObject, error) {
	return s.createRepositoryCommitObject(ctx, repository.ID, repository.FullName, options)
}

func (s *Service) createRepositoryCommitObject(ctx context.Context, repositoryID uint, fullName string, options gitstore.CreateCommitOptions) (gitstore.GitCommitObject, error) {
	if s == nil || s.repositoryMutations == nil || s.Git == nil {
		return gitstore.GitCommitObject{}, ErrRepositoryMigrationConflict
	}
	session, err := s.repositoryMutations.AcquireSession(ctx, repositoryID, fullName)
	if err != nil {
		return gitstore.GitCommitObject{}, repositoryMutationError(err)
	}
	defer func() { _ = session.Release() }()
	sha, err := session.CreateCommitObject(ctx, options)
	if err != nil {
		return gitstore.GitCommitObject{}, repositoryMutationError(err)
	}
	object, err := s.Git.GetGitCommitObject(ctx, fullName, sha)
	return object, repositoryMutationError(err)
}

func (s *Service) CreateRepositoryTagObject(ctx context.Context, repository db.Repository, options gitstore.CreateTagOptions) (gitstore.GitTagObject, error) {
	if s == nil || s.repositoryMutations == nil || s.Git == nil {
		return gitstore.GitTagObject{}, ErrRepositoryMigrationConflict
	}
	session, err := s.repositoryMutations.AcquireSession(ctx, repository.ID, repository.FullName)
	if err != nil {
		return gitstore.GitTagObject{}, repositoryMutationError(err)
	}
	defer func() { _ = session.Release() }()
	sha, err := session.CreateTagObject(ctx, options)
	if err != nil {
		return gitstore.GitTagObject{}, repositoryMutationError(err)
	}
	object, err := s.Git.GetGitTagObject(ctx, repository.FullName, sha)
	return object, repositoryMutationError(err)
}
