package providers

import (
	"context"
	"errors"

	aliopenapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
	"github.com/aliyun/credentials-go/credentials"
	"github.com/tidbcloud/observability/pkg/resourceworker/providers/alicloud"
	"github.com/tidbcloud/observability/pkg/resourceworker/providers/types"

	awsconfig "github.com/aws/aws-sdk-go-v2/config"

	"github.com/tidbcloud/observability/pkg/common/constants"
	"github.com/tidbcloud/observability/pkg/common/structs"
	"github.com/tidbcloud/observability/pkg/resourceworker/providers/aws"
	"github.com/tidbcloud/observability/pkg/resourceworker/providers/gcp"
)

var (
	awsIAMClient             IAMClient
	gcpIAMClient             IAMClientGCP
	aliCloudIAMClient        IAMClientAliCloud
	awsPrivateEndpointClient PrivateEndpointClient
	gcpPSCClient             PSCClient
)

func InitClient(serviceInfo structs.ServiceInfo) error {
	awsCli, err := initAWSIAMClient(serviceInfo)
	if err != nil {
		return err
	}
	awsIAMClient = awsCli

	gcpCli, err := initGCPIAMClient(serviceInfo)
	if err != nil {
		return err
	}
	gcpIAMClient = gcpCli

	aliCli, err := initAliCloudRAMClient(serviceInfo)
	if err != nil {
		return err
	}
	aliCloudIAMClient = aliCli

	awsPECli, err := initAWSPrivateEndpointClient(serviceInfo)
	if err != nil {
		return err
	}
	awsPrivateEndpointClient = awsPECli

	gcpPSCCli, err := initGCPPSCClient(serviceInfo)
	if err != nil {
		return err
	}
	gcpPSCClient = gcpPSCCli

	return nil
}

func initAWSIAMClient(serviceInfo structs.ServiceInfo) (IAMClient, error) {
	if serviceInfo.Vendor != string(constants.AWS) {
		return nil, nil
	}
	// we should  init aws config and client before start Observe
	cfg, err := awsconfig.LoadDefaultConfig(context.TODO())
	if err != nil {
		return nil, errors.New("unable to get aws config")
	}
	cfg.Region = serviceInfo.Region
	// get iam client
	return aws.NewIAM(cfg), nil
}

func initGCPIAMClient(serviceInfo structs.ServiceInfo) (IAMClientGCP, error) {
	if serviceInfo.Vendor != string(constants.GCP) {
		return nil, nil
	}
	// get iam client
	return gcp.NewIAM(serviceInfo.AccountInfo.Project, serviceInfo.Region)
}

func initAliCloudRAMClient(serviceInfo structs.ServiceInfo) (IAMClientAliCloud, error) {
	if serviceInfo.Vendor != string(constants.AliCloud) {
		return nil, nil
	}

	cred, err := credentials.NewCredential(nil)
	if err != nil {
		return nil, errors.New("unable to get ali cloud config")
	}
	region := serviceInfo.Region
	config := &aliopenapi.Config{
		Credential: cred,
		RegionId:   &region,
	}

	return alicloud.NewIAMClient(config), nil
}

func initAWSPrivateEndpointClient(serviceInfo structs.ServiceInfo) (PrivateEndpointClient, error) {
	if serviceInfo.Vendor != string(constants.AWS) {
		return nil, nil
	}
	// we should  init aws config and client before start Observe
	cfg, err := awsconfig.LoadDefaultConfig(context.TODO())
	if err != nil {
		return nil, errors.New("unable to get aws config")
	}
	cfg.Region = serviceInfo.Region
	// get iam client
	return aws.NewPrivateEndpoint(cfg), nil
}

func initGCPPSCClient(serviceInfo structs.ServiceInfo) (PSCClient, error) {
	if serviceInfo.Vendor != string(constants.GCP) {
		return nil, nil
	}
	// get iam client
	return gcp.NewPSC(serviceInfo.AccountInfo.Project, serviceInfo.Region)
}

func GetAWSIAMClient() IAMClient {
	return awsIAMClient
}

func GetGCPIAMClient() IAMClientGCP {
	return gcpIAMClient
}

func GetAliCloudIAMClient() IAMClientAliCloud {
	return aliCloudIAMClient
}

func GetAWSPrivateEndpointClient() PrivateEndpointClient {
	return awsPrivateEndpointClient
}

func GetGCPPSCClient() PSCClient {
	return gcpPSCClient
}

func MockAWSIAMClient(mock IAMClient) {
	awsIAMClient = mock
}

func MockGCPIAMClient(mock IAMClientGCP) {
	gcpIAMClient = mock
}

func MockAliCloudIAMClient(mock IAMClientAliCloud) {
	aliCloudIAMClient = mock
}

func MockAWSPrivateEndpointClient(mock PrivateEndpointClient) {
	awsPrivateEndpointClient = mock
}

func MockGCPPSCClient(mock PSCClient) {
	gcpPSCClient = mock
}

func IsObjectNotFound(err error) bool {
	return errors.Is(err, types.ObjectNotFoundError)
}
