package env

import (
	"context"
	"fmt"

	ec2v1beta1 "github.com/crossplane-contrib/provider-aws/apis/ec2/v1beta1"
	xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1"
	corev1 "k8s.io/api/core/v1"

	"github.com/tidbcloud/eks-provider/tests/e2e/dag"
	"github.com/tidbcloud/eks-provider/tests/e2e/data"
	"github.com/tidbcloud/eks-provider/tests/e2e/label"
	"github.com/tidbcloud/eks-provider/tests/e2e/suite"
	"github.com/tidbcloud/eks-provider/tests/e2e/utils/wait"
)

func init() {
	suite.AddEnv(
		label.New(NewAWSEndUserEnv(), "setup end user env", label.FeatEndUser),
	)
}

// This env creates a lambda function in another aws VPC, we can invoke this function to
// connect tidb cluster's private endpoint.
type AWSEndUserEnv struct {
	Config *Config

	EndUserVPC         *ec2v1beta1.VPC
	AWSEndUserLambdaFn string
}

func NewAWSEndUserEnv() dag.Env {
	return &AWSEndUserEnv{}
}

func (e *AWSEndUserEnv) Name() string {
	return WithCloud("EndUser", CloudAWS)
}

func (e *AWSEndUserEnv) Setup(ctx context.Context) error {
	// TODO: change ident
	//nolint: goconst
	ident := "test"
	vpc := data.NewEndUserVPC(ident, e.Config.Region)
	if err := e.Config.InfraAPIClient.Create(ctx, vpc); err != nil {
		return err
	}
	for i := 0; i < 3; i++ {
		subnet := data.NewEndUserSubnet(ident, e.Config.Region, i)
		if err := e.Config.InfraAPIClient.Create(ctx, subnet); err != nil {
			return err
		}
	}

	sg := data.NewEndUserSecurityGroup(ident)
	if err := e.Config.InfraAPIClient.Create(ctx, sg); err != nil {
		return err
	}

	lambda := data.NewEndUserLambda(ident, e.Config.Region)
	if err := e.Config.InfraAPIClient.Create(ctx, lambda); err != nil {
		return err
	}

	if err := wait.WaitForCondition(ctx, e.Config.InfraAPIClient, vpc, func() error {
		if vpc.Status.AtProvider.VPCID == "" {
			return fmt.Errorf("vpc id is not set")
		}
		if vpc.Status.AtProvider.OwnerID == "" {
			return fmt.Errorf("owner id is not set")
		}
		return nil

	}, wait.ShortTaskTimeout); err != nil {
		return fmt.Errorf("wait for end user's vpc ready: %w", err)
	}

	if err := wait.WaitForCondition(ctx, e.Config.InfraAPIClient, lambda, func() error {
		const trueCondCount = 2
		count := 0
		for _, c := range lambda.Status.Conditions {
			switch c.Type {
			case xpv1.TypeReady:
				if c.Status != corev1.ConditionTrue {
					return fmt.Errorf("lamba function is not ready")
				}
				count++
			case xpv1.TypeSynced:
				if c.Status != corev1.ConditionTrue {
					return fmt.Errorf("lamba function is not synced")
				}
				count++
			default:
			}
		}

		if trueCondCount != count {
			return fmt.Errorf("lambda function is not set or not synced")
		}
		return nil
	}, wait.LongTaskTimeout); err != nil {
		return err
	}

	e.AWSEndUserLambdaFn = lambda.Name

	return nil
}

func (e *AWSEndUserEnv) Envs() []string {
	return []string{
		WithCloud("Config", CloudAWS),
	}
}
