package model

import (
	"fmt"
	"regexp"
	"strings"
	"time"

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

const (
	// Trigger name format is the same as plan's.
	triggerNameFmt       string = planNameFmt
	triggerNameErrMsg    string = "trigger name must consist of lower case alphanumeric characters, '@', '_', '-' or '.', and must start and end with an alphanumeric character"
	triggerNameMaxLength int    = planNameMaxLength

	// The prefix of trigger name for GITHUB plan
	TriggerNamePrefixGitHubPlan = "gh."
	// The prefix of trigger name for ONE_SHOT plan
	TriggerNamePrefixOneShotPlan = "oneshot."
	// The prefix of trigger name for release trigger
	TriggerNamePrefixRelease = "release-"
)

var (
	triggerNameRegex          = regexp.MustCompile("^" + triggerNameFmt + "$")
	TriggerNameValidationRule = validation.By(func(v interface{}) error {
		v, isNil := validation.Indirect(v)
		if isNil || validation.IsEmpty(v) {
			return nil
		}
		s := v.(string)

		invalidNamePrefix := []string{
			TriggerNamePrefixOneShotPlan,
			TriggerNamePrefixGitHubPlan,
		}
		for _, prefix := range invalidNamePrefix {
			if strings.HasPrefix(s, prefix) {
				return fmt.Errorf("trigger name should not start with %v", invalidNamePrefix)
			}
		}

		if err := validation.Length(1, triggerNameMaxLength).Validate(s); err != nil {
			return err
		}
		return validation.Match(triggerNameRegex).Error(triggerNameErrMsg).Validate(s)
	})
)

type TestTrigger struct {
	ID        ID     `json:"id" db:"id"`
	Name      string `json:"name,omitempty" db:"name"`
	Summary   string `json:"summary" db:"summary"`
	CreatedBy `json:"createdBy"`
	// Used in Release Test
	ReleaseBranch string       `json:"releaseBranch,omitempty" db:"release_branch"`
	ExecPriority  ExecPriority `json:"priority" db:"priority"`
	Type          TriggerType  `json:"type" db:"type"`
	Expr          string       `json:"expr" db:"expr"`
	// Ts is the expected time of trigger.
	Ts              int64     `json:"-" db:"ts"`
	Active          bool      `json:"active" db:"active"`
	LastTriggeredAt *int64    `json:"lastTriggeredAt,omitempty" db:"last_triggered_at"`
	CreatedAt       time.Time `json:"createdAt" db:"created_at"`
	UpdatedAt       time.Time `json:"updatedAt" db:"updated_at"`
	PlanRef
}

type TriggerType string

const (
	TriggerTypeOnce TriggerType = "ONCE"
	TriggerTypeCron TriggerType = "CRON"

	// TriggerTypeRelease is a special ONCE trigger that can trigger multiple plans.
	// It's used in release test.
	TriggerTypeRelease TriggerType = "RELEASE"
)

func (tt TriggerType) IsValid() bool {
	return tt.Validate() == nil
}

func (v TriggerType) Validate() error {
	values := []TriggerType{
		TriggerTypeOnce,
		TriggerTypeCron,
		TriggerTypeRelease,
	}

	for _, value := range values {
		if value == v {
			return nil
		}
	}
	return fmt.Errorf("trigger-type should be one of %v", values)
}
