package taskruns

import (
	"context"
	"strconv"
	"strings"

	"github.com/pingcap/test-infra/common/client/tcms/client"

	"github.com/pingcap/test-infra/common/client/util"
	"github.com/pingcap/test-infra/common/model"
	"github.com/pingcap/test-infra/common/model/tcms/taskruns"
)

type TestbedSetupClient interface {
	List(ctx context.Context, opts taskruns.ListTestbedSetupOpts) (*taskruns.TestbedSetupList, error)
	Get(ctx context.Context, id model.ID, opts taskruns.GetTestbedSetupOpts) (*taskruns.TestbedSetup, error)
	Create(ctx context.Context, run taskruns.TestbedSetup, opts taskruns.CreateTestbedSetupOpts) (*taskruns.TestbedSetup, error)
	UpdateSchedule(ctx context.Context, run taskruns.TestbedSetup, opts taskruns.GeneralUpdateOpts) (*taskruns.TestbedSetup, error)
	UpdateRun(ctx context.Context, run taskruns.TestbedSetup, opts taskruns.GeneralUpdateOpts) (*taskruns.TestbedSetup, error)
	UpdateReport(ctx context.Context, run taskruns.TestbedSetup, opts taskruns.GeneralUpdateOpts) (*taskruns.TestbedSetup, error)
}

type testbedSetupClient struct {
	tcmsClient client.TcmsClient
}

const testbedSetupPath = "api/v1/testbedsetups"

func (t testbedSetupClient) NewRequest(ctx context.Context) *util.Request {
	return t.tcmsClient.NewRequest().AppendPath(testbedSetupPath).WithContext(ctx)
}

func (t testbedSetupClient) List(ctx context.Context, opts taskruns.ListTestbedSetupOpts) (*taskruns.TestbedSetupList, error) {
	rt := taskruns.TestbedSetupList{}
	// just for better encoding style, not functionally necessary
	var planExecIDs string
	if opts.PlanExecIDs != nil {
		IDs := make([]string, 0, len(opts.PlanExecIDs))
		for _, i := range opts.PlanExecIDs {
			IDs = append(IDs, i.String())
		}
		planExecIDs = strings.Join(IDs, ",")
		opts.PlanExecIDs = nil
	}
	_, err := t.NewRequest(ctx).
		Get().
		SchemaQuery(opts).
		AppendQueryIfVal("planExecIDs", planExecIDs).
		Into(&rt).
		Do()
	if err != nil {
		return nil, err
	}
	return &rt, nil
}

func (t testbedSetupClient) Get(ctx context.Context, id model.ID, opts taskruns.GetTestbedSetupOpts) (*taskruns.TestbedSetup, error) {
	rt := taskruns.TestbedSetup{}
	_, err := t.NewRequest(ctx).
		Get().
		AppendPath(strconv.Itoa(int(id))).
		SchemaQuery(opts).
		Into(&rt).
		Do()
	if err != nil {
		return nil, err
	}
	return &rt, nil
}

func (t testbedSetupClient) Create(ctx context.Context, run taskruns.TestbedSetup, opts taskruns.CreateTestbedSetupOpts) (*taskruns.TestbedSetup, error) {
	rt := taskruns.TestbedSetup{}
	_, err := t.NewRequest(ctx).
		Post().
		Json(run).
		SchemaQuery(opts).
		Into(&rt).
		Do()
	if err != nil {
		return nil, err
	}
	return &rt, nil
}

func (t testbedSetupClient) UpdateSchedule(ctx context.Context, run taskruns.TestbedSetup, opts taskruns.GeneralUpdateOpts) (*taskruns.TestbedSetup, error) {
	return t.updatePartialStatus(ctx, run, opts, "schedule")
}

func (t testbedSetupClient) UpdateRun(ctx context.Context, run taskruns.TestbedSetup, opts taskruns.GeneralUpdateOpts) (*taskruns.TestbedSetup, error) {
	return t.updatePartialStatus(ctx, run, opts, "run")
}

func (t testbedSetupClient) UpdateReport(ctx context.Context, run taskruns.TestbedSetup, opts taskruns.GeneralUpdateOpts) (*taskruns.TestbedSetup, error) {
	return t.updatePartialStatus(ctx, run, opts, "report")
}

func (t testbedSetupClient) updatePartialStatus(ctx context.Context, run taskruns.TestbedSetup, opts taskruns.GeneralUpdateOpts, subPath string) (*taskruns.TestbedSetup, error) {
	rt := taskruns.TestbedSetup{}
	_, err := t.NewRequest(ctx).
		Put().
		AppendPath(strconv.Itoa(int(run.MetaData.ID))).
		AppendPath("status").
		AppendPath(subPath).
		SchemaQuery(opts).
		Json(run).
		Into(&rt).
		Do()
	if err != nil {
		return nil, err
	}
	return &rt, nil
}

func NewTestbedSetupClient(tcmsClient client.TcmsClient) TestbedSetupClient {
	return testbedSetupClient{tcmsClient: tcmsClient}
}
