package tenant

import (
	"fmt"
	"strconv"
	"time"

	"github.com/tidbcloud/devops/api"
	commonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	nextgenv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
	mgmtv1 "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
	"google.golang.org/protobuf/types/known/timestamppb"
)

type ListReservedCapacityTemplatesReq struct {
	api.PaginationReq `json:",inline"`

	OrganizationID     string `form:"organization_id" json:"organization_id"`
	WorkloadType       string `form:"workload_type" json:"workload_type" enums:"READ,WRITE,MIXED"`
	QPS                int64  `form:"qps" json:"qps"`
	DataSizeGiB        int64  `form:"data_size_gib" json:"data_size_gib"`
	P99ResponseLatency int64  `form:"p99_response_latency" json:"p99_response_latency"`
	Status             string `form:"status" json:"status" enums:"ACTIVE,DEPRECATED"`
	Scope              string `form:"scope" json:"scope" enums:"DEFAULT,ORG_PRIVATE"`
	ServicePlan        string `form:"service_plan" json:"service_plan" enums:"Starter,Essential,Premium,BYOC,Premium_Reserved,Essential_V2"`
}

type ListReservedCapacityTemplatesResp struct {
	api.BaseResp `json:",inline"`

	Total                     uint32                      `json:"total"`
	ReservedCapacityTemplates []*ReservedCapacityTemplate `json:"reserved_capacity_templates"`
}

type GetReservedCapacityTemplateResp struct {
	api.BaseResp `json:",inline"`

	ReservedCapacityTemplate *ReservedCapacityTemplate `json:"reserved_capacity_template"`
}

type CreateReservedCapacityTemplateReq struct {
	Reason                   string                    `json:"reason" binding:"required"`
	ReservedCapacityTemplate *ReservedCapacityTemplate `json:"reserved_capacity_template" binding:"required"`
}

type UpdateReservedCapacityTemplateStatusReq struct {
	Reason string `json:"reason" binding:"required"`
	Status string `json:"status" binding:"required" enums:"ACTIVE,DEPRECATED"`
}

type DeleteReservedCapacityTemplateReq struct {
	Reason string `json:"reason" binding:"required"`
}

type ReservedCapacityTemplateOperationResp struct {
	api.BaseResp `json:",inline"`

	InstanceID *uint64 `json:"instance_id"`
}

type ReservedCapacityTemplate struct {
	ID              uint64                            `json:"id,string,omitempty"`
	TemplateCode    string                            `json:"template_code,omitempty"`
	Description     string                            `json:"description,omitempty"`
	Scope           string                            `json:"scope,omitempty" enums:"DEFAULT,ORG_PRIVATE"`
	OrganizationID  uint64                            `json:"organization_id,string,omitempty"`
	Status          string                            `json:"status,omitempty" enums:"ACTIVE,DEPRECATED"`
	PurchasedVCPU   int64                             `json:"purchased_vcpu,omitempty"`
	Workload        *ReservedCapacityTemplateWorkload `json:"workload,omitempty"`
	FixedTopology   *ReservedCapacityFixedTopology    `json:"fixed_topology,omitempty"`
	TopologySummary string                            `json:"topology_summary,omitempty"`
	CreateTime      *time.Time                        `json:"create_time,omitempty"`
	UpdateTime      *time.Time                        `json:"update_time,omitempty"`
	DeleteTime      *time.Time                        `json:"delete_time,omitempty"`
}

type ReservedCapacityTemplateWorkload struct {
	WorkloadType          string `json:"workload_type,omitempty" enums:"READ,WRITE,MIXED"`
	QPSMin                int64  `json:"qps_min,omitempty"`
	QPSMax                int64  `json:"qps_max,omitempty"`
	DataSizeGiBMin        int64  `json:"data_size_gib_min,omitempty"`
	DataSizeGiBMax        int64  `json:"data_size_gib_max,omitempty"`
	P99ResponseLatencyMin int64  `json:"p99_response_latency_min,omitempty"`
	P99ResponseLatencyMax int64  `json:"p99_response_latency_max,omitempty"`
}

type ReservedCapacityFixedTopology struct {
	Components []*ReservedCapacityComponentTopology `json:"components,omitempty"`
}

