package mgmthttpcli

import (
	"context"

	"github.com/go-resty/resty/v2"

	apiv1 "github.com/tidbcloud/infra-provider/api/tidbcloud/cluster-agent/v1"
)

type DataPlaneKubernete struct {
	Version    string `json:"version"`
	Kubeconfig []byte `json:"kubeconfig"`
	Name       string `json:"name"`
}

type DataPlane struct {
	ID         string             `json:"id"`
	AccountID  string             `json:"account_id"`
	Kubernetes DataPlaneKubernete `json:"kubernetes"`
}

type Component_Storage struct {
	Name          string `json:"name,omitempty"`
	Type          string `json:"type,omitempty"`
	SizeGi        uint32 `json:"sizeGi,omitempty"`
	Iops          uint32 `json:"iops,omitempty"`
	ThroughputMbs uint32 `json:"throughputMbs,omitempty"`
	MountPath     string `json:"mountPath,omitempty"`
}

type NodeSpec struct {
	Cpu      uint32 `json:"cpu,omitempty"`
	MemoryGi uint32 `json:"memoryGi,omitempty"`
}

type NodeQuota struct {
	NodeSpec  *NodeSpec `json:"nodeSpec,omitempty"`
	NodeCount uint32    `json:"nodeCount,omitempty"`
	// Read Only: true
	MaxNodeCount uint32 `json:"maxNodeCount,omitempty"`
	// Read Only: true
	MinNodeCount uint32 `json:"minNodeCount,omitempty"`
}

type Component struct {
	Id            string               `json:"id,omitempty"`
	ComponentType string               `json:"componentType,omitempty"`
	NodeQuota     *NodeQuota           `json:"nodeQuota,omitempty"`
	Storages      []*Component_Storage `json:"storages,omitempty"`
	Version       string               `json:"version,omitempty"`
	Phase         string               `json:"phase,omitempty"`
}

type SharedPool struct {
	Id                       string              `json:"id,omitempty"`
	ComponentGroups          []*Component        `json:"componentGroups,omitempty"`
	Version                  string              `json:"version,omitempty"`
	DataplaneName            string              `json:"dataplaneName,omitempty"`
	Phase                    string              `json:"phase,omitempty"`
	CapacityMode             string              `json:"capacityMode,omitempty"`
	PdDeploymentMode         string              `json:"pdDeploymentMode,omitempty"`
	EnabledTiKVRemoteWorkers []string            `json:"enabledTikvRemoteWorkers,omitempty"`
	PdDeploymentStatus       *PDDeploymentStatus `json:"pdDeploymentStatus,omitempty"`
}

type PDDeploymentStatus struct {
	CurrentMode string `json:"currentMode,omitempty"`
	Phase       string `json:"phase,omitempty"`
	Reason      string `json:"reason,omitempty"`
	Message     string `json:"message,omitempty"`
}

type ScaleSharedPoolRequest struct {
	Component *Component `json:"component,omitempty"`
	Force     bool       `json:"force,omitempty"`
}

type TiDBGroup struct {
	Id           string     `json:"id,omitempty"`
	ClusterID    string     `json:"clusterId,omitempty"`
	TenantID     string     `json:"tenantId,omitempty"`
	SharedPoolID string     `json:"sharedPoolId,omitempty"`
	NodeQuota    *NodeQuota `json:"nodeQuota,omitempty"`
	Phase        string     `json:"phase,omitempty"`
	// ...
}

type ListTiDBGroupsResp struct {
	TidbGroups []*TiDBGroup `json:"tidbGroups,omitempty"`
	TotalCount int          `json:"totalCount,omitempty"`
}

type ScaleTiDBGroupRequest struct {
	//ClusterID     string     `json:"clusterId,omitempty"`
	//TidbGroupID   string     `json:"tidbGroupId,omitempty"`
	ComponentType string     `json:"componentType,omitempty"`
	NodeQuota     *NodeQuota `json:"nodeQuota,omitempty"`
	Force         bool       `json:"force,omitempty"`
}

type ApplyComponent struct {
	Id            string `json:"id,omitempty"`
	ComponentType string `json:"componentType,omitempty"`
}

type ApplySharedPoolRequest struct {
	ApplyComponents []*ApplyComponent `json:"applyComponents,omitempty"`
	Force           bool              `json:"force,omitempty"`
}

type VPCPeering struct {
	ID                   string          `json:"id,omitempty"`
	SourceCIDRBlock      string          `json:"sourceCidrBlock,omitempty"`
	DestinationCIDRBlock string          `json:"destinationCidrBlock"`
	AWS                  *VPCPeeringInfo `json:"aws,omitempty"`
	Ali                  *VPCPeeringInfo `json:"ali,omitempty"`
	Status               string          `json:"status,omitempty"`
	Message              string          `json:"message,omitempty"`
}

