// Copyright 2024 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package timanager

import (
	"context"
	"sync"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"

	"github.com/pingcap/tidb-operator/pkg/client"
)

type Object[T any] interface {
	*T
	client.Object
	DeepCopy() *T
	DeepCopyInto(*T)
}

// NOTE: it's only for test now
type List[T any, PT Object[T]] struct {
	metav1.TypeMeta
	metav1.ListMeta

	Items []T
}

// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *List[T, PT]) DeepCopyObject() runtime.Object {
	if c := in.DeepCopy(); c != nil {
		return c
	}
	return nil
}

// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *List[T, PT]) DeepCopyInto(out *List[T, PT]) {
	*out = *in
	out.TypeMeta = in.TypeMeta
	in.ListMeta.DeepCopyInto(&out.ListMeta)
	if in.Items != nil {
		in, out := &in.Items, &out.Items
		*out = make([]T, len(*in))
		for i := range *in {
			var obj PT = &(*in)[i]
			obj.DeepCopyInto(&(*out)[i])
		}
	}
}

// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterList.
func (in *List[T, PT]) DeepCopy() *List[T, PT] {
	if in == nil {
		return nil
	}
	out := new(List[T, PT])
	in.DeepCopyInto(out)
	return out
}

// Map is a wrapper of sync.Map to avoid type assertion in the outer function
type Map[K comparable, V any] struct {
	sync.Map
}

func (m *Map[K, V]) Load(k K) (_ V, _ bool) {
	val, ok := m.Map.Load(k)
	if !ok {
		return
	}
	return val.(V), true
}

func (m *Map[K, V]) Store(k K, v V) {
	m.Map.Store(k, v)
}

func (m *Map[K, V]) Delete(k K) {
	m.Map.Delete(k)
}

func (m *Map[K, V]) Range(f func(K, V) bool) {
	m.Map.Range(func(key, val any) bool {
		k := key.(K)
		v := val.(V)
		return f(k, v)
	})
}

func (m *Map[K, V]) LoadAndDelete(k K) (_ V, _ bool) {
	val, ok := m.Map.LoadAndDelete(k)
	if !ok {
		return
	}
	return val.(V), true
}

func (m *Map[K, V]) Swap(k K, v V) (_ V, _ bool) {
	val, ok := m.Map.Swap(k, v)
	if !ok {
		return
	}
	return val.(V), true
}

type Cache[Client, UnderlayClient any] interface {
	Client() Client
	InformerFactory() SharedInformerFactory[UnderlayClient]

	Keys() []string

	Start(ctx context.Context)
	Stop()
}

type cached[Client, UnderlayClient any] struct {
	c Client
	f SharedInformerFactory[UnderlayClient]

	cancel context.CancelFunc

	cacheKeys []string
}

func NewCache[Client, UnderlayClient any](keys []string, c Client, f SharedInformerFactory[UnderlayClient]) Cache[Client, UnderlayClient] {
	return &cached[Client, UnderlayClient]{
		c:         c,
		f:         f,
		cacheKeys: keys,
	}
}

func (c *cached[Client, UnderlayClient]) Client() Client {
	return c.c
}

func (c *cached[Client, UnderlayClient]) InformerFactory() SharedInformerFactory[UnderlayClient] {
	return c.f
}

func (c *cached[Client, UnderlayClient]) Keys() []string {
	return c.cacheKeys
}

func (c *cached[Client, UnderlayClient]) Start(ctx context.Context) {
	nctx, cancel := context.WithCancel(ctx)
	c.cancel = cancel
	c.f.Start(nctx.Done())
}

func (c *cached[Client, UnderlayClient]) Stop() {
	if c.cancel != nil {
		c.cancel()
	}
	c.f.Shutdown()
}
