package service

import (
	"context"

	"github.com/pingcap/errors"
	"go.uber.org/zap"
	"gorm.io/gorm"

	"github.com/pingcap/log"

	apierror "github.com/pingcap-inc/clinic/pkg/api/error"
	"github.com/pingcap-inc/clinic/pkg/db"
	"github.com/pingcap-inc/clinic/pkg/pagination"
)

type IndexAdvisorService interface {
	QueryResult(ctx context.Context, id uint64) (result db.IndexAdvisorResult, err error)
	QueryResults(ctx context.Context, dbaasOrgID, dbaasClusterID, limit, page uint64, timeOrder string) (results []db.IndexAdvisorResult, count int64, err error)
}

func NewIndexAdvisorService() IndexAdvisorService {
	return &indexAdvisorService{}
}

type indexAdvisorService struct{}

func (i *indexAdvisorService) QueryResult(ctx context.Context, id uint64) (result db.IndexAdvisorResult, err error) {
	err = db.TransactionWithContext(ctx, func(tx *gorm.DB) error {
		return tx.Model(&db.IndexAdvisorResult{}).Take(&result, id).Error
	})
	if err != nil {
		log.Error("failed to query index advisor result",
			zap.Error(err),
			zap.Uint64("id", id))
		return db.IndexAdvisorResult{}, apierror.ErrInternal.New("internal error: failed to query index advisor result")
	}
	return
}

func (i *indexAdvisorService) QueryResults(ctx context.Context, dbaasOrgID, dbaasClusterID, limit, page uint64, timeOrder string) (results []db.IndexAdvisorResult, count int64, err error) {
	err = db.TransactionWithContext(ctx, func(tx *gorm.DB) error {
		q := tx.Model(&db.IndexAdvisorResult{}).Where("dbaas_org_id = ? AND dbaas_cluster_id = ?", dbaasOrgID, dbaasClusterID)

		// Count # of results that match the query.
		if err := q.Count(&count).Error; err != nil {
			return errors.Annotate(err, "count index advisor results error")
		}

		// Pagination.
		offset := pagination.GetStartIndex(int(page), int(limit))
		q = q.Limit(int(limit)).Offset(offset).Order("updated_at " + timeOrder)

		// Do query.
		return q.Find(&results).Error
	})
	if err != nil {
		log.Error("failed to query index advisor results",
			zap.Error(err),
			zap.Uint64("dbaasOrgID", dbaasOrgID),
			zap.Uint64("dbaasClusterID", dbaasClusterID))
		return nil, 0, apierror.ErrInternal.New("internal error: failed to query index advisor results")
	}
	return
}