type VPCPeeringInfo struct {
	DestinationAccountID   string `json:"destinationAccountId"`
	DestinationVPCID       string `json:"destinationVpcId"`
	DestinationRegion      string `json:"destinationRegion"`
	SourceAccountID        string `json:"sourceAccountId,omitempty"`
	SourceVPCID            string `json:"sourceVpcId,omitempty"`
	VPCPeeringConnectionID string `json:"vpcPeeringConnectionId,omitempty"`
}

type ListVPCPeeringsResp struct {
	VPCPeerings []*VPCPeering `json:"vpcPeerings,omitempty"`
}

type SetTiProxyExternalDNSRequest struct {
	Enable bool   `json:"enable"`
	TTL    *int32 `json:"ttl,omitempty"`
}

type NextGenAPI interface {
	GetDataPlane(ctx context.Context, clusterID, dataplaneID string) (*DataPlane, error)
	GetSharedPool(ctx context.Context, clusterID string, sharedPoolID string) (*SharedPool, error)
	ScaleSharedPool(ctx context.Context, clusterID string, sharedPoolID string, request *ScaleSharedPoolRequest) (*SharedPool, error)
	ApplySharedPool(ctx context.Context, clusterID string, sharedPoolID string, request *ApplySharedPoolRequest) error
	ListTiDBGroups(ctx context.Context, clusterID string) (*ListTiDBGroupsResp, error)
	GetTiDBGroup(ctx context.Context, clusterID, tidbGroupID string) (*TiDBGroup, error)
	ScaleTiDBGroup(ctx context.Context, clusterID, tidbGroupID string, request *ScaleTiDBGroupRequest) (*TiDBGroup, error)
	SetTiProxyExternalDNS(ctx context.Context, clusterID, tidbGroupID string, request *SetTiProxyExternalDNSRequest) error
	RestartSharedPoolComponent(ctx context.Context, sharedPoolID string, request *apiv1.RestartReq) (*apiv1.RestartResp, error)
	CreateVPCPeering(ctx context.Context, clusterID, dataplaneID string, request *VPCPeering) (*VPCPeering, error)
	ListVPCPeerings(ctx context.Context, clusterID, dataplaneID string) (*ListVPCPeeringsResp, error)
	DeleteVPCPeering(ctx context.Context, clusterID, dataplaneID, vpcPeeringID string) error
}

type nextgen struct {
	core *resty.Client
}

func (g *nextgen) GetDataPlane(ctx context.Context, clusterID, dataplaneID string) (*DataPlane, error) {
	result := &DataPlane{}
	_, err := g.core.R().
		SetContext(ctx).
		SetPathParams(map[string]string{
			"cluster_id":   clusterID,
			"dataplane_id": dataplaneID,
		}).
		SetResult(result).
		Get("/proxy/nextgen/clusters/{cluster_id}/dataplane-manager/api/v1/dataplanes/{dataplane_id}")
	if err != nil {
		return nil, err
	}

	return result, nil
}

func (g *nextgen) GetSharedPool(ctx context.Context, clusterID string, sharedPoolID string) (*SharedPool, error) {
	result := &SharedPool{}
	_, err := g.core.R().
		SetContext(ctx).
		SetPathParams(map[string]string{
			"cluster_id":     clusterID,
			"shared_pool_id": sharedPoolID,
		}).
		SetResult(result).
		Get("/proxy/nextgen/clusters/{cluster_id}/api/premium/v1/sharedPools/{shared_pool_id}")
	if err != nil {
		return nil, err
	}

	return result, nil
}

func (g *nextgen) ScaleSharedPool(ctx context.Context, clusterID string, sharedPoolID string, request *ScaleSharedPoolRequest) (*SharedPool, error) {
	result := &SharedPool{}
	_, err := g.core.R().
		SetContext(ctx).
		SetPathParams(map[string]string{
			"cluster_id":     clusterID,
			"shared_pool_id": sharedPoolID,
		}).
		SetBody(request).
		SetResult(result).
		Post("/proxy/nextgen/clusters/{cluster_id}/api/premium/v1/sharedPools/{shared_pool_id}:scale")
	if err != nil {
		return nil, err
	}

	return result, nil
}

func (g *nextgen) ApplySharedPool(ctx context.Context, clusterID string, sharedPoolID string, request *ApplySharedPoolRequest) error {
	_, err := g.core.R().
		SetContext(ctx).
		SetPathParams(map[string]string{
			"cluster_id":     clusterID,
			"shared_pool_id": sharedPoolID,
		}).
		SetBody(request).
		Post("/proxy/nextgen/clusters/{cluster_id}/api/premium/v1/sharedPools/{shared_pool_id}:apply")
	if err != nil {
		return err
	}

	return nil
}

