package repo

import (
	"context"
	"fmt"

	cp "github.com/tidbcloud/devops/entity/controlplane"
	depmod "github.com/tidbcloud/devops/models/controlplane"
	"github.com/tidbcloud/devops/pkg/service/controlplane/repo/cvt"

	"github.com/tidbcloud/pkgv2/db"
)

type DeploymentDriver interface {
	Load(ctx context.Context, id uint64) (*cp.Deployment, error)
	LoadByApprovalID(ctx context.Context, approvalID uint64) (*cp.Deployment, error)
	Save(ctx context.Context, dep *cp.Deployment) (*cp.Deployment, error)
	Query(ctx context.Context, scenario *cp.Scenario, phase *cp.Phase) ([]*cp.Deployment, error)
}

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

func NewDeploymentDriver(dbProvider db.Provider) DeploymentDriver {
	return &deploymentDriver{
		cvter:      cvt.NewConverter(),
		dbProvider: dbProvider,
	}
}

func (r *deploymentDriver) Load(ctx context.Context, id uint64) (*cp.Deployment, error) {
	rdb := r.dbProvider.ReadDB(ctx)
	m := &depmod.Deployment{}

	err := rdb.Where("id = ?", id).First(m).Error
	if err != nil {
		return nil, fmt.Errorf("load deployment %d failed: %w", id, err)
	}

	return r.cvter.ModelToEntity.Deployment(m)
}

func (r *deploymentDriver) LoadByApprovalID(ctx context.Context, approvalID uint64) (*cp.Deployment, error) {
	rdb := r.dbProvider.ReadDB(ctx)
	m := &depmod.Deployment{}

	err := rdb.Where("approval_id = ?", approvalID).First(m).Error
	if err != nil {
		return nil, fmt.Errorf("load deployment by approval_id %d failed: %w", approvalID, err)
	}

	return r.cvter.ModelToEntity.Deployment(m)
}

func (r *deploymentDriver) Save(ctx context.Context, dep *cp.Deployment) (*cp.Deployment, error) {
	wdb := r.dbProvider.WriteDB(ctx)
	m, err := r.cvter.EntityToModel.Deployment(dep)
	if err != nil {
		return nil, fmt.Errorf("convert deployment %d to model failed: %w", dep.ID, err)
	}

	if err := wdb.Save(m).Error; err != nil {
		return nil, fmt.Errorf("save deployment %d failed: %w", dep.ID, err)
	}

	return r.cvter.ModelToEntity.Deployment(m)
}

func (r *deploymentDriver) Query(ctx context.Context, scenario *cp.Scenario, phase *cp.Phase) ([]*cp.Deployment, error) {
	rdb := r.dbProvider.ReadDB(ctx)
	var models []*depmod.Deployment

	if scenario != nil {
		rdb = rdb.Where("scenario = ?", string(*scenario))
	}
	if phase != nil {
		rdb = rdb.Where("phase = ?", string(*phase))
	}

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

	result := make([]*cp.Deployment, 0, len(models))
	for _, m := range models {
		e, err := r.cvter.ModelToEntity.Deployment(m)
		if err != nil {
			return nil, fmt.Errorf("convert deployment %d from model failed: %w", m.ID, err)
		}
		result = append(result, e)
	}
	return result, nil
}
