package resource

import (
	"fmt"
	"time"

	validation "github.com/Thearas/ozzo-validation/v4"
	"github.com/pkg/errors"
)

var (
	DurationValidation = validation.By(func(v interface{}) error {
		v, isNil := validation.Indirect(v)
		if isNil || validation.IsEmpty(v) {
			return nil
		}
		if _, err := time.ParseDuration(v.(string)); err != nil {
			return errors.Wrap(err, v.(string))
		}
		return nil
	})
	DurationLessThanValidation = func(max time.Duration) validation.Rule {
		return validation.By(func(v interface{}) error {
			v, isNil := validation.Indirect(v)
			if isNil || validation.IsEmpty(v) {
				return nil
			}
			d, err := time.ParseDuration(v.(string))
			if err != nil {
				return errors.Wrap(err, v.(string))
			}
			if d > max {
				return fmt.Errorf("duration %s that great than %s is forbidden", v.(string), max.String())
			}
			return nil
		})
	}
)

type ReclaimPolicy struct {
	Lease      string     `json:"lease" default:"24h"`
	GcStrategy GcStrategy `json:"gcStrategy"`
}

func (r ReclaimPolicy) Validate() error {
	return validation.ValidateStruct(&r,
		validation.Field(&r.Lease, validation.Required, DurationValidation),
		validation.Field(&r.GcStrategy))
}

// GcStrategy defines actions when plan is finished
//
// * OnFailure - take actions immediately when plan is failed
//
// * OnSuccess - take actions immediately when plan is successful
//
// * OnCompletion - take actions when plan is completed
type GcStrategy struct {
	OnFailure    *GcAction `json:"onFailure,omitempty"`
	OnSuccess    *GcAction `json:"onSuccess,omitempty"`
	OnCompletion *GcAction `json:"onCompletion,omitempty"`
}

// SetDefaults implements defaults.Setter interface
func (g *GcStrategy) SetDefaults() {
	var defaultEnvReserver = &GcReserveEnvAction{
		DurationOnWorkingHour: "0h",
	}
	if g.OnCompletion == nil {
		g.OnCompletion = &GcAction{
			Artifact: &GcArtifactAction{
				Monitor: true,
				Log:     true,
			},
			ReserveEnv:   defaultEnvReserver,
			Notification: nil,
		}
	}
	if g.OnSuccess != nil && g.OnSuccess.ReserveEnv == nil {
		g.OnSuccess.ReserveEnv = defaultEnvReserver
	}
	if g.OnFailure != nil && g.OnFailure.ReserveEnv == nil {
		g.OnFailure.ReserveEnv = defaultEnvReserver
	}
}

func (g GcStrategy) Validate() error {
	return validation.ValidateStruct(&g,
		validation.Field(&g.OnSuccess),
		validation.Field(&g.OnFailure),
		validation.Field(&g.OnCompletion),
	)
}

// GcAction defines actions when a testbed is completed
type GcAction struct {
	Artifact     *GcArtifactAction     `json:"artifact,omitempty"`
	ReserveEnv   *GcReserveEnvAction   `json:"reserveEnv,omitempty"`
	Notification *GcNotificationAction `json:"notification,omitempty"`
}

func (g GcAction) Validate() error {
	return validation.ValidateStruct(&g,
		validation.Field(&g.Artifact),
		validation.Field(&g.ReserveEnv),
		validation.Field(&g.Notification),
	)
}

// GcArtifactAction defines how to collect artifacts
type GcArtifactAction struct {
	Monitor bool `json:"monitor"`
	Log     bool `json:"log"`
}

func (g GcArtifactAction) Validate() error {
	return nil
}

// GcReserveEnvAction defines how long and how to reserve the testbed
type GcReserveEnvAction struct {
	// retain duration on working hour, if triggers on non-working hour, retain to next working hour
	DurationOnWorkingHour string `json:"durationOnWorkingHour"`
	// scale in to min replicas, to tidbcluster, is 1 tidb, 1 tikv, 1 pd etc
	ScaleIn bool `json:"scaleIn"`
	// scale down to min request, on k8s impl, means removing resource requests(cpu & memory)
	ScaleDown bool `json:"scaleDown"`
}

func (g GcReserveEnvAction) Validate() error {
	return validation.ValidateStruct(&g,
		validation.Field(&g.DurationOnWorkingHour, validation.Required, DurationLessThanValidation(time.Hour*3*24)),
	)
}

// GcNotificationAction defines how to notify listeners
type GcNotificationAction struct {
	Feishu string `json:"feishu,omitempty"`
}

func (g GcNotificationAction) Validate() error {
	return nil
}