type ReservedCapacityComponentTopology struct {
	ComponentType string                         `json:"component_type,omitempty" enums:"TIDB,TIKV,PD,TIPROXY,TIDB_WORKER,TIKV_WORKER,COPROCESSOR_WORKER,TIFLASH_WRITE,TIFLASH_COMPUTE,PD_TSO,PD_SCHEDULING"`
	NodeQuota     *ReservedCapacityNodeQuota     `json:"node_quota,omitempty"`
	Storages      []*ReservedCapacityStorageSpec `json:"storages,omitempty"`
}

type ReservedCapacityNodeQuota struct {
	NodeSpec  *ReservedCapacityNodeSpec `json:"node_spec,omitempty"`
	NodeCount uint32                    `json:"node_count,omitempty"`
}

type ReservedCapacityNodeSpec struct {
	CPU      uint32 `json:"cpu,omitempty"`
	MemoryGi uint32 `json:"memory_gi,omitempty"`
}

type ReservedCapacityStorageSpec struct {
	Name          string `json:"name,omitempty" enums:"DATA,RAFTLOG,SLOWLOG"`
	Type          string `json:"type,omitempty"`
	SizeGi        uint32 `json:"size_gi,omitempty"`
	IOPS          uint32 `json:"iops,omitempty"`
	ThroughputMBS uint32 `json:"throughput_mbs,omitempty"`
}

func BuildListReservedCapacityTemplatesRequest(req *ListReservedCapacityTemplatesReq) (*mgmtv1.ListReservedCapacityTemplatesRequest, error) {
	var organizationID uint64
	if req.OrganizationID != "" {
		parsedOrganizationID, err := strconv.ParseUint(req.OrganizationID, 10, 64)
		if err != nil {
			return nil, fmt.Errorf("invalid organization_id: %w", err)
		}
		organizationID = parsedOrganizationID
	}

	workloadType, err := reservedCapacityTemplateWorkloadTypeFromString(req.WorkloadType)
	if err != nil {
		return nil, err
	}
	status, err := reservedCapacityTemplateStatusFromString(req.Status)
	if err != nil {
		return nil, err
	}
	scope, err := reservedCapacityTemplateScopeFromString(req.Scope)
	if err != nil {
		return nil, err
	}
	servicePlan, err := servicePlanFromString(req.ServicePlan)
	if err != nil {
		return nil, err
	}

	return &mgmtv1.ListReservedCapacityTemplatesRequest{
		OrganizationId:     organizationID,
		WorkloadType:       workloadType,
		Qps:                req.QPS,
		DataSizeGib:        req.DataSizeGiB,
		P99ResponseLatency: req.P99ResponseLatency,
		Status:             status,
		Scope:              scope,
		ServicePlan:        servicePlan,
		Page:               req.Page,
		PageSize:           req.PerPage,
	}, nil
}

func BuildReservedCapacityTemplatePB(req *ReservedCapacityTemplate) (*nextgenv1.ReservedCapacityTemplate, error) {
	if req == nil {
		return nil, nil
	}

	scope, err := reservedCapacityTemplateScopeFromString(req.Scope)
	if err != nil {
		return nil, err
	}
	status, err := reservedCapacityTemplateStatusFromString(req.Status)
	if err != nil {
		return nil, err
	}
	workload, err := buildReservedCapacityTemplateWorkloadPB(req.Workload)
	if err != nil {
		return nil, err
	}
	topology, err := buildReservedCapacityFixedTopologyPB(req.FixedTopology)
	if err != nil {
		return nil, err
	}

	return &nextgenv1.ReservedCapacityTemplate{
		Id:              req.ID,
		TemplateCode:    req.TemplateCode,
		Description:     req.Description,
		Scope:           scope,
		OrganizationId:  req.OrganizationID,
		Status:          status,
		PurchasedVcpu:   req.PurchasedVCPU,
		Workload:        workload,
		FixedTopology:   topology,
		TopologySummary: req.TopologySummary,
	}, nil
}

func ReservedCapacityTemplateStatusPB(status string) (nextgenv1.ReservedCapacityTemplate_Status, error) {
	return reservedCapacityTemplateStatusFromString(status)
}

