package resourcemanager

import (
	"context"

	helmv2 "github.com/fluxcd/helm-controller/api/v2"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"sigs.k8s.io/controller-runtime/pkg/client"

	commonerrors "github.com/tidbcloud/observability/pkg/common/errors"
	"github.com/tidbcloud/observability/pkg/common/message"
)

type helmReleaseReaderWriter struct {
	cli     client.Client
	readCli client.Reader
}

// Create creates a new HelmRelease CR
func (h helmReleaseReaderWriter) Create(ctx context.Context, helmRelease *helmv2.HelmRelease) error {
	err := h.cli.Create(ctx, helmRelease)
	return WrapClientError(err)
}

// Get retrieves a specific HelmRelease CR by namespace and name
func (h helmReleaseReaderWriter) Get(ctx context.Context, namespace, name string) (*helmv2.HelmRelease, error) {
	helmRelease := &helmv2.HelmRelease{}
	err := h.cli.Get(ctx, client.ObjectKey{
		Namespace: namespace,
		Name:      name,
	}, helmRelease)

	if err != nil {
		if apierrors.IsNotFound(err) {
			return nil, commonerrors.NewErrorf(commonerrors.HelmReleaseNotFound, "HelmRelease %s/%s is not found", namespace, name)
		}
		return nil, WrapClientError(err)
	}

	return helmRelease, nil
}

// List retrieves HelmRelease CRs in a specific namespace with pagination
func (h helmReleaseReaderWriter) List(ctx context.Context, namespace string, cr message.ContinueRequest) (*helmv2.HelmReleaseList, error) {
	helmReleases := &helmv2.HelmReleaseList{}

	opts := []client.ListOption{
		client.InNamespace(namespace),
	}

	// Add pagination options if provided
	if cr.Limit > 0 {
		opts = append(opts, client.Limit(cr.Limit))
	}
	if cr.ContinueToken != "" {
		opts = append(opts, client.Continue(cr.ContinueToken))
	}

	err := h.readCli.List(ctx, helmReleases, opts...)
	if err != nil {
		return nil, WrapClientError(err)
	}

	return helmReleases, nil
}

// ListAll retrieves all HelmRelease CRs across all namespaces with pagination
func (h helmReleaseReaderWriter) ListAll(ctx context.Context, cr message.ContinueRequest) (*helmv2.HelmReleaseList, error) {
	helmReleases := &helmv2.HelmReleaseList{}

	opts := []client.ListOption{}

	// Add pagination options if provided
	if cr.Limit > 0 {
		opts = append(opts, client.Limit(cr.Limit))
	}
	if cr.ContinueToken != "" {
		opts = append(opts, client.Continue(cr.ContinueToken))
	}

	err := h.readCli.List(ctx, helmReleases, opts...)
	if err != nil {
		return nil, WrapClientError(err)
	}

	return helmReleases, nil
}

// Update updates an existing HelmRelease CR
func (h helmReleaseReaderWriter) Update(ctx context.Context, helmRelease *helmv2.HelmRelease) error {
	err := h.cli.Update(ctx, helmRelease)
	return WrapClientError(err)
}

// Delete removes a HelmRelease CR by namespace and name
func (h helmReleaseReaderWriter) Delete(ctx context.Context, namespace, name string) error {
	helmRelease := &helmv2.HelmRelease{
		ObjectMeta: metav1.ObjectMeta{
			Name:      name,
			Namespace: namespace,
		},
	}

	err := h.cli.Delete(ctx, helmRelease)
	if apierrors.IsNotFound(err) {
		return commonerrors.NewErrorf(commonerrors.HelmReleaseNotFound, "HelmRelease %s/%s is not found", namespace, name)
	}
	return WrapClientError(err)
}

// IsHelmReleaseExists checks if a HelmRelease CR exists
func (h helmReleaseReaderWriter) IsHelmReleaseExists(ctx context.Context, namespace, name string) (bool, error) {
	helmRelease := &helmv2.HelmRelease{}
	err := h.cli.Get(ctx, client.ObjectKey{
		Namespace: namespace,
		Name:      name,
	}, helmRelease)

	if err != nil {
		if client.IgnoreNotFound(err) == nil {
			return false, nil
		}
		return false, WrapClientError(err)
	}

	return true, nil
}
