package utils

import (
	"bytes"
	"text/template"

	"github.com/Masterminds/sprig/v3"
	"github.com/juju/errors"
	"github.com/tidbcloud/auto-deploy/internal/types"
	"gopkg.in/yaml.v3"
)

func AwsRoleArn(acc *types.Account) func(string) string {
	return func(role string) string {
		arn := types.AwsRoleArn{
			AccountId: acc.AwsRole.AccountId,
			RoleType:  "role",
			Role:      role,
		}
		return arn.String()
	}
}

// toYaml encodes an item into a YAML string
func toYaml(v any) string {
	output, _ := yaml.Marshal(v)
	return string(output)
}

func mustToYaml(v any) (string, error) {
	output, err := yaml.Marshal(v)
	if err != nil {
		return "", err
	}
	return string(output), nil
}

func RenderK8sTemplate(tmpl string, region *types.Region) (*types.K8s, error) {
	funcs := template.FuncMap{
		"getAwsRoleArn": AwsRoleArn(&region.Account),
		"toYaml":        toYaml,
		"mustToYaml":    mustToYaml,
		"getEksAddonLatestVersion": func(addon, k8sVersion string) (string, error) {
			return GetEksAddonLatestVersion(addon, region.Region, k8sVersion)
		},
		"getEksAddonDefaultVersion": func(addon, k8sVersion string) (string, error) {
			return GetEksAddonDefaultVersion(addon, region.Region, k8sVersion)
		},
		"getAvailabilityZones": func() (string, error) {
			return GetAvailabilityZonesString(region.CloudProviderType, region.Region)
		},
		"getAvailabilityZonesN": func(n int) (string, error) {
			return GetAvailabilityZonesNString(region.CloudProviderType, region.Region, n)
		},
		"getAvailabilityZoneIndex": func(index int) (string, error) {
			return GetAvailabilityZoneIndex(region.CloudProviderType, region.Region, index)
		},
		"awsAzId2Name": func(profile string, ids string) (string, error) {
			return AwsAzIdToName(region.Region, ids, profile)
		},
	}

	t, err := template.New("k8s").Funcs(sprig.TxtFuncMap()).Funcs(funcs).Parse(tmpl)
	if err != nil {
		return nil, errors.Annotatef(err, "Failed to parse k8s template")
	}

	var buffer bytes.Buffer
	if err = t.Execute(&buffer, region); err != nil {
		return nil, errors.Annotatef(err, "Failed to render k8s template")
	}

	output := &types.K8s{}
	if err := yaml.Unmarshal(buffer.Bytes(), output); err != nil {
		return nil, errors.Annotatef(err, "Failed to unmarshal k8s template: %s", buffer.String())
	}
	return output, nil
}

func RenderJobOptionsTemplate(tmpl string, region *types.Region) (map[string]any, error) {
	funcs := template.FuncMap{
		"getAwsRoleArn":   AwsRoleArn(&region.Account),
		"toYaml":          toYaml,
		"getHostedZoneId": GetHostZoneId,
		"getLoadBalancerHostedZoneId": func() (string, error) {
			return GetElbServiceNlbHostedZoneId(region.Region)
		},
		"getAvailabilityZones": func() (string, error) {
			return GetAvailabilityZonesString(region.CloudProviderType, region.Region)
		},
		"getAvailabilityZonesN": func(n int) (string, error) {
			return GetAvailabilityZonesNString(region.CloudProviderType, region.Region, n)
		},
		"getAvailabilityZoneIndex": func(index int) (string, error) {
			return GetAvailabilityZoneIndex(region.CloudProviderType, region.Region, index)
		},
		"getS3PolicyElbAccount": func() string {
			return GetS3PolicyElbAccountByRegion(region.Region)
		},
		"awsAzId2Name": func(profile string, ids string) (string, error) {
			return AwsAzIdToName(region.Region, ids, profile)
		},
	}

	t, err := template.New("jobOptions").Funcs(sprig.TxtFuncMap()).Funcs(funcs).Parse(tmpl)
	if err != nil {
		return nil, errors.Annotatef(err, "Failed to parse job input template")
	}

	var buffer bytes.Buffer
	if err = t.Execute(&buffer, region); err != nil {
		return nil, errors.Annotatef(err, "Failed to render job template")
	}

	output := make(map[string]any)
	if err := yaml.Unmarshal(buffer.Bytes(), &output); err != nil {
		return nil, errors.Annotatef(err, "Failed to unmarshal job input: %s", buffer.String())
	}
	return output, nil
}