func BuildReservedCapacityTemplates(resp *mgmtv1.ListReservedCapacityTemplatesResponse) []*ReservedCapacityTemplate {
	if resp == nil {
		return nil
	}
	templates := make([]*ReservedCapacityTemplate, 0, len(resp.GetReservedCapacityTemplates()))
	for _, template := range resp.GetReservedCapacityTemplates() {
		templates = append(templates, BuildReservedCapacityTemplate(template))
	}
	return templates
}

func BuildReservedCapacityTemplate(pb *nextgenv1.ReservedCapacityTemplate) *ReservedCapacityTemplate {
	if pb == nil {
		return nil
	}

	return &ReservedCapacityTemplate{
		ID:              pb.GetId(),
		TemplateCode:    pb.GetTemplateCode(),
		Description:     pb.GetDescription(),
		Scope:           reservedCapacityTemplateScopeToString(pb.GetScope()),
		OrganizationID:  pb.GetOrganizationId(),
		Status:          reservedCapacityTemplateStatusToString(pb.GetStatus()),
		PurchasedVCPU:   pb.GetPurchasedVcpu(),
		Workload:        buildReservedCapacityTemplateWorkload(pb.GetWorkload()),
		FixedTopology:   buildReservedCapacityFixedTopology(pb.GetFixedTopology()),
		TopologySummary: pb.GetTopologySummary(),
		CreateTime:      timestampToTime(pb.GetCreateTime()),
		UpdateTime:      timestampToTime(pb.GetUpdateTime()),
		DeleteTime:      timestampToTime(pb.GetDeleteTime()),
	}
}

func buildReservedCapacityTemplateWorkloadPB(req *ReservedCapacityTemplateWorkload) (*nextgenv1.ReservedCapacityTemplate_Workload, error) {
	if req == nil {
		return nil, nil
	}
	workloadType, err := reservedCapacityTemplateWorkloadTypeFromString(req.WorkloadType)
	if err != nil {
		return nil, err
	}
	return &nextgenv1.ReservedCapacityTemplate_Workload{
		WorkloadType:          workloadType,
		QpsMin:                req.QPSMin,
		QpsMax:                req.QPSMax,
		DataSizeGibMin:        req.DataSizeGiBMin,
		DataSizeGibMax:        req.DataSizeGiBMax,
		P99ResponseLatencyMin: req.P99ResponseLatencyMin,
		P99ResponseLatencyMax: req.P99ResponseLatencyMax,
	}, nil
}

func buildReservedCapacityFixedTopologyPB(req *ReservedCapacityFixedTopology) (*nextgenv1.FixedTopology, error) {
	if req == nil {
		return nil, nil
	}
	topology := &nextgenv1.FixedTopology{
		Components: make([]*nextgenv1.ComponentTopology, 0, len(req.Components)),
	}
	for _, component := range req.Components {
		componentPB, err := buildReservedCapacityComponentTopologyPB(component)
		if err != nil {
			return nil, err
		}
		topology.Components = append(topology.Components, componentPB)
	}
	return topology, nil
}

func buildReservedCapacityComponentTopologyPB(req *ReservedCapacityComponentTopology) (*nextgenv1.ComponentTopology, error) {
	if req == nil {
		return nil, nil
	}
	componentType, err := reservedCapacityComponentTypeFromString(req.ComponentType)
	if err != nil {
		return nil, err
	}
	component := &nextgenv1.ComponentTopology{
		ComponentType: componentType,
		NodeQuota:     buildReservedCapacityNodeQuotaPB(req.NodeQuota),
		Storages:      make([]*nextgenv1.StorageSpec, 0, len(req.Storages)),
	}
	for _, storage := range req.Storages {
		storagePB, err := buildReservedCapacityStorageSpecPB(storage)
		if err != nil {
			return nil, err
		}
		component.Storages = append(component.Storages, storagePB)
	}
	return component, nil
}

func buildReservedCapacityNodeQuotaPB(req *ReservedCapacityNodeQuota) *nextgenv1.NodeQuota {
	if req == nil {
		return nil
	}
	return &nextgenv1.NodeQuota{
		NodeSpec:  buildReservedCapacityNodeSpecPB(req.NodeSpec),
		NodeCount: req.NodeCount,
	}
}

func buildReservedCapacityNodeSpecPB(req *ReservedCapacityNodeSpec) *nextgenv1.NodeSpec {
	if req == nil {
		return nil
	}
	return &nextgenv1.NodeSpec{
		Cpu:      req.CPU,
		MemoryGi: req.MemoryGi,
	}
}

