package k8s

import (
	"context"

	"github.com/tidbcloud/infra-provider/regional-server/pkg/consts"
	"sigs.k8s.io/controller-runtime/pkg/client"
)

// Resource is a type constraint for Kubernetes resources that can be read and written.
// It requires the resource type R to be a client.Object.
// It also requires the associated list type L to be a client.ObjectList.
type Resource[R client.Object, L client.ObjectList] interface {
	client.Object  // R must satisfy client.Object
	IsList(L) bool // A dummy method to enforce the list type L as well
}

// --- Generic Interfaces ---

// GenericReaderWriter defines the combined reading and writing operations for any resource.
type GenericReaderWriter[R client.Object, L client.ObjectList] interface {
	GenericReader[R, L]
	GenericWriter[R]
}

// GenericWriter defines the writing operations for any resource R.
type GenericWriter[R client.Object] interface {
	Create(ctx context.Context, obj R) error
	Update(ctx context.Context, obj R) error
	Delete(ctx context.Context, obj R) error
	Patch(ctx context.Context, obj R, patch client.Patch) error
	UpdateStatus(ctx context.Context, obj R) error
}

// GenericReader defines the reading operations for any resource R and its list L.
type GenericReader[R client.Object, L client.ObjectList] interface {
	Get(ctx context.Context, key client.ObjectKey, obj R) error
	List(ctx context.Context, list L, opts ...client.ListOption) error
}

func NewGenericReaderWriter[R client.Object, L client.ObjectList](cli client.Client) GenericReaderWriter[R, L] {
	return &genericReaderWriter[R, L]{
		reader: NewGenericReader[R, L](cli),
		writer: NewGenericWriter[R](cli),
	}
}

func NewGenericReader[R client.Object, L client.ObjectList](cli client.Client) GenericReader[R, L] {
	return &genericReader[R, L]{
		readCli: cli,
	}
}

func NewGenericWriter[R client.Object](cli client.Client) GenericWriter[R] {
	return &genericWriter[R]{
		cli: cli,
	}
}

type genericReaderWriter[R client.Object, L client.ObjectList] struct {
	reader GenericReader[R, L]
	writer GenericWriter[R]
}

func (r *genericReaderWriter[R, L]) Get(ctx context.Context, key client.ObjectKey, obj R) error {
	return r.reader.Get(ctx, key, obj)
}
func (r *genericReaderWriter[R, L]) List(ctx context.Context, list L, opts ...client.ListOption) error {
	return r.reader.List(ctx, list, opts...)
}
func (w *genericReaderWriter[R, L]) Create(ctx context.Context, obj R) error {
	return w.writer.Create(ctx, obj)
}
func (w *genericReaderWriter[R, L]) Update(ctx context.Context, obj R) error {
	return w.writer.Update(ctx, obj)
}
func (w *genericReaderWriter[R, L]) Delete(ctx context.Context, obj R) error {
	if err := w.Get(ctx, client.ObjectKeyFromObject(obj), obj); err != nil {
		return err
	}
	deletionPatch := SetAnnotationPatch(consts.AnnotationKeyConfirmDeletion, "true")
	patch, err := ConstructPatch(deletionPatch(obj))
	if err != nil {
		return err
	}
	if err := w.Patch(ctx, obj, patch); err != nil {
		return err
	}
	return w.writer.Delete(ctx, obj)
}

func (w *genericReaderWriter[R, L]) Patch(ctx context.Context, obj R, patch client.Patch) error {
	return w.writer.Patch(ctx, obj, patch)
}

func (w *genericReaderWriter[R, L]) UpdateStatus(ctx context.Context, obj R) error {
	return w.writer.UpdateStatus(ctx, obj)
}

type genericWriter[R client.Object] struct {
	cli client.Client
}

func (w *genericWriter[R]) Create(ctx context.Context, obj R) error {
	return w.cli.Create(ctx, obj)
}

func (w *genericWriter[R]) Update(ctx context.Context, obj R) error {
	return w.cli.Update(ctx, obj)
}

func (w *genericWriter[R]) Delete(ctx context.Context, obj R) error {
	return w.cli.Delete(ctx, obj)
}

func (w *genericWriter[R]) Patch(ctx context.Context, obj R, patch client.Patch) error {
	return w.cli.Patch(ctx, obj, patch)
}

func (w *genericWriter[R]) UpdateStatus(ctx context.Context, obj R) error {
	return w.cli.Status().Update(ctx, obj)
}

type genericReader[R client.Object, L client.ObjectList] struct {
	readCli client.Reader
}

func (r *genericReader[R, L]) Get(ctx context.Context, key client.ObjectKey, obj R) error {
	return r.readCli.Get(ctx, key, obj)
}

func (r *genericReader[R, L]) List(ctx context.Context, list L, opts ...client.ListOption) error {
	return r.readCli.List(ctx, list, opts...)
}
