package task

import (
	"context"
	"fmt"
	"strings"
	"time"

	"github.com/go-logr/logr"
	"github.com/tidbcloud/infra-provider/pkg/deploy"
	"github.com/tidbcloud/infra-provider/pkg/namer"
	"github.com/tidbcloud/infra-provider/regional-server/api/v1alpha1"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/common/utils"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/config"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/resourcemanager/appbox"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/resourcemanager/internet"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/resourcemanager/vpc"
	"sigs.k8s.io/controller-runtime/pkg/client"
)

// NetworkNameLimit is the limit of network name.
// At present, subnetwork name is `{networkName}-nodes`, so we need to limit the length of network name.
const NetworkNameLimit = 57

type Context struct {
	Local      client.Client
	Logger     logr.Logger
	VPC        *v1alpha1.VPC
	BeginAt    *time.Time
	Namer      *namer.Namer
	VPCRW      vpc.VPCReaderWriter
	APPBoxR    appbox.APPBoxReader
	InternetRW internet.InternetReaderWriter

	Config config.RootConfig
}

var _ deploy.Task = &BaseTask{}

func (c *Context) Default() (*Context, error) {
	// Use vpc name and namespace as the network name and namespace
	// to ensure compatibility
	c.Namer = namer.NewNamer().
		LegacyWithNetworkName(c.VPC.Name).
		LegacyWithNetworkNamespace(c.VPC.Namespace)
	return c, nil
}

func (c *Context) ProviderConfigName() string {
	if c.VPC.Spec.AccountRef != nil {
		return c.VPC.Spec.AccountRef.ProviderConfigName
	}
	return ""
}

func (c *Context) GetVPCName() string {
	if name := c.VPC.Annotations[v1alpha1.AnnotationKeyOwnCrossplaneNameVPC]; name != "" {
		return name
	}
	return namer.UniqueIdentifier(c.VPC.ObjectMeta)
}

// GetSecondaryCidrName creates a Kubernetes resource name for a secondary CIDR block.
// It combines the VPC name with a sanitized version of the CIDR block.
// The CIDR block is sanitized by replacing dots and slashes with hyphens.
// Returns an error if the CIDR block is empty or invalid.
func (c *Context) GetSecondaryCidrName(cidr string) (string, error) {
	if cidr == "" {
		return "", fmt.Errorf("CIDR block cannot be empty")
	}

	vpcName := c.GetVPCName()
	if vpcName == "" {
		return "", fmt.Errorf("VPC name cannot be empty")
	}

	sanitizedCidr := utils.SanitizeCidr(cidr)

	// Additional validation: ensure the sanitized CIDR contains only valid characters
	if strings.TrimSpace(sanitizedCidr) == "" {
		return "", fmt.Errorf("CIDR block '%s' contains no valid characters after sanitization", cidr)
	}

	return fmt.Sprintf("%s-%s", vpcName, sanitizedCidr), nil
}

func (c *Context) GetNetworkName() string {
	if name := c.VPC.Annotations[v1alpha1.AnnotationKeyOwnCrossplaneNameNetwork]; name != "" {
		return name
	}
	return namer.UniqueIdentifier(c.VPC.ObjectMeta, namer.WithLimit(NetworkNameLimit))
}

func (c *Context) GetGKESubnetName() string {
	return fmt.Sprintf("%s-nodes", c.GetNetworkName())
}

func (c *Context) IsHibernated() bool {
	return c.VPC.Annotations[v1alpha1.AnnotationKeyIsHiberated] == "true"
}

func (c *Context) GetZones() []string {
	if len(c.VPC.Spec.Zones) > 0 {
		return c.VPC.Spec.Zones
	}
	return c.Config.RegionConfig.Zones
}

type BaseTask struct {
	*Context
}

func (t *BaseTask) Name() string {
	return ""
}

func (t *BaseTask) Category() deploy.TaskCategory {
	return deploy.ClusterTask
}

func (t *BaseTask) Create(ctx context.Context) deploy.Result {
	return deploy.Done()
}

func (t *BaseTask) PostCreate(ctx context.Context) deploy.Result {
	return deploy.Done()
}

func (t *BaseTask) Delete(ctx context.Context) deploy.Result {
	return deploy.Done()
}

func (t *BaseTask) PostDelete(ctx context.Context) deploy.Result {
	return deploy.Done()
}

func (t *BaseTask) MetricsStartTime() *time.Time {
	return t.BeginAt
}