func buildReservedCapacityStorageSpecPB(req *ReservedCapacityStorageSpec) (*nextgenv1.StorageSpec, error) {
	if req == nil {
		return nil, nil
	}
	name, err := reservedCapacityStorageNameFromString(req.Name)
	if err != nil {
		return nil, err
	}
	return &nextgenv1.StorageSpec{
		Name:          name,
		Type:          req.Type,
		SizeGi:        req.SizeGi,
		Iops:          req.IOPS,
		ThroughputMbs: req.ThroughputMBS,
	}, nil
}

func buildReservedCapacityTemplateWorkload(pb *nextgenv1.ReservedCapacityTemplate_Workload) *ReservedCapacityTemplateWorkload {
	if pb == nil {
		return nil
	}
	return &ReservedCapacityTemplateWorkload{
		WorkloadType:          reservedCapacityTemplateWorkloadTypeToString(pb.GetWorkloadType()),
		QPSMin:                pb.GetQpsMin(),
		QPSMax:                pb.GetQpsMax(),
		DataSizeGiBMin:        pb.GetDataSizeGibMin(),
		DataSizeGiBMax:        pb.GetDataSizeGibMax(),
		P99ResponseLatencyMin: pb.GetP99ResponseLatencyMin(),
		P99ResponseLatencyMax: pb.GetP99ResponseLatencyMax(),
	}
}

func buildReservedCapacityFixedTopology(pb *nextgenv1.FixedTopology) *ReservedCapacityFixedTopology {
	if pb == nil {
		return nil
	}
	topology := &ReservedCapacityFixedTopology{
		Components: make([]*ReservedCapacityComponentTopology, 0, len(pb.GetComponents())),
	}
	for _, component := range pb.GetComponents() {
		topology.Components = append(topology.Components, buildReservedCapacityComponentTopology(component))
	}
	return topology
}

func buildReservedCapacityComponentTopology(pb *nextgenv1.ComponentTopology) *ReservedCapacityComponentTopology {
	if pb == nil {
		return nil
	}
	component := &ReservedCapacityComponentTopology{
		ComponentType: reservedCapacityComponentTypeToString(pb.GetComponentType()),
		NodeQuota:     buildReservedCapacityNodeQuota(pb.GetNodeQuota()),
		Storages:      make([]*ReservedCapacityStorageSpec, 0, len(pb.GetStorages())),
	}
	for _, storage := range pb.GetStorages() {
		component.Storages = append(component.Storages, buildReservedCapacityStorageSpec(storage))
	}
	return component
}

func buildReservedCapacityNodeQuota(pb *nextgenv1.NodeQuota) *ReservedCapacityNodeQuota {
	if pb == nil {
		return nil
	}
	return &ReservedCapacityNodeQuota{
		NodeSpec:  buildReservedCapacityNodeSpec(pb.GetNodeSpec()),
		NodeCount: pb.GetNodeCount(),
	}
}

func buildReservedCapacityNodeSpec(pb *nextgenv1.NodeSpec) *ReservedCapacityNodeSpec {
	if pb == nil {
		return nil
	}
	return &ReservedCapacityNodeSpec{
		CPU:      pb.GetCpu(),
		MemoryGi: pb.GetMemoryGi(),
	}
}

func buildReservedCapacityStorageSpec(pb *nextgenv1.StorageSpec) *ReservedCapacityStorageSpec {
	if pb == nil {
		return nil
	}
	return &ReservedCapacityStorageSpec{
		Name:          reservedCapacityStorageNameToString(pb.GetName()),
		Type:          pb.GetType(),
		SizeGi:        pb.GetSizeGi(),
		IOPS:          pb.GetIops(),
		ThroughputMBS: pb.GetThroughputMbs(),
	}
}

func timestampToTime(ts *timestamppb.Timestamp) *time.Time {
	if ts == nil {
		return nil
	}
	t := ts.AsTime()
	return &t
}

