package reservedcapacitytemplate

import (
	"google.golang.org/protobuf/encoding/protojson"
	"google.golang.org/protobuf/proto"
	"google.golang.org/protobuf/types/known/timestamppb"
	"gorm.io/datatypes"

	globalngv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/entity"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/infra/repoimpl/schema"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

func entityToModel(en *entity.ReservedCapacityTemplate) (*schema.ReservedCapacityTemplate, bizerr.BizError) {
	if en == nil {
		return nil, bizerr.New(bizerr.ErrCodeParams, "reserved capacity template entity is nil")
	}
	workloadJSON, err := marshalProtoJSON(en.GetWorkload())
	if err != nil {
		return nil, err
	}
	fixedTopologyJSON, err := marshalProtoJSON(en.GetFixedTopology())
	if err != nil {
		return nil, err
	}
	model := &schema.ReservedCapacityTemplate{
		ID:              en.GetId(),
		TemplateCode:    en.GetTemplateCode(),
		Description:     en.GetDescription(),
		Scope:           en.GetScope().String(),
		OrganizationId:  en.GetOrganizationId(),
		Status:          en.GetStatus().String(),
		PurchasedVcpu:   en.GetPurchasedVcpu(),
		TopologySummary: en.GetTopologySummary(),
		Workload:        workloadJSON,
		FixedTopology:   fixedTopologyJSON,
	}
	if en.GetCreateTime().IsValid() {
		model.CreatedAt = en.GetCreateTime().AsTime()
	}
	if en.GetUpdateTime().IsValid() {
		model.UpdatedAt = en.GetUpdateTime().AsTime()
	}
	if en.GetDeleteTime().IsValid() {
		model.DeletedAt.Valid = true
		model.DeletedAt.Time = en.GetDeleteTime().AsTime()
	}
	return model, nil
}

func modelToEntity(model *schema.ReservedCapacityTemplate) (*entity.ReservedCapacityTemplate, bizerr.BizError) {
	if model == nil {
		return nil, bizerr.New(bizerr.ErrCodeParams, "reserved capacity template model is nil")
	}
	var workload *globalngv1.ReservedCapacityTemplate_Workload
	var err bizerr.BizError
	if len(model.Workload) > 0 {
		workload, err = unmarshalWorkloadJSON(model.Workload)
		if err != nil {
			return nil, err
		}
	}
	var fixedTopology *globalngv1.FixedTopology
	if len(model.FixedTopology) > 0 {
		fixedTopology, err = unmarshalFixedTopologyJSON(model.FixedTopology)
		if err != nil {
			return nil, err
		}
	}
	en := &globalngv1.ReservedCapacityTemplate{
		Id:              model.ID,
		TemplateCode:    model.TemplateCode,
		Description:     model.Description,
		Scope:           globalngv1.ReservedCapacityTemplate_Scope(globalngv1.ReservedCapacityTemplate_Scope_value[model.Scope]),
		OrganizationId:  model.OrganizationId,
		Status:          globalngv1.ReservedCapacityTemplate_Status(globalngv1.ReservedCapacityTemplate_Status_value[model.Status]),
		PurchasedVcpu:   model.PurchasedVcpu,
		Workload:        workload,
		FixedTopology:   fixedTopology,
		TopologySummary: model.TopologySummary,
		CreateTime:      timestamppb.New(model.CreatedAt),
		UpdateTime:      timestamppb.New(model.UpdatedAt),
	}
	if model.DeletedAt.Valid {
		en.DeleteTime = timestamppb.New(model.DeletedAt.Time)
	}
	return en, nil
}

var protoJSONMarshalOptions = protojson.MarshalOptions{
	UseProtoNames:   true,
	UseEnumNumbers:  false,
	EmitUnpopulated: false,
}

func marshalProtoJSON(message proto.Message) (datatypes.JSON, bizerr.BizError) {
	if message == nil {
		return nil, nil
	}
	jsonBytes, err := protoJSONMarshalOptions.Marshal(message)
	if err != nil {
		return nil, bizerr.WrapErrorWithCode(err, bizerr.ErrCodeUnknown, "marshal reserved capacity template proto json")
	}
	return datatypes.JSON(jsonBytes), nil
}

func unmarshalWorkloadJSON(raw datatypes.JSON) (*globalngv1.ReservedCapacityTemplate_Workload, bizerr.BizError) {
	var workload globalngv1.ReservedCapacityTemplate_Workload
	if err := (protojson.UnmarshalOptions{DiscardUnknown: true}).Unmarshal(raw, &workload); err != nil {
		return nil, bizerr.WrapErrorWithCode(err, bizerr.ErrCodeUnknown, "unmarshal reserved capacity workload json")
	}
	return &workload, nil
}

func unmarshalFixedTopologyJSON(raw datatypes.JSON) (*globalngv1.FixedTopology, bizerr.BizError) {
	var fixedTopology globalngv1.FixedTopology
	if err := (protojson.UnmarshalOptions{DiscardUnknown: true}).Unmarshal(raw, &fixedTopology); err != nil {
		return nil, bizerr.WrapErrorWithCode(err, bizerr.ErrCodeUnknown, "unmarshal reserved capacity fixed topology json")
	}
	return &fixedTopology, nil
}
