package k8s

import (
	"encoding/json"
	"net/url"
	"os"

	"github.com/pingcap/errors"

	apiresource "github.com/pingcap/test-infra/common/model/resource"
	"github.com/pingcap/test-infra/sdk/core"
	sdkresource "github.com/pingcap/test-infra/sdk/resource"
)

var (
	_ sdkresource.Client = BRClient{}
)

type BRClient struct {
	WorkloadNodeClient
}

func NewBRClient(k *KubeClientSet) BRClient {
	return BRClient{WorkloadNodeClient{k}}
}

// Create ...
//
//	_, err := client.Create([]byte(`{
//        "name": "br",
//        "testbed": "testbed-1",
//        "type": "BR_NODE",
//        "spec": {
//            "image": "docker.io/pingcap/br:v5.0.1",
//            "resources": {
//                "requests": {
//                    "memory": "64Mi",
//                    "cpu": "250m",
//                    "storage": "1Gi"
//                }
//            }
//        }
//	}`))
func (t BRClient) Create(spec json.RawMessage) (core.Resource, error) {
	apiRes, err := t.buildBRObject(spec)
	if err != nil {
		return nil, err
	}
	wSpec := buildWorkloadNodeFromBR(apiRes)
	spec, err = json.Marshal(wSpec)
	if err != nil {
		return nil, err
	}
	w, err := t.WorkloadNodeClient.Create(spec)
	if err != nil {
		return nil, err
	}
	return t.buildBRFromWorkloadNode(w.(*WorkloadNode)), nil
}

func (t BRClient) Get(handle apiresource.ResourceHandle) (core.Resource, error) {
	res, err := t.WorkloadNodeClient.Get(handle)
	if err != nil {
		return nil, err
	}
	return t.buildBRFromWorkloadNode(res.(*WorkloadNode)), nil
}

func (t BRClient) GetEndpoints(_ apiresource.ResourceHandle) (apiresource.ResourceEndpoints, error) {
	return apiresource.ResourceEndpoints{}, nil
}

func (t BRClient) Delete(handle apiresource.ResourceHandle) error {
	return t.WorkloadNodeClient.Delete(handle)
}

func (t BRClient) FQDN() string {
	return sdkresource.FQDN(t)
}

func (t BRClient) ReserveEnv(_ apiresource.ResourceHandle, _ apiresource.GcReserveEnvAction) error {
	return nil
}

func (t BRClient) Artifact(handle apiresource.ResourceHandle, action apiresource.GcArtifactAction, archiver sdkresource.ArchiverFunc) (map[string]*url.URL, error) {
	resource, err := t.Get(handle)
	if err != nil {
		return nil, err
	}
	br := resource.(*BR)
	if err = br.Sync(); err != nil {
		return nil, err
	}
	return artifactWorkloadNode(t.KubeClientSet, &br.WorkloadNode, archiver)
}

func (t BRClient) buildBRObject(params json.RawMessage) (resHandle *apiresource.BR, err error) {
	resHandle = &apiresource.BR{}

	// TODO: add validation on BR
	if err := json.Unmarshal(params, resHandle); err != nil {
		return nil, errors.Trace(err)
	}

	return resHandle, nil
}

func (t BRClient) buildBRFromWorkloadNode(node *WorkloadNode) *BR {
	res := &BR{*node}
	res.Provider = t.FQDN()
	return res
}

func buildWorkloadNodeFromBR(br *apiresource.BR) apiresource.WorkloadNode {
	if br.Spec.Name == "" {
		br.Spec.Name = "br"
	}
	if len(br.Spec.Command) == 0 {
		br.Spec.Command = []string{"tail", "-f", "/dev/null"}
	}
	return apiresource.WorkloadNode{
		ResourceHandle: br.ResourceHandle,
		Spec: apiresource.WorkloadNodeSpec{
			Container: br.Spec.WorkloadUnit,
			Artifact:  br.Spec.Artifact,
		},
		Qos: br.Qos,
	}
}

type BRGetter struct{ handle apiresource.ResourceHandle }

func (t *BRGetter) Get() (core.Resource, error) {
	k, err := NewKubeClientFromKubeConfig(t.handle.Testbed, os.Getenv("KUBECONFIG"))
	if err != nil {
		return nil, err
	}
	return NewBRClient(k).Get(t.handle)
}

func init() {
	core.RegisterProvider(sdkresource.FQDN(BRClient{}),
		func(handle apiresource.ResourceHandle) core.ResourceGetter { return &BRGetter{handle: handle} })
}
