package helper

import (
	"context"
	"fmt"
	"time"

	"k8s.io/client-go/util/retry"
	"sigs.k8s.io/controller-runtime/pkg/client"

	"github.com/tidbcloud/eks-provider/tests/e2e/data"
	"github.com/tidbcloud/eks-provider/tests/e2e/utils/wait"
	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
)

func Create(ctx context.Context, c client.Client, obj client.Object, ps ...data.Patch) error {
	obj = data.Apply(obj, ps...)
	return c.Create(ctx, obj)
}

func DeleteAndWait(ctx context.Context, c client.Client, obj client.Object, timeout time.Duration) error {
	if err := c.Delete(ctx, obj); err != nil {
		return err
	}

	return wait.WaitForObjectDeleted(ctx, c, obj, timeout)
}

func TryToUpdate(ctx context.Context, c client.Client, obj client.Object, ps ...data.Patch) error {
	if err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
		if err := c.Get(ctx, client.ObjectKeyFromObject(obj), obj); err != nil {
			return err
		}

		obj = data.Apply(obj, ps...)

		if err := c.Update(ctx, obj); err != nil {
			return err
		}

		return nil
	}); err != nil {
		return fmt.Errorf("try to update obj(%v) failed: %w", client.ObjectKeyFromObject(obj), err)
	}

	return nil
}

func TryToUpdateStatus(ctx context.Context, c client.Client, obj client.Object, ps ...data.Patch) error {
	if err := retry.RetryOnConflict(retry.DefaultRetry, func() error {
		if err := c.Get(ctx, client.ObjectKeyFromObject(obj), obj); err != nil {
			return err
		}

		obj = data.Apply(obj, ps...)

		if err := c.Status().Update(ctx, obj); err != nil {
			return err
		}

		return nil
	}); err != nil {
		return fmt.Errorf("try to update obj(%v) failed: %w", client.ObjectKeyFromObject(obj), err)
	}

	return nil
}

type ConnectionType string

const (
	ConnectionTypePublic      = "public"
	ConnectionTypePeering     = "peering"
	ConnectionTypePrivateLink = "private-link"
)

func GetDSN(ep *cloudv1.SqlEndpointsStatus, t ConnectionType) string {
	switch t {
	case ConnectionTypePublic:
		if ep.PublicEndpoint == nil {
			return ""
		}
		return fmt.Sprintf("root@tcp(%s:%d)/",
			ep.PublicEndpoint.Host,
			ep.PublicEndpoint.Port,
		)
	case ConnectionTypePeering:
		if ep.PrivateEndpoint == nil {
			return ""
		}
		return fmt.Sprintf("root@tcp(%s:%d)/",
			ep.PrivateEndpoint.Host,
			ep.PrivateEndpoint.Port,
		)
	case ConnectionTypePrivateLink:
		if ep.PrivateLinkService == nil {
			return ""
		}
		return fmt.Sprintf("root@tcp(%s:%d)/",
			ep.PrivateLinkService.AWS.PrivateDNSName,
			*ep.PrivateLinkService.AWS.Port,
		)
	}
	panic("unreachable")
}
