package task

import (
	"context"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"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/cloud/azure"
	"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/repository"
)

type Context struct {
	Local      client.Client
	Logger     logr.Logger
	Config     config.RootConfig
	Bucket     *v1alpha1.Bucket
	DataPlaneR repository.DataPlaneReader
	AppBoxR    repository.APPBoxReader
	AzureCli   azure.Client

	BeginAt *time.Time
}

var _ deploy.Task = &BaseTask{}

func (c *Context) Default() (*Context, error) {
	return c, nil
}

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(context.Context) deploy.Result {
	return deploy.Done()
}

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

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

func (t *BaseTask) EffectiveCloudTags(ctx context.Context, provider string) (map[string]string, error) {
	var dataPlaneCloudTags map[string]string
	if t.DataPlaneR != nil && t.Bucket != nil && t.Bucket.Spec.DataplaneId != "" {
		dataPlane := &v1alpha1.DataPlane{}
		if err := t.DataPlaneR.Get(ctx, client.ObjectKey{
			Namespace: t.Config.Namespace,
			Name:      t.Bucket.Spec.DataplaneId,
		}, dataPlane); err != nil {
			return nil, fmt.Errorf("get dataplane %s: %w", t.Bucket.Spec.DataplaneId, err)
		}
		dataPlaneCloudTags = dataPlane.Spec.CloudTags
	}
	return controllers.EffectiveCloudTags(
		provider,
		t.Config.AdditionalCloudTags,
		t.Bucket.Labels,
		t.Bucket.Spec.Tags,
		dataPlaneCloudTags,
	), nil
}

func (t *BaseTask) GetSubResourceName() (string, error) {
	return t.getSubResourceName("")
}

func (t *BaseTask) GetSubResourceNameAfterConflict(conflictingSubResourceName string) (string, error) {
	if conflictingSubResourceName == "" {
		return "", fmt.Errorf("conflicting subresource name is empty")
	}
	return t.getSubResourceName(fmt.Sprintf("conflict/%s", conflictingSubResourceName))
}

func (t *BaseTask) GetCurrentOrSubResourceName() (string, error) {
	if t.Bucket != nil && t.Bucket.Status.Bucket != nil && t.Bucket.Status.Bucket.Name != "" {
		return t.Bucket.Status.Bucket.Name, nil
	}
	return t.GetSubResourceName()
}

func (t *BaseTask) getSubResourceName(salt string) (string, error) {
	if t.Bucket == nil {
		return "", fmt.Errorf("bucket is nil")
	}

	source := fmt.Sprintf("%s/%s", t.Bucket.Spec.DataplaneId, t.Bucket.Spec.Id)
	if t.Bucket.Spec.DataplaneId == "" || t.Bucket.Spec.Id == "" {
		source = t.Bucket.Name
	}
	if source == "" {
		return "", fmt.Errorf("bucket identity is empty")
	}
	if salt != "" {
		source = fmt.Sprintf("%s/%s", source, salt)
	}

	hash := sha256.Sum256([]byte(source))
	hashedName := hex.EncodeToString(hash[:])
	hashedName = fmt.Sprintf("%s-%s", t.Config.ResourceNamePrefix, hashedName)
	if len(hashedName) > 63 {
		hashedName = hashedName[:63]
	}
	return hashedName, nil
}