func (g *nextgen) ListTiDBGroups(ctx context.Context, clusterID string) (*ListTiDBGroupsResp, error) {
	result := &ListTiDBGroupsResp{}
	_, err := g.core.R().
		SetContext(ctx).
		SetPathParams(map[string]string{
			"cluster_id": clusterID,
		}).
		SetResult(result).
		Get("/proxy/nextgen/clusters/{cluster_id}/api/premium/v1/clusters/{cluster_id}/tidbGroups")
	if err != nil {
		return nil, err
	}

	return result, nil
}

func (g *nextgen) GetTiDBGroup(ctx context.Context, clusterID string, tidbGroupID string) (*TiDBGroup, error) {
	result := &TiDBGroup{}
	_, err := g.core.R().
		SetContext(ctx).
		SetPathParams(map[string]string{
			"cluster_id":    clusterID,
			"tidb_group_id": tidbGroupID,
		}).
		SetResult(result).
		Get("/proxy/nextgen/clusters/{cluster_id}/api/premium/v1/clusters/{cluster_id}/tidbGroups/{tidb_group_id}")
	if err != nil {
		return nil, err
	}

	return result, nil
}

func (g *nextgen) ScaleTiDBGroup(ctx context.Context, clusterID, tidbGroupID string, request *ScaleTiDBGroupRequest) (*TiDBGroup, error) {
	result := &TiDBGroup{}
	_, err := g.core.R().
		SetContext(ctx).
		SetPathParams(map[string]string{
			"cluster_id":    clusterID,
			"tidb_group_id": tidbGroupID,
		}).
		SetBody(request).
		SetResult(result).
		Post("/proxy/nextgen/clusters/{cluster_id}/api/premium/v1/clusters/{cluster_id}/tidbGroups/{tidb_group_id}:scale")
	if err != nil {
		return nil, err
	}

	return result, nil
}

func (g *nextgen) SetTiProxyExternalDNS(ctx context.Context, clusterID, tidbGroupID string, request *SetTiProxyExternalDNSRequest) error {
	_, err := g.core.R().
		SetContext(ctx).
		SetPathParams(map[string]string{
			"cluster_id":    clusterID,
			"tidb_group_id": tidbGroupID,
		}).
		SetBody(request).
		Post("/proxy/nextgen/clusters/{cluster_id}/api/premium/v1/clusters/{cluster_id}/tidbGroups/{tidb_group_id}/tiproxy/externalDNS:set")
	return err
}

func (g *nextgen) RestartSharedPoolComponent(ctx context.Context, sharedPoolID string, req *apiv1.RestartReq) (*apiv1.RestartResp, error) {
	var resp apiv1.RestartResp
	_, err := g.core.R().
		SetContext(ctx).
		SetPathParams(map[string]string{
			"shared_pool_id": sharedPoolID,
		}).
		SetBody(req).
		SetResult(&resp).
		Post(nextGenSharedPoolAgentProxyPrefix + "/api/v1/clusters/{shared_pool_id}:restart")
	if err != nil {
		return nil, err
	}

	return &resp, nil
}

func (g *nextgen) CreateVPCPeering(ctx context.Context, clusterID, dataplaneID string, request *VPCPeering) (*VPCPeering, error) {
	result := &VPCPeering{}
	_, err := g.core.R().
		SetContext(ctx).
		SetPathParams(map[string]string{
			"cluster_id":   clusterID,
			"dataplane_id": dataplaneID,
		}).
		SetBody(request).
		SetResult(result).
		Post("/proxy/nextgen/clusters/{cluster_id}/dataplane-manager/api/v1/dataplanes/{dataplane_id}/vpcPeerings")
	if err != nil {
		return nil, err
	}
	return result, nil
}

func (g *nextgen) ListVPCPeerings(ctx context.Context, clusterID, dataplaneID string) (*ListVPCPeeringsResp, error) {
	result := &ListVPCPeeringsResp{}
	_, err := g.core.R().
		SetContext(ctx).
		SetPathParams(map[string]string{
			"cluster_id":   clusterID,
			"dataplane_id": dataplaneID,
		}).
		SetResult(result).
		Get("/proxy/nextgen/clusters/{cluster_id}/dataplane-manager/api/v1/dataplanes/{dataplane_id}/vpcPeerings")
	if err != nil {
		return nil, err
	}

	return result, nil
}

func (g *nextgen) DeleteVPCPeering(ctx context.Context, clusterID, dataplaneID, vpcPeeringID string) error {
	_, err := g.core.R().
		SetContext(ctx).
		SetPathParams(map[string]string{
			"cluster_id":     clusterID,
			"dataplane_id":   dataplaneID,
			"vpc_peering_id": vpcPeeringID,
		}).
		Delete("/proxy/nextgen/clusters/{cluster_id}/dataplane-manager/api/v1/dataplanes/{dataplane_id}/vpcPeerings/{vpc_peering_id}")

	return err
}
