package resources

import (
	"encoding/json"

	"github.com/hashicorp/go-multierror"

	commR "github.com/pingcap/test-infra/common/model/resource"
	"github.com/pingcap/test-infra/common/model/rms/response"
	"github.com/pingcap/test-infra/services/sdkserver/core"
)

type TestBed struct {
	core.Directory
	Name string
	spec response.CreateTestbedResponse
}

var _ core.Archivable = TestBed{}

// Archive implements the core.Archivable interface
func (b TestBed) Archive() (map[string]string, error) {
	items := NewTestbedItems(b.spec.Items)
	urlMap := make(map[string]string)
	var errs error

	entries, _ := items.ListEntry()
	for _, ent := range entries {
		resource, err := ent.ResourceFunc()
		if err != nil {
			return nil, err
		}

		r, ok := resource.(core.Archivable) // types like TiDBCluster will implements archivable interface
		if !ok {
			continue
		}

		singleResult, err := r.Archive()
		if err != nil {
			errs = multierror.Append(errs, err)
		}
		for k, v := range singleResult {
			urlMap[k] = v
		}
	}

	return urlMap, errs
}

func (b TestBed) ListEntry() ([]core.DirEntry, error) {
	return []core.DirEntry{
		{Name: "name", Resource: core.NewStringFile(b.Name)},
		{Name: "items", Resource: NewTestbedItems(b.spec.Items)},
		{Name: "testResourceType", Resource: core.NewStringFile("TESTBED")},
	}, nil
}

func NewTestBed(spec string) TestBed {
	tb := &TestBed{}
	err := json.Unmarshal([]byte(spec), &(tb.spec))
	if err != nil {
		panic(err)
	}
	tb.Name = tb.spec.Name
	tb.Init(tb)
	return *tb
}

type TestbedItems struct {
	core.Directory
	handlers []commR.ResourceHandle
}

func (t TestbedItems) ListEntry() ([]core.DirEntry, error) {
	dent := make([]core.DirEntry, 0, len(t.handlers))
	for _, h := range t.handlers {
		h := h // for closure to pack value
		dent = append(dent, core.DirEntry{Name: h.Name, ResourceFunc: func() (core.Resource, error) {
			return MakeResource(h)
		}})
	}
	return dent, nil
}

func NewTestbedItems(handlers []response.TestbedResponseItem) TestbedItems {
	t := TestbedItems{handlers: make([]commR.ResourceHandle, 0, len(handlers))}
	for _, ti := range handlers {
		t.handlers = append(t.handlers, ti.ResourceHandle)
	}
	t.Init(&t)
	return t
}
