package api

import (
	"net/http"

	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/client-go/dynamic"
	"k8s.io/client-go/kubernetes"

	authlib "github.com/grafana/authlib/types"
	collectionsV1 "github.com/grafana/grafana/apps/collections/pkg/apis/collections/v1alpha1"
	dashboardsV1 "github.com/grafana/grafana/apps/dashboard/pkg/apis/dashboard/v1beta1"
	"github.com/grafana/grafana/pkg/api/response"
	"github.com/grafana/grafana/pkg/apimachinery/identity"
	"github.com/grafana/grafana/pkg/apimachinery/utils"
	"github.com/grafana/grafana/pkg/services/apiserver"
	contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
)

//go:generate mockery --name K8sClients --structname MockK8sClients --inpackage --filename client_mock.go --with-expecter
type K8sClients interface {
	GetDashboardID(c *contextmodel.ReqContext, uid string) (int64, response.Response)
	GetStars(c *contextmodel.ReqContext) ([]string, error)
	AddStar(c *contextmodel.ReqContext, uid string) error
	RemoveStar(c *contextmodel.ReqContext, uid string) error
}

type k8sClients struct {
	namespacer     authlib.NamespaceFormatter
	configProvider apiserver.DirectRestConfigProvider
}

var (
	_ K8sClients = (*k8sClients)(nil)
)

// GetDashboardID implements the K8sClients interface.
func (k *k8sClients) GetDashboardID(c *contextmodel.ReqContext, uid string) (int64, response.Response) {
	dyn, err := dynamic.NewForConfig(k.configProvider.GetDirectRestConfig(c))
	if err != nil {
		return 0, response.Error(http.StatusInternalServerError, "client config", err)
	}
	client := dyn.Resource(dashboardsV1.GroupVersion.WithResource(dashboardsV1.DASHBOARD_RESOURCE)).Namespace(k.namespacer(c.OrgID))
	obj, err := client.Get(c.Req.Context(), uid, v1.GetOptions{})
	if err != nil {
		return 0, response.Error(http.StatusNotFound, "Dashboard not found", err)
	}
	dash, err := utils.MetaAccessor(obj)
	if err != nil {
		return 0, response.Error(http.StatusInternalServerError, "invalid object", err)
	}
	return dash.GetDeprecatedInternalID(), nil // nolint:staticcheck
}

// GetStars implements K8sClients.
func (k *k8sClients) GetStars(c *contextmodel.ReqContext) ([]string, error) {
	dyn, err := dynamic.NewForConfig(k.configProvider.GetDirectRestConfig(c))
	if err != nil {
		return nil, err
	}
	client := dyn.Resource(collectionsV1.StarsResourceInfo.GroupVersionResource()).Namespace(k.namespacer(c.OrgID))

	ctx := c.Req.Context()
	user, err := identity.GetRequester(ctx)
	if err != nil {
		return nil, err
	}

	obj, _ := client.Get(ctx, "user-"+user.GetIdentifier(), v1.GetOptions{})
	if obj != nil {
		resources, ok, _ := unstructured.NestedSlice(obj.Object, "spec", "resource")
		if ok && resources != nil {
			for _, r := range resources {
				tmp, ok := r.(map[string]any)
				if ok {
					g, _, _ := unstructured.NestedString(tmp, "group")
					k, _, _ := unstructured.NestedString(tmp, "kind")
					if k == "Dashboard" && g == dashboardsV1.APIGroup {
						names, _, _ := unstructured.NestedStringSlice(tmp, "names")
						return names, nil
					}
				}
			}
		}
	}
	return []string{}, nil
}

// AddStar implements K8sClients.
func (k *k8sClients) AddStar(c *contextmodel.ReqContext, uid string) error {
	dyn, err := kubernetes.NewForConfig(k.configProvider.GetDirectRestConfig(c))
	if err != nil {
		return err
	}

	ctx := c.Req.Context()
	user, err := identity.GetRequester(ctx)
	if err != nil {
		return err
	}

	ns := k.namespacer(c.OrgID)

	client := dyn.RESTClient()
	rsp := client.Put().AbsPath(
		"apis", collectionsV1.APIGroup, collectionsV1.APIVersion, "namespaces", ns,
		"stars", "user-"+user.GetIdentifier(),
		"update", dashboardsV1.APIGroup, dashboardsV1.DashboardKind().Kind(), uid,
	).Do(ctx)

	return rsp.Error()
}

// RemoveStar implements K8sClients.
func (k *k8sClients) RemoveStar(c *contextmodel.ReqContext, uid string) error {
	dyn, err := kubernetes.NewForConfig(k.configProvider.GetDirectRestConfig(c))
	if err != nil {
		return err
	}

	ctx := c.Req.Context()
	user, err := identity.GetRequester(ctx)
	if err != nil {
		return err
	}

	ns := k.namespacer(c.OrgID)

	client := dyn.RESTClient()
	rsp := client.Delete().AbsPath(
		"apis", collectionsV1.APIGroup, collectionsV1.APIVersion, "namespaces", ns,
		"stars", "user-"+user.GetIdentifier(),
		"update", dashboardsV1.APIGroup, dashboardsV1.DashboardKind().Kind(), uid,
	).Do(ctx)

	return rsp.Error()
}
