package resourcepool

import (
	"context"
	"time"

	"github.com/tidbcloud/pkgv2/db"

	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/entity"
	resourcepoolrepo "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/resourcepool"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/infra/repoimpl/schema"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

type pauseOperationRepoImpl struct {
	provider db.Provider
}

func NewPauseOperationRepo(provider db.Provider) resourcepoolrepo.PauseOperationRepo {
	return &pauseOperationRepoImpl{provider: provider}
}

func (r *pauseOperationRepoImpl) Create(ctx context.Context, operation *entity.ResourcePoolPauseOperation) bizerr.BizError {
	if operation == nil {
		return bizerr.New(bizerr.ErrCodeParams, "pause operation is nil")
	}
	now := time.Now().UTC()
	operation.CreatedAt = now
	operation.UpdatedAt = now
	if err := r.provider.WriteDB(ctx).Create(pauseOperationToSchema(operation)).Error; err != nil {
		return bizerr.WrapGormError(err, "failed to create pause operation")
	}
	return nil
}

func (r *pauseOperationRepoImpl) Save(ctx context.Context, operation *entity.ResourcePoolPauseOperation) bizerr.BizError {
	if operation == nil {
		return bizerr.New(bizerr.ErrCodeParams, "pause operation is nil")
	}
	operation.UpdatedAt = time.Now().UTC()
	model := pauseOperationToSchema(operation)
	if err := r.provider.WriteDB(ctx).
		Model(&schema.ResourcePoolPauseOperation{}).
		Where("resource_pool_id = ? AND operation_id = ?", operation.ResourcePoolID, operation.OperationID).
		Updates(model).Error; err != nil {
		return bizerr.WrapGormError(err, "failed to save pause operation")
	}
	return nil
}

func (r *pauseOperationRepoImpl) Get(
	ctx context.Context,
	resourcePoolID uint64,
	operationID string,
) (*entity.ResourcePoolPauseOperation, bizerr.BizError) {
	model := &schema.ResourcePoolPauseOperation{}
	if err := r.provider.WriteDB(ctx).
		Where("resource_pool_id = ? AND operation_id = ?", resourcePoolID, operationID).
		First(model).Error; err != nil {
		return nil, bizerr.WrapGormError(err, "failed to get pause operation")
	}
	return pauseOperationToEntity(model), nil
}

func (r *pauseOperationRepoImpl) GetRunning(
	ctx context.Context,
	resourcePoolID uint64,
) (*entity.ResourcePoolPauseOperation, bizerr.BizError) {
	model := &schema.ResourcePoolPauseOperation{}
	if err := r.provider.ReadDB(ctx).
		Where(
			"resource_pool_id = ? AND operation_type = ? AND status = ?",
			resourcePoolID,
			entity.ResourcePoolPauseOperationTypePause,
			entity.ResourcePoolPauseOperationRunning,
		).
		Order("created_at DESC").
		First(model).Error; err != nil {
		return nil, bizerr.WrapGormError(err, "failed to get running pause operation")
	}
	return pauseOperationToEntity(model), nil
}

func (r *pauseOperationRepoImpl) ListExpiredAdmissions(
	ctx context.Context,
	deadline time.Time,
) ([]*entity.ResourcePoolPauseOperation, bizerr.BizError) {
	var models []*schema.ResourcePoolPauseOperation
	if err := r.provider.ReadDB(ctx).
		Where("status = ? AND stage IN ? AND deadline_at <= ?",
			entity.ResourcePoolPauseOperationRunning,
			[]entity.ResourcePoolPauseOperationStage{
				entity.ResourcePoolPauseOperationAdmission,
				entity.ResourcePoolPauseOperationAdmissionWithManualBackup,
			},
			deadline,
		).
		Find(&models).Error; err != nil {
		return nil, bizerr.WrapGormError(err, "failed to list expired pause admissions")
	}
	operations := make([]*entity.ResourcePoolPauseOperation, 0, len(models))
	for _, model := range models {
		operations = append(operations, pauseOperationToEntity(model))
	}
	return operations, nil
}

func pauseOperationToSchema(operation *entity.ResourcePoolPauseOperation) *schema.ResourcePoolPauseOperation {
	return &schema.ResourcePoolPauseOperation{
		ResourcePoolID: operation.ResourcePoolID,
		OperationID:    operation.OperationID,
		OperationType:  string(operation.Type),
		Status:         string(operation.Status),
		Stage:          string(operation.Stage),
		ResultSummary:  operation.ResultSummary,
		CreatedAt:      operation.CreatedAt,
		UpdatedAt:      operation.UpdatedAt,
		DeadlineAt:     operation.DeadlineAt,
		CompletedAt:    operation.CompletedAt,
	}
}

func pauseOperationToEntity(model *schema.ResourcePoolPauseOperation) *entity.ResourcePoolPauseOperation {
	return &entity.ResourcePoolPauseOperation{
		ResourcePoolID: model.ResourcePoolID,
		OperationID:    model.OperationID,
		Type:           entity.ResourcePoolPauseOperationType(model.OperationType),
		Status:         entity.ResourcePoolPauseOperationStatus(model.Status),
		Stage:          entity.ResourcePoolPauseOperationStage(model.Stage),
		ResultSummary:  model.ResultSummary,
		CreatedAt:      model.CreatedAt,
		UpdatedAt:      model.UpdatedAt,
		DeadlineAt:     model.DeadlineAt,
		CompletedAt:    model.CompletedAt,
	}
}
