package spu

import (
	"context"

	"google.golang.org/protobuf/types/known/emptypb"

	globaldedicatedv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/dedicated/v1"
	tidbmgmtservicev1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

func (s *serverImpl) CreateComputeMu(
	ctx context.Context,
	req *tidbmgmtservicev1.CreateComputeMuRequest,
) (*tidbmgmtservicev1.ComputeMu, error) {
	ctx, span := tracer.Start(ctx, "CreateComputeMu")
	defer span.End()

	if req.GetComputeMu() == nil {
		return nil, bizerr.New(bizerr.ErrCodeParams, "ComputeMu is required")
	}
	cmu := req.GetComputeMu()

	outputs, err := s.dedicatedCli.BatchUpsertInstanceMus(ctx, &globaldedicatedv1.BatchUpsertInstanceMusRequest{
		Adds: []*globaldedicatedv1.InstanceMu{covertComputeMuToInstanceSku(cmu)},
	})
	if err != nil {
		return nil, err
	}

	if len(outputs.GetAdds()) != 1 {
		return nil, bizerr.New(bizerr.ErrCodeGRPC, "unexpected compute mu added")
	}

	return covertInstanceSkuToComputeMu(outputs.GetAdds()[0]), nil
}

func (s *serverImpl) DeleteComputeMu(
	ctx context.Context,
	req *tidbmgmtservicev1.DeleteComputeMuRequest,
) (*emptypb.Empty, error) {
	ctx, span := tracer.Start(ctx, "DeleteComputeMu")
	defer span.End()

	if req.GetId() == 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "ComputeMuId is required")
	}

	_, err := s.dedicatedCli.BatchUpsertInstanceMus(ctx, &globaldedicatedv1.BatchUpsertInstanceMusRequest{
		Deletes: []uint64{req.GetId()},
	})
	if err != nil {
		return nil, err
	}
	return &emptypb.Empty{}, nil
}

func (s *serverImpl) GetComputeMu(
	ctx context.Context,
	req *tidbmgmtservicev1.GetComputeMuRequest,
) (*tidbmgmtservicev1.ComputeMu, error) {
	ctx, span := tracer.Start(ctx, "GetComputeMu")
	defer span.End()

	if req.GetId() == 0 {
		return nil, bizerr.New(bizerr.ErrCodeParams, "ComputeMuId is required")
	}

	output, err := s.dedicatedCli.GetInstanceMu(ctx, &globaldedicatedv1.GetInstanceMuRequest{
		Id: req.GetId(),
	})
	if err != nil {
		return nil, err
	}

	return covertInstanceSkuToComputeMu(output), nil
}

func (s *serverImpl) ListComputeMus(
	ctx context.Context,
	req *tidbmgmtservicev1.ListComputeMusRequest,
) (*tidbmgmtservicev1.ListComputeMusResponse, error) {
	ctx, span := tracer.Start(ctx, "ListComputeMus")
	defer span.End()

	outputs, err := s.dedicatedCli.ListInstanceMus(ctx, &globaldedicatedv1.ListInstanceMusRequest{
		CloudProvider: req.GetCloudProvider(),
		CpuArchs:      req.GetCpuArchs(),
	})
	if err != nil {
		return nil, err
	}

	computeMus := make([]*tidbmgmtservicev1.ComputeMu, 0, len(outputs.GetInstanceMus()))
	for _, ins := range outputs.GetInstanceMus() {
		computeMus = append(computeMus, covertInstanceSkuToComputeMu(ins))
	}

	return &tidbmgmtservicev1.ListComputeMusResponse{
		ComputeMus: computeMus,
	}, nil
}

func covertComputeMuToInstanceSku(cmu *tidbmgmtservicev1.ComputeMu) *globaldedicatedv1.InstanceMu {
	return &globaldedicatedv1.InstanceMu{
		Id:            cmu.GetId(),
		InstanceType:  cmu.GetInstanceType(),
		CpuCore:       cmu.GetCpuCore(),
		MemoryGi:      cmu.GetMemoryGi(),
		CpuArch:       cmu.GetCpuArch(),
		CloudProvider: cmu.GetCloudProvider(),
	}
}

func covertInstanceSkuToComputeMu(ins *globaldedicatedv1.InstanceMu) *tidbmgmtservicev1.ComputeMu {
	return &tidbmgmtservicev1.ComputeMu{
		Id:            ins.GetId(),
		InstanceType:  ins.GetInstanceType(),
		CpuCore:       ins.GetCpuCore(),
		MemoryGi:      ins.GetMemoryGi(),
		CpuArch:       ins.GetCpuArch(),
		CloudProvider: ins.GetCloudProvider(),
	}
}
