package validation

import (
	"slices"

	"github.com/tidbcloud/devops/entity/operation"
	"github.com/tidbcloud/devops/pkg/infra/utils/container"
)

var (
	Operation = operationValidator{}
)

type operationValidator struct{}

func (v operationValidator) Instance(i *operation.Instance) error {
	if !slices.Contains(operation.OperationTargetValues, i.Type) {
		return ErrInvalid("type", i.Type)
	}
	if i.Operation == "" {
		return ErrRequired("operation")
	}
	if i.Applicant == "" {
		return ErrRequired("applicant")
	}
	if !slices.Contains(operation.InstancePhaseValues, i.Phase) {
		return ErrInvalid("phase", i.Phase)
	}

	orderSet := container.NewSet[operation.ActionOrder]()
	for _, action := range i.Actions {
		err := v.InstanceAction(action)
		if err != nil {
			return err
		}
		if orderSet.Has(action.Order) {
			return ErrDuplicate("action.order", action.Order)
		}
		orderSet.Insert(action.Order)
	}

	return nil
}

func (v operationValidator) InstanceAction(a *operation.InstanceAction) error {
	if a.Name == "" {
		return ErrRequired("name")
	}
	if !slices.Contains(operation.ActionTypeValues, a.Type) {
		return ErrInvalid("type", a.Type)
	}
	if !slices.Contains(operation.ActionStatusValues, a.Status) {
		return ErrInvalid("status", a.Status)
	}
	if !slices.Contains(operation.ActionStartPolicyValues, a.StartPolicy) {
		return ErrInvalid("start_policy", a.StartPolicy)
	}
	if a.StartPolicy == operation.ActionStartPolicySchedule && a.StartScheduleAt == nil {
		return ErrRequired("start_schedule_at")
	}

	return nil
}

func (v operationValidator) Operation(o *operation.Operation) error {
	if o.Name == "" {
		return ErrRequired("name")
	}
	if o.Alias == "" {
		return ErrRequired("alias")
	}
	if !slices.Contains(operation.OperationTargetValues, o.Target) {
		return ErrInvalid("target", o.Target)
	}
	if len(o.Params) != 0 {
		for _, param := range o.Params {
			err := v.ParamDefine(param)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func (v operationValidator) ParamDefine(o operation.ParamDefine) error {
	if !slices.Contains(operation.ParamTypeValues, o.Type) {
		return ErrInvalid("type", o.Type)
	}
	return nil
}
