package gardener

import (
	"context"
	"fmt"

	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
	"sigs.k8s.io/controller-runtime/pkg/manager"

	gardencorev1beta1 "github.com/tidbcloud/infra-api/pkg/gardener/core/apis/v1beta1"
	"github.com/tidbcloud/infra-api/pkg/nodegroup"
)

type NodeGroupPolicy string

const (
	// NodeGroupPolicyRollingUpdate will rolling-update the node group if needed, e.g. the node image is updated
	NodeGroupPolicyRollingUpdate NodeGroupPolicy = "RollingUpdate"
	// NodeGroupPolicyScaleOnly will only scale-out/in existing node groups
	NodeGroupPolicyScaleOnly NodeGroupPolicy = "ScaleOnly"
	// NodeGroupPolicyDryrun will not update node groups and only generate changes
	NodeGroupPolicyDryrun NodeGroupPolicy = "Dryrun"
)

type NodeGroupOperation string

const (
	NodeGroupOpDelete = "Delete"
	NodeGroupOpUpdate = "Update"
	NodeGroupOpCreate = "Create"
)

type GardenerAPI interface {
	Manager() manager.Manager
	ProjectName() string
	GetShootNamespace() string
	GetShootContext(ctx context.Context, name string) (sctx *ShootContext, err error)
	GetShootContextWithNamespace(ctx context.Context, name string, namespace string) (sctx *ShootContext, err error)
	ShootClusterIsExist(ctx context.Context, name string) (bool, error)
	ApplyNodeGroupSet(ctx context.Context, shootName string, ngSet *nodegroup.Set, policy NodeGroupPolicy) ([]NodeGroupChange, error)
	CreateOrUpdateShoot(ctx context.Context, s *ShootArgs) (controllerutil.OperationResult, error)
	UpdateShootFirewall(ctx context.Context, shootName string, cidrs []string) error
	GetShoot(ctx context.Context, shootName string) (*gardencorev1beta1.Shoot, error)
	DeleteShoot(ctx context.Context, name string) (bool, error)
	ListShoots(ctx context.Context) (*gardencorev1beta1.ShootList, error)
	LoadShootClientsIntoCache(ctx context.Context) error
	// CancelShootContext will cancel list-watch for shoot cluster
	CancelShootContext(name string)
	AwakeShoot(ctx context.Context, name string) error
	HibernateShoot(ctx context.Context, name string) error
	Started() bool
}

type NodeGroupChange struct {
	Operation NodeGroupOperation
	Before    *gardencorev1beta1.Worker
	After     *gardencorev1beta1.Worker
}

func (n *NodeGroupChange) String() string {
	switch n.Operation {
	case NodeGroupOpCreate:
		return fmt.Sprintf("create node group %s, machine: %s, replicas: %d", n.After.Name, n.After.Machine.Type, n.After.Minimum)
	case NodeGroupOpDelete:
		return fmt.Sprintf("delete node group %s, machine: %s, replicas: %d", n.Before.Name, n.Before.Machine.Type, n.Before.Minimum)
	case NodeGroupOpUpdate:
		return fmt.Sprintf("update node group %s, machine: %s => %s, replicas: %d => %d",
			n.After.Name, n.Before.Machine.Type, n.After.Machine.Type, n.Before.Minimum, n.After.Minimum)
	default:
		return fmt.Sprintf("unknown operation %s", n.Operation)
	}
}

type ShootArgs struct {
	Name           string
	ProviderType   string
	Region         string
	Zones          []string
	Labels         map[string]string
	Cidr           string
	WorkerCidr     []string
	PublicLBCidr   []string
	InternalLBCidr []string
}
