package common

import (
	"context"

	"github.com/pingcap/errors"
	"github.com/tidbcloud/core-svc/idl/pbgen/proto/core"
	"github.com/tidbcloud/dataflow-service/internal/bizutils"
	"github.com/tidbcloud/dataflow-service/internal/db/entity"
	"github.com/tidbcloud/dataflow-service/internal/grpc/grpcli"
	"github.com/tidbcloud/dataflow-service/internal/handler/agent"
	"github.com/tidbcloud/dataflow-service/internal/utils"
	pkggrpcli "github.com/tidbcloud/dataflow-service/pkg/grpc/grpcli"
	regionalapi "github.com/tidbcloud/dataflow-service/pkg/regional-api"
	"github.com/tidbcloud/pkgv2/log"
	"go.uber.org/zap"
	"google.golang.org/genproto/googleapis/rpc/errdetails"
)

func GetClusterNetworkAgentById(ctx context.Context, clusterId uint64) (
	cluster *core.Cluster,
	network *pkggrpcli.RegionalNetwork,
	agentClient agent.AgentClient,
	err error,
) {
	logger := log.WithContextL(ctx)

	cluster, bizErr := grpcli.GetClusterWithDataplaneByID(ctx, clusterId)
	if bizErr != nil {
		errMsg := "Failed to get cluster information. Please check whether the cluster is cleared or deleted."
		logger.Warn(errMsg, zap.Error(bizErr))
		if bizErr := bizutils.AppendErrorDetails(ctx, &errdetails.ErrorInfo{
			Reason: bizErr.Error(),
		}); bizErr != nil {
			logger.Warn("append error detail error", zap.Error(bizErr))
		}
		return nil, nil, nil, utils.WrapAsErrorCodeErr(bizErr, errMsg)
	}

	network, agentClient, err = GetNetworkAgentByCluster(ctx, cluster)
	if err != nil {
		return nil, nil, nil, err
	}
	return cluster, network, agentClient, nil
}

func getRegionalNetworkByAPI(ctx context.Context, cluster *core.Cluster) (*pkggrpcli.RegionalNetwork, error) {
	regionalAPIEndpoint, err := agent.BuildRegionalAPIEndpoint(ctx, cluster.InfraProviderType, cluster.RegionName, cluster.TenantId)
	if err != nil {
		return nil, err
	}
	regionalAPI := regionalapi.NewTCIDMHTTPClient(regionalAPIEndpoint)

	networks, err := regionalAPI.ListVPCs(ctx, cluster.TenantId, cluster.ProjectId, cluster.Id, []regionalapi.VPCStatus{regionalapi.VPCStatusActive})
	if err != nil {
		return nil, err
	}

	if len(networks) == 0 {
		return nil, errors.New("no network found")
	}

	var (
		awsExternalID          = ""
		awsRoleARN             = ""
		gcpServiceAccountEmail = ""
	)
	clusterRole, err := regionalAPI.GetClusterRole(ctx, cluster.Id)
	if err != nil {
		return nil, err
	}
	awsExternalID = clusterRole.ExternalId
	awsRoleARN = clusterRole.CloudIamIdentity
	gcpServiceAccountEmail = clusterRole.CloudIamIdentity

	network := &pkggrpcli.RegionalNetwork{
		AWS: &pkggrpcli.AWSNetworkSpec{
			VpcID:          networks[0].Info.AWS.VPCID,
			NodeRoleARN:    awsRoleARN,
			ExternalID:     awsExternalID,
			AccountID:      networks[0].Info.AWS.AccountID,
			NodeSubnets:    []string{}, // APPBox does not return this field
			EKSClusterName: "",         // APPBox does not return this field
		},
		GCP: &pkggrpcli.GCPNetworkSpec{
			ServiceAccountEmail: gcpServiceAccountEmail,
			ProjectID:           networks[0].Info.GCP.ProjectID,
			VpcID:               networks[0].VPCID,
			Subnets:             []string{}, // APPBox does not return this field
			NodeZones:           []string{}, // APPBox does not return this field
		},
		Azure: &pkggrpcli.AzureNetworkSpec{
			VNetID: networks[0].Info.Azure.VNetID,
		},
		DataflowAgentUrl: "", // APPBox does not return this field
		Cidr:             networks[0].CidrBlock,
		ExternalIPs:      networks[0].ExternalIPs,
	}

	return network, nil
}

func GetNetworkByCluster(ctx context.Context, cluster *core.Cluster) (*pkggrpcli.RegionalNetwork, error) {
	return getRegionalNetworkByAPI(ctx, cluster)
}

func GetNetworkAgentByCluster(ctx context.Context, cluster *core.Cluster) (*pkggrpcli.RegionalNetwork, agent.AgentClient, error) {
	network, err := GetNetworkByCluster(ctx, cluster)
	if err != nil {
		return nil, nil, err
	}

	agentClient, err := GetAgentClientByCluster(ctx, cluster, network)
	if err != nil {
		logger := log.WithContextS(ctx)
		errMsg := "Network error. If it still fails after several attempts, please contact our support team."
		logger.Error(errMsg, zap.Error(err))
		return nil, nil, utils.WrapAsErrorCodeErr(err, errMsg)
	}
	return network, agentClient, nil
}

func GetAgentClientByNetwork(ctx context.Context, clusterID uint64, network *pkggrpcli.RegionalNetwork) (agent.AgentClient, error) {

	cluster, bizErr := grpcli.GetAnyClusterByID(ctx, clusterID)
	if bizErr != nil {
		return nil, bizErr
	}

	return GetAgentClientByCluster(ctx, cluster, network)
}

func GetAgentClientByCluster(ctx context.Context, cluster *core.Cluster, network *pkggrpcli.RegionalNetwork) (agent.AgentClient, error) {
	return agent.GetAgentClient(ctx, cluster)
}

func GetLightningClient(ctx context.Context, clusterID, importID entity.ID) (agent.LightningAPI, error) {
	_, _, agentClient, err := GetClusterNetworkAgentById(ctx, clusterID.Uint64())
	if err != nil {
		return nil, err
	}
	return agentClient.GetLightningClient(clusterID.Uint64(), importID.Uint64()), nil
}
