package resource

import (
	"errors"
	"fmt"
	"regexp"

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

var (
	testbedItemNameRegexp    = regexp.MustCompile("^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$")
	TestbedItemNameValidator = validation.By(func(name interface{}) error {
		value, isNil := validation.Indirect(name)
		if isNil || validation.IsEmpty(value) {
			return nil
		}

		nameStr, ok := name.(string)
		if !ok {
			return errors.New("should be string")
		}
		if len(nameStr) == 0 {
			return errors.New(`should not be empty`)
		}
		if len(nameStr) > 128 {
			return errors.New(`should be less than 128 characters`)
		}
		if !testbedItemNameRegexp.MatchString(nameStr) {
			return fmt.Errorf(`invalid value: "%s", a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. go-tpc-workload, gotpcc)`, nameStr)
		}
		return nil
	})
)

// ResourceType is the string literal of one kind of test resource
type ResourceType string

const (
	ResourceTypeTestbed          = ResourceType("TEST_BED")
	ResourceTypeTiDBCluster      = ResourceType("TIDB_CLUSTER")
	ResourceTypeWorkloadNode     = ResourceType("WORKLOAD_NODE")
	ResourceTypeTiDBLightning    = ResourceType("TIDB_LIGHTNING")
	ResourceTypeDrainer          = ResourceType("TIDB_DRAINER")
	ResourceTypeMySQLNode        = ResourceType("MYSQL_NODE")
	ResourceTypeDMCluster        = ResourceType("DM_CLUSTER")
	ResourceTypeBRNode           = ResourceType("BR_NODE")
	ResourceTypeKafkaCluster     = ResourceType("KAFKA_CLUSTER")
	ResourceTypeLoadBalancer     = ResourceType("LOADBALANCER")
	ResourceTypeTiDBCloudCluster = ResourceType("TIDBCLOUD_CLUSTER")
)

func (v ResourceType) Validate() error {
	values := []ResourceType{
		ResourceTypeTestbed,
		ResourceTypeTiDBCluster,
		ResourceTypeWorkloadNode,
		ResourceTypeTiDBLightning,
		ResourceTypeDrainer,
		ResourceTypeMySQLNode,
		ResourceTypeDMCluster,
		ResourceTypeBRNode,
		ResourceTypeKafkaCluster,
		ResourceTypeLoadBalancer,
		ResourceTypeTiDBCloudCluster,
	}
	for _, value := range values {
		if value == v {
			return nil
		}
	}
	return fmt.Errorf("invalid value: %s, resource type should be one of %v", v, values)
}

// ResourceState is the string literal of a test resource;
type ResourceState string

// ResourceHandle is the identifier of a test resource
type ResourceHandle struct {
	// Provider indicates the name of provider who creates the resource.
	Provider string `json:"provider,omitempty"`
	// Name is the name of the test resource
	Name string `json:"name,omitempty" validate:"max=255"`
	// Testbed is the name of test resource's testbed and it's required unless the Type value equals to TEST_BED
	Testbed string `json:"testbed,omitempty" validate:"required_unless=Type TEST_BED"`
	// Type is the resource type
	Type ResourceType `json:"type" validate:"required"`
	// Ready identifies whether the test resource is in the ready state, it's generated on the RMS server side
	Ready *bool `json:"ready,omitempty"`
}

func (h ResourceHandle) Validate() error {
	return validation.ValidateStruct(&h,
		validation.Field(&h.Name, validation.Required, TestbedItemNameValidator),
		validation.Field(&h.Type),
	)
}

type Item struct {
	ResourceHandle `json:",inline"`
	Details        Details `json:"details,omitempty"`
}

type Details struct {
	// TODO(@codingpoet): add more fields here
	Endpoints ResourceEndpoints `json:"endpoints,omitempty"`
}

type EndpointType string

const (
	EndpointTypeGrafana    EndpointType = "GRAFANA"
	EndpointTypePrometheus EndpointType = "PROMETHEUS"
	EndpointTypeDsn        EndpointType = "DSN"
	EndpointTypeOther      EndpointType = "OTHER"
)

type Endpoint struct {
	Type               EndpointType `json:"type"`
	Name               string       `json:"name"`
	ExternalSvcAddress string       `json:"externalSvcAddress"`
}

type ResourceEndpoints []Endpoint
