package cloud_provider

import (
	"fmt"
	"os"
	"path"
	"strings"

	"github.com/juju/errors"
	log "github.com/sirupsen/logrus"
	"github.com/tidbcloud/auto-deploy/internal/types"
	"github.com/tidbcloud/auto-deploy/internal/utils"
)

type alicloudProvider struct {
	oidcProvider types.OidcProvider
	account      types.Account
	config       string
}

func NewAliCloudProvider(oidcProvider types.OidcProvider, acc types.Account, region string) (types.CloudProvider, error) {
	configPath := path.Join(os.Getenv("HOME"), ".aliyun/config.json")
	bytes, err := os.ReadFile(configPath)
	if err != nil {
		return nil, errors.Annotatef(err, "Failed to read alicloud config file %s", configPath)
	}

	return &alicloudProvider{
		oidcProvider: oidcProvider,
		account:      acc,
		config:       string(bytes),
	}, nil
}

func (p *alicloudProvider) GetCloudProviderType() types.CloudProviderType {
	return types.AliCloud
}

func (*alicloudProvider) GetAuthConfigPath(homeDir string) string {
	return homeDir + "/.aliyun"
}

func (*alicloudProvider) GetAuthConfigSubPath() []string {
	return []string{"config.json"}
}

func (p *alicloudProvider) GetAuthConfigYaml(name string, accounts []types.Account) string {
	return `
apiVersion: v1
kind: Secret
type: Opaque
metadata:
    name: ` + name + `
stringData:
    config.json: |
` + utils.GetContentWithIndent(6, p.config) + `
`
}

func (*alicloudProvider) WriteToLocal(homeDir string, accounts []types.Account) error {
	return nil
}

func (p *alicloudProvider) CreateK8s(k8s *types.K8s) error {
	oidcIssuer, err := utils.RunCommandAndGetOutput("./scripts/get-ack-info.sh",
		"oidc-issuer", k8s.Name)
	if err == nil {
		log.WithField("name", k8s.Name).Info("Clsuter already exists")
		k8s.OidcIssuer = oidcIssuer
		p.getPublicIp(k8s)
		p.getEndpoint(k8s)
		return errors.AlreadyExistsf("Cluster %s", k8s.Name)
	}

	args, err := getCreateK8sArgs(k8s)
	if err != nil {
		return err
	}

	exitCode, err := utils.RunCommand("./scripts/create-ack.sh",
		utils.LogWithPrefix(log.InfoLevel, fmt.Sprintf("create-%s-output: ", k8s.Name)),
		args)
	if err != nil {
		return errors.Annotatef(err, "k8sName=%s", k8s.Name)
	}
	if exitCode != 0 {
		return errors.Errorf("Failed to create k8s %s, exitCode=%d", k8s.Name, exitCode)
	}
	log.WithFields(log.Fields{
		"name":    k8s.Name,
		"version": k8s.Version,
		"region":  k8s.Region.Region,
	}).Info("Created K8s")

	oidcIssuer, err = utils.RunCommandAndGetOutput("./scripts/get-ack-info.sh",
		"oidc-issuer", k8s.Name)
	if err != nil {
		return err
	}
	k8s.OidcIssuer = oidcIssuer

	p.getPublicIp(k8s)
	p.getEndpoint(k8s)
	log.WithFields(log.Fields{
		"oidc-isuer": oidcIssuer,
		"public-ip":  k8s.PublicIps,
		"endpoint":   k8s.EndPoint,
	}).Debug("Get kubeconfig")
	return nil
}

func (*alicloudProvider) DestroyK8s(k8s *types.K8s) error {
	args, err := getCreateK8sArgs(k8s)
	if err != nil {
		return err
	}

	exitCode, err := utils.RunCommand("./scripts/destroy-ack.sh",
		utils.LogWithPrefix(log.InfoLevel, fmt.Sprintf("destroy-%s-output: ", k8s.Name)),
		args)
	if err != nil {
		return errors.Annotatef(err, "k8sName=%s", k8s.Name)
	}
	if exitCode != 0 {
		return errors.Errorf("Failed to create k8s %s, exitCode=%d", k8s.Name, exitCode)
	}
	log.WithFields(log.Fields{
		"name":   k8s.Name,
		"region": k8s.Region.Region,
	}).Info("Destroyed K8s")
	return nil
}

func (*alicloudProvider) GetKubeConfig(k8s *types.K8s) (string, error) {
	return utils.RunCommandAndGetOutput("./scripts/get-ack-info.sh", "kubeconfig", k8s.Name)
}

func (p *alicloudProvider) getEndpoint(k8s *types.K8s) {
	endpoint, err := utils.RunCommandAndGetOutput("./scripts/get-ack-info.sh",
		"endpoint", k8s.Name)
	if err == nil {
		k8s.EndPoint = endpoint
	}
	log.WithField("endpoint", endpoint).Debug("Get endpoint")
}

func (p *alicloudProvider) getPublicIp(k8s *types.K8s) {
	publicIp, err := utils.RunCommandAndGetOutput("./scripts/get-ack-info.sh",
		"public-ip", k8s.Name)
	if err == nil {
		k8s.PublicIps = strings.Split(publicIp, "\n")
	}
	log.WithField("public-ip", publicIp).Debug("Get public ip")
}
