package repo

import (
	"context"

	ent "github.com/tidbcloud/devops/entity/github"
	"github.com/tidbcloud/devops/pkg/infra/utils/repoopt"
	"github.com/tidbcloud/devops/pkg/service/github/repo/cvt"
	"github.com/tidbcloud/pkgv2/db"
)

type (
	PatchPullRequest func(current *ent.PullRequestInDB) error
	PatchRepository  func(current *ent.RepositoryInDB) error
)

type Repo interface {
	// PR
	GetPullRequest(ctx context.Context, id uint64) (*ent.PullRequestInDB, error)
	ListPullRequest(ctx context.Context, opts ...repoopt.Option) ([]*ent.PullRequestInDB, error)
	CreatePullRequest(ctx context.Context, pr *ent.PullRequestInDB) (*ent.PullRequestInDB, error)
	PatchPullRequest(ctx context.Context, current *ent.PullRequestInDB, patch ...PatchPullRequest) error
	CountPullRequest(ctx context.Context, opts ...repoopt.Option) (int64, error)

	// Repo
	GetRepository(ctx context.Context, id uint64) (*ent.RepositoryInDB, error)
	ListRepository(ctx context.Context, opts ...repoopt.Option) ([]*ent.RepositoryInDB, error)
	CreateRepository(ctx context.Context, pr *ent.RepositoryInDB) (*ent.RepositoryInDB, error)
	PatchRepository(ctx context.Context, current *ent.RepositoryInDB, patch ...PatchRepository) error
	CountRepository(ctx context.Context, opts ...repoopt.Option) (int64, error)
}

func NewRepo(dbp db.Provider) Repo {
	driver := &driver{
		cvter:      cvt.NewConverter(),
		dbProvider: dbp,
	}

	return &repo{
		prDriver:   (*prDriver)(driver),
		repoDriver: (*repoDriver)(driver),
	}
}

type repo struct {
	prDriver   PRDriver
	repoDriver RepoDriver
}

type driver struct {
	cvter      *cvt.Converter
	dbProvider db.Provider
}

func (r *repo) GetPullRequest(ctx context.Context, id uint64) (*ent.PullRequestInDB, error) {
	return r.prDriver.Load(ctx, id)
}

func (r *repo) ListPullRequest(ctx context.Context, opts ...repoopt.Option) ([]*ent.PullRequestInDB, error) {
	return r.prDriver.Query(ctx, opts...)
}

func (r *repo) CreatePullRequest(ctx context.Context, pr *ent.PullRequestInDB) (*ent.PullRequestInDB, error) {
	return r.prDriver.Save(ctx, pr)
}

func (r *repo) PatchPullRequest(ctx context.Context, current *ent.PullRequestInDB, patch ...PatchPullRequest) error {
	for _, p := range patch {
		err := p(current)
		if err != nil {
			return err
		}
	}

	_, err := r.prDriver.Save(ctx, current)
	return err
}

func (r *repo) CountPullRequest(ctx context.Context, opts ...repoopt.Option) (int64, error) {
	return r.prDriver.Count(ctx, opts...)
}

func (r *repo) GetRepository(ctx context.Context, id uint64) (*ent.RepositoryInDB, error) {
	return r.repoDriver.Load(ctx, id)
}

func (r *repo) ListRepository(ctx context.Context, opts ...repoopt.Option) ([]*ent.RepositoryInDB, error) {
	return r.repoDriver.Query(ctx, opts...)
}

func (r *repo) CreateRepository(ctx context.Context, pr *ent.RepositoryInDB) (*ent.RepositoryInDB, error) {
	return r.repoDriver.Save(ctx, pr)
}

func (r *repo) PatchRepository(ctx context.Context, current *ent.RepositoryInDB, patch ...PatchRepository) error {
	for _, p := range patch {
		err := p(current)
		if err != nil {
			return err
		}
	}

	_, err := r.repoDriver.Save(ctx, current)
	return err
}

func (r *repo) CountRepository(ctx context.Context, opts ...repoopt.Option) (int64, error) {
	return r.repoDriver.Count(ctx, opts...)
}