func reservedCapacityTemplateScopeFromString(value string) (nextgenv1.ReservedCapacityTemplate_Scope, error) {
	if value == "" {
		return nextgenv1.ReservedCapacityTemplate_SCOPE_UNSPECIFIED, nil
	}
	v, ok := nextgenv1.ReservedCapacityTemplate_Scope_value[value]
	if !ok {
		return nextgenv1.ReservedCapacityTemplate_SCOPE_UNSPECIFIED, fmt.Errorf("invalid reserved capacity template scope: %s", value)
	}
	return nextgenv1.ReservedCapacityTemplate_Scope(v), nil
}

func reservedCapacityTemplateScopeToString(value nextgenv1.ReservedCapacityTemplate_Scope) string {
	return nextgenv1.ReservedCapacityTemplate_Scope_name[int32(value)]
}

func reservedCapacityTemplateStatusFromString(value string) (nextgenv1.ReservedCapacityTemplate_Status, error) {
	if value == "" {
		return nextgenv1.ReservedCapacityTemplate_STATUS_UNSPECIFIED, nil
	}
	v, ok := nextgenv1.ReservedCapacityTemplate_Status_value[value]
	if !ok {
		return nextgenv1.ReservedCapacityTemplate_STATUS_UNSPECIFIED, fmt.Errorf("invalid reserved capacity template status: %s", value)
	}
	return nextgenv1.ReservedCapacityTemplate_Status(v), nil
}

func reservedCapacityTemplateStatusToString(value nextgenv1.ReservedCapacityTemplate_Status) string {
	return nextgenv1.ReservedCapacityTemplate_Status_name[int32(value)]
}

func reservedCapacityTemplateWorkloadTypeFromString(value string) (nextgenv1.ReservedCapacityTemplate_WorkloadType, error) {
	if value == "" {
		return nextgenv1.ReservedCapacityTemplate_WORKLOAD_TYPE_UNSPECIFIED, nil
	}
	v, ok := nextgenv1.ReservedCapacityTemplate_WorkloadType_value[value]
	if !ok {
		return nextgenv1.ReservedCapacityTemplate_WORKLOAD_TYPE_UNSPECIFIED, fmt.Errorf("invalid reserved capacity template workload type: %s", value)
	}
	return nextgenv1.ReservedCapacityTemplate_WorkloadType(v), nil
}

func reservedCapacityTemplateWorkloadTypeToString(value nextgenv1.ReservedCapacityTemplate_WorkloadType) string {
	return nextgenv1.ReservedCapacityTemplate_WorkloadType_name[int32(value)]
}

func reservedCapacityComponentTypeFromString(value string) (nextgenv1.ComponentType, error) {
	if value == "" {
		return nextgenv1.ComponentType_COMPONENT_TYPE_UNSPECIFIED, nil
	}
	v, ok := nextgenv1.ComponentType_value[value]
	if !ok {
		return nextgenv1.ComponentType_COMPONENT_TYPE_UNSPECIFIED, fmt.Errorf("invalid reserved capacity component type: %s", value)
	}
	return nextgenv1.ComponentType(v), nil
}

func reservedCapacityComponentTypeToString(value nextgenv1.ComponentType) string {
	return nextgenv1.ComponentType_name[int32(value)]
}

func reservedCapacityStorageNameFromString(value string) (nextgenv1.StorageSpec_StorageName, error) {
	if value == "" {
		return nextgenv1.StorageSpec_STORAGE_NAME_UNSPECIFIED, nil
	}
	v, ok := nextgenv1.StorageSpec_StorageName_value[value]
	if !ok {
		return nextgenv1.StorageSpec_STORAGE_NAME_UNSPECIFIED, fmt.Errorf("invalid reserved capacity storage name: %s", value)
	}
	return nextgenv1.StorageSpec_StorageName(v), nil
}

func reservedCapacityStorageNameToString(value nextgenv1.StorageSpec_StorageName) string {
	return nextgenv1.StorageSpec_StorageName_name[int32(value)]
}

func servicePlanFromString(value string) (commonv1.ServicePlan, error) {
	if value == "" {
		return commonv1.ServicePlan_SERVICE_PLAN_UNSPECIFIED, nil
	}
	v, ok := commonv1.ServicePlan_value[value]
	if !ok {
		return commonv1.ServicePlan_SERVICE_PLAN_UNSPECIFIED, fmt.Errorf("invalid service plan: %s", value)
	}
	return commonv1.ServicePlan(v), nil
}
