package repo

import (
	"context"
	"fmt"

	jiraent "github.com/tidbcloud/devops/entity/jira"
	jiramod "github.com/tidbcloud/devops/models/jira"
	"github.com/tidbcloud/devops/pkg/infra/utils/container"
	"github.com/tidbcloud/devops/pkg/infra/utils/repoopt"

	"gorm.io/gorm"
)

type IssueDriver interface {
	Save(ctx context.Context, issue *jiraent.IssueInRepo) (*jiraent.IssueInRepo, error)
	Load(ctx context.Context, key string) (*jiraent.IssueInRepo, error)
	Query(ctx context.Context, opts ...repoopt.Option) ([]*jiraent.IssueInRepo, error)
	Count(ctx context.Context, opts ...repoopt.Option) (int64, error)
}

type imD driver

func (d *imD) Save(ctx context.Context, info *jiraent.IssueInRepo) (*jiraent.IssueInRepo, error) {
	wdb := d.dbProvider.WriteDB(ctx)
	mod := d.cvter.EntityToModel().IssueInRepo(info)

	err := wdb.Session(&gorm.Session{FullSaveAssociations: true}).Save(mod).Error
	if err != nil {
		return nil, fmt.Errorf("save failed: %w", err)
	}

	// fill back id
	return d.cvter.ModelToEntity().IssueInRepo(mod), nil
}

func (d *imD) Load(ctx context.Context, key string) (*jiraent.IssueInRepo, error) {
	rdb := d.dbProvider.ReadDB(ctx)
	mod := &jiramod.Issue{}

	err := d.buildModel(rdb).Where("`key` = ?", key).First(mod).Error
	if err != nil {
		return nil, fmt.Errorf("load %s failed: %w", key, err)
	}

	return d.cvter.ModelToEntity().IssueInRepo(mod), nil
}

func (d *imD) Query(ctx context.Context, opts ...repoopt.Option) ([]*jiraent.IssueInRepo, error) {
	rdb := d.dbProvider.ReadDB(ctx)
	mods := make([]*jiramod.Issue, 0)

	rdb = d.buildModel(rdb)
	rdb = repoopt.Aggregate(opts...).Apply(rdb)

	err := rdb.Find(&mods).Error
	if err != nil {
		return nil, fmt.Errorf("query failed: %w", err)
	}

	return container.TransformSlice(mods, d.cvter.ModelToEntity().IssueInRepo), nil
}

func (d *imD) Count(ctx context.Context, opts ...repoopt.Option) (int64, error) {
	rdb := d.dbProvider.ReadDB(ctx).Model(&jiramod.Issue{})
	rdb = repoopt.Aggregate(opts...).Apply(rdb)

	var count int64
	err := rdb.Count(&count).Error
	if err != nil {
		return 0, fmt.Errorf("count failed: %w", err)
	}

	return count, nil
}

func (d *imD) buildModel(db *gorm.DB) *gorm.DB {
	db = db.Model(&jiramod.Issue{}).Preload("Snapshot.CustomerInfo")

	return db
}
