package task

import (
	"context"
	"time"

	"github.com/go-logr/logr"
	"sigs.k8s.io/controller-runtime/pkg/client"

	"github.com/tidbcloud/infra-provider/regional-server/api/v1alpha1"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/config"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/deploy"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/namer"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/repository"
)

type Context struct {
	Local       client.Client
	Logger      logr.Logger
	BeginAt     *time.Time
	Namer       *namer.Namer
	Internet    *v1alpha1.Internet
	VPC         *v1alpha1.VPC
	InternetRW  repository.InternetReaderWriter
	VPCR        repository.VPCReader
	APPBoxR     repository.APPBoxReader
	VPCPeeringR repository.VPCPeeringReader
	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) S3GatewayPrefix() string {
	return c.InternetName()
}

func (c *Context) InternetName() string {
	return controllers.UniqueIdentifier(c.Internet.ObjectMeta, namer.WithPrefix(c.Config.ResourceNamePrefix))
}

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

func (c *Context) VPCName() string {
	if c.VPC.Status.VPC.Name != "" {
		return c.VPC.Status.VPC.Name
	}
	return controllers.UniqueIdentifier(c.VPC.ObjectMeta, namer.WithPrefix(c.Config.ResourceNamePrefix))
}

func (c *Context) IsBYOC() bool {
	return c.Config.BYOC.BYOCPrivateLink.IsEnabled()
}

// GetZones returns baseline AZs from VPC spec.
//
// VPC.Spec.Zones is expected to be persisted and non-empty.
// This method intentionally ignores NodeZones because some internet resources
// should still follow the full baseline zones.
func (c *Context) GetZones() []string {
	return append([]string(nil), c.VPC.Spec.Zones...)
}

// GetNodeZones returns AZs for workload-related internet resources.
//
// Priority:
//  1. VPC.Spec.NodeZones
//  2. VPC.Spec.Zones
func (c *Context) GetNodeZones() []string {
	if len(c.VPC.Spec.NodeZones) > 0 {
		return append([]string(nil), c.VPC.Spec.NodeZones...)
	}
	return append([]string(nil), c.VPC.Spec.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
}
