//go:build generate
// +build generate

package main

import (
	"bytes"
	"encoding/csv"
	"fmt"
	"io"
	"log"
	"os"
	"sort"
	"strings"
	"text/template"

	"github.com/hashicorp/hcl/v2/hclsimple"
	"github.com/hashicorp/terraform-provider-aws/names"
)

const (
	servicesAllFile   = `../../../.teamcity/components/generated/services_all.kt`
	namesDataFile     = "../../../names/names_data.csv"
	serviceConfigFile = "./acctest_services.hcl"
)

type ServiceDatum struct {
	ProviderPackage string
	HumanFriendly   string
	VpcLock         bool
	Parallelism     int
}

type TemplateData struct {
	Services []ServiceDatum
}

func main() {
	fmt.Printf("Generating %s\n", strings.TrimPrefix(servicesAllFile, "../../../"))

	serviceConfigs, err := acctestConfigurations(serviceConfigFile)
	if err != nil {
		log.Fatal(err)
	}

	f, err := os.Open(namesDataFile)
	if err != nil {
		log.Fatal(err)
	}

	defer f.Close()

	csvReader := csv.NewReader(f)

	data, err := csvReader.ReadAll()
	if err != nil {
		log.Fatal(err)
	}

	td := TemplateData{}

	for i, l := range data {
		if i < 1 { // no header
			continue
		}

		if l[names.ColExclude] != "" {
			continue
		}

		if l[names.ColProviderPackageActual] == "" && l[names.ColProviderPackageCorrect] == "" {
			continue
		}

		p := l[names.ColProviderPackageCorrect]

		if l[names.ColProviderPackageActual] != "" {
			p = l[names.ColProviderPackageActual]
		}

		if _, err := os.Stat(fmt.Sprintf("../../service/%s", p)); err != nil || os.IsNotExist(err) {
			continue
		}

		sd := ServiceDatum{
			ProviderPackage: p,
			HumanFriendly:   l[names.ColHumanFriendly],
		}
		serviceConfig, ok := serviceConfigs[p]
		if ok {
			sd.VpcLock = serviceConfig.VpcLock
			sd.Parallelism = serviceConfig.Parallelism
		}

		td.Services = append(td.Services, sd)
	}

	sort.SliceStable(td.Services, func(i, j int) bool {
		return td.Services[i].ProviderPackage < td.Services[j].ProviderPackage
	})

	writeTemplate(servicesAllFile, tmpl, "teamcity", td)
}

func writeTemplate(filename, body, templateName string, td TemplateData) {
	// If the file doesn't exist, create it, otherwise truncate the file
	f, err := os.OpenFile(filename, os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644)
	if err != nil {
		log.Fatalf("error opening file (%s): %s", filename, err)
	}

	tplate, err := template.New(templateName).Parse(body)
	if err != nil {
		log.Fatalf("error parsing template: %s", err)
	}

	var buffer bytes.Buffer
	err = tplate.Execute(&buffer, td)
	if err != nil {
		log.Fatalf("error executing template: %s", err)
	}

	if _, err := f.Write(buffer.Bytes()); err != nil {
		f.Close()
		log.Fatalf("error writing to file (%s): %s", filename, err)
	}

	if err := f.Close(); err != nil {
		log.Fatalf("error closing file (%s): %s", filename, err)
	}
}

var tmpl = `// Code generated by internal/generate/teamcity/main.go; DO NOT EDIT.

val services = mapOf(
{{- range .Services }}
    "{{ .ProviderPackage }}" to ServiceSpec("{{ .HumanFriendly }}"{{ if .VpcLock }}, vpcLock = true{{ end }}{{ if ne .Parallelism 0 }}, parallelismOverride = {{ .Parallelism }}{{ end }}),
{{- end }}
)
`

type acctestConfig struct {
	Services []acctestServiceConfig `hcl:"service,block"`
}

type acctestServiceConfig struct {
	Service     string `hcl:",label"`
	VpcLock     bool   `hcl:"vpc_lock,optional"`
	Parallelism int    `hcl:"parallelism,optional"`
}

func acctestConfigurations(filename string) (map[string]acctestServiceConfig, error) {
	var config acctestConfig

	err := decodeHclFile(filename, &config)
	if err != nil {
		return nil, err
	}

	result := make(map[string]acctestServiceConfig)

	for _, v := range config.Services {
		result[v.Service] = v
	}

	return result, nil
}

func decodeHclFile(filename string, target interface{}) error {
	f, err := os.Open(filename)
	if err != nil {
		return err
	}
	defer f.Close()

	b, err := io.ReadAll(f)
	if err != nil {
		return err
	}

	err = hclsimple.Decode(filename, b, nil, target)
	if err != nil {
		return err
	}

	return nil
}
