//go:build generate
// +build generate

package main

import (
	"bytes"
	"flag"
	"fmt"
	"go/format"
	"log"
	"os"
	"text/template"

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

var (
	idAttribName = flag.String("IDAttribName", "resource_arn", "idAttribName")
)

func usage() {
	fmt.Fprintf(os.Stderr, "Usage:\n")
	fmt.Fprintf(os.Stderr, "\tmain.go [flags]\n\n")
	fmt.Fprintf(os.Stderr, "Flags:\n")
	flag.PrintDefaults()
}

type TemplateData struct {
	AWSService      string
	AWSServiceUpper string
	ServicePackage  string

	IDAttribName string
}

func main() {
	log.SetFlags(0)
	flag.Usage = usage
	flag.Parse()

	servicePackage := os.Getenv("GOPACKAGE")
	awsService, err := names.AWSGoV1Package(servicePackage)

	if err != nil {
		log.Fatalf("encountered: %s", err)
	}

	u, err := names.ProviderNameUpper(servicePackage)

	if err != nil {
		log.Fatalf("encountered: %s", err)
	}

	templateData := TemplateData{
		AWSService:      awsService,
		AWSServiceUpper: u,
		ServicePackage:  servicePackage,
		IDAttribName:    *idAttribName,
	}

	resourceFilename := "tag_gen.go"
	resourceTestFilename := "tag_gen_test.go"

	if err := generateTemplateFile(resourceFilename, resourceTemplateBody, templateData); err != nil {
		log.Fatal(err)
	}

	if err := generateTemplateFile(resourceTestFilename, resourceTestTemplateBody, templateData); err != nil {
		log.Fatal(err)
	}
}

func generateTemplateFile(filename string, templateBody string, templateData interface{}) error {
	tmpl, err := template.New(filename).Parse(templateBody)

	if err != nil {
		return fmt.Errorf("error parsing template: %w", err)
	}

	var buffer bytes.Buffer
	err = tmpl.Execute(&buffer, templateData)

	if err != nil {
		return fmt.Errorf("error executing template: %w", err)
	}

	generatedFileContents, err := format.Source(buffer.Bytes())

	if err != nil {
		return fmt.Errorf("error formatting generated file: %w", err)
	}

	f, err := os.Create(filename)

	if err != nil {
		return fmt.Errorf("error creating file (%s): %w", filename, err)
	}

	defer f.Close()

	_, err = f.Write(generatedFileContents)

	if err != nil {
		return fmt.Errorf("error writing to file (%s): %w", filename, err)
	}

	return nil
}

const (
	resourceTemplateBody = `
// Code generated by internal/generate/tagresource/main.go; DO NOT EDIT.

package {{ .ServicePackage }}

import (
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go/service/{{ .AWSService }}"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
	"github.com/hashicorp/terraform-provider-aws/internal/conns"
	tftags "github.com/hashicorp/terraform-provider-aws/internal/tags"
	"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
)

func ResourceTag() *schema.Resource {
	return &schema.Resource{
		Create: resourceTagCreate,
		Read:   resourceTagRead,
		Update: resourceTagUpdate,
		Delete: resourceTagDelete,
		Importer: &schema.ResourceImporter{
			State: schema.ImportStatePassthrough,
		},

		Schema: map[string]*schema.Schema{
			"{{ .IDAttribName }}": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"key": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"value": {
				Type:     schema.TypeString,
				Required: true,
			},
		},
	}
}

func resourceTagCreate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*conns.AWSClient).{{ .AWSServiceUpper }}Conn

	identifier := d.Get("{{ .IDAttribName }}").(string)
	key := d.Get("key").(string)
	value := d.Get("value").(string)

	{{ if eq .ServicePackage "ec2" }}
	if err := CreateTags(conn, identifier, map[string]string{key: value}); err != nil {
	{{- else }}
	if err := UpdateTags(conn, identifier, nil, map[string]string{key: value}); err != nil {
	{{- end }}
		return fmt.Errorf("error creating %s resource (%s) tag (%s): %w", {{ .ServicePackage }}.ServiceID, identifier, key, err)
	}

	d.SetId(tftags.SetResourceID(identifier, key))

	return resourceTagRead(d, meta)
}

func resourceTagRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*conns.AWSClient).{{ .AWSServiceUpper }}Conn
	identifier, key, err := tftags.GetResourceID(d.Id())

	if err != nil {
		return err
	}

	value, err := GetTag(conn, identifier, key)

	if !d.IsNewResource() && tfresource.NotFound(err) {
		log.Printf("[WARN] %s resource (%s) tag (%s) not found, removing from state", {{ .ServicePackage }}.ServiceID, identifier, key)
		d.SetId("")
		return nil
	}

	if err != nil {
		return fmt.Errorf("error reading %s resource (%s) tag (%s): %w", {{ .ServicePackage }}.ServiceID, identifier, key, err)
	}

	d.Set("{{ .IDAttribName }}", identifier)
	d.Set("key", key)
	d.Set("value", value)

	return nil
}

func resourceTagUpdate(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*conns.AWSClient).{{ .AWSServiceUpper }}Conn
	identifier, key, err := tftags.GetResourceID(d.Id())

	if err != nil {
		return err
	}

	if err := UpdateTags(conn, identifier, nil, map[string]string{key: d.Get("value").(string)}); err != nil {
		return fmt.Errorf("error updating %s resource (%s) tag (%s): %w", {{ .ServicePackage }}.ServiceID, identifier, key, err)
	}

	return resourceTagRead(d, meta)
}

func resourceTagDelete(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*conns.AWSClient).{{ .AWSServiceUpper }}Conn
	identifier, key, err := tftags.GetResourceID(d.Id())

	if err != nil {
		return err
	}

	if err := UpdateTags(conn, identifier, map[string]string{key: d.Get("value").(string)}, nil); err != nil {
		return fmt.Errorf("error deleting %s resource (%s) tag (%s): %w", {{ .ServicePackage }}.ServiceID, identifier, key, err)
	}

	return nil
}
`
	resourceTestTemplateBody = `
// Code generated by internal/generate/tagresource/main.go; DO NOT EDIT.

package {{ .ServicePackage }}_test

import (
	"fmt"

	"github.com/aws/aws-sdk-go/service/{{ .AWSService }}"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
	"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
	"github.com/hashicorp/terraform-provider-aws/internal/acctest"
	"github.com/hashicorp/terraform-provider-aws/internal/conns"
	tftags "github.com/hashicorp/terraform-provider-aws/internal/tags"
	tf{{ .ServicePackage }} "github.com/hashicorp/terraform-provider-aws/internal/service/{{ .ServicePackage }}"
	"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
)

func testAccCheckTagDestroy(s *terraform.State) error {
	conn := acctest.Provider.Meta().(*conns.AWSClient).{{ .AWSServiceUpper }}Conn

	for _, rs := range s.RootModule().Resources {
		if rs.Type != "aws_{{ .ServicePackage }}_tag" {
			continue
		}

		identifier, key, err := tftags.GetResourceID(rs.Primary.ID)

		if err != nil {
			return err
		}

		_, err = tf{{ .ServicePackage }}.GetTag(conn, identifier, key)

		if tfresource.NotFound(err) {
			continue
		}

		if err != nil {
			return err
		}

		return fmt.Errorf("%s resource (%s) tag (%s) still exists", {{ .ServicePackage }}.ServiceID, identifier, key)
	}

	return nil
}

func testAccCheckTagExists(resourceName string) resource.TestCheckFunc {
	return func(s *terraform.State) error {
		rs, ok := s.RootModule().Resources[resourceName]
		if !ok {
			return fmt.Errorf("not found: %s", resourceName)
		}

		if rs.Primary.ID == "" {
			return fmt.Errorf("%s: missing resource ID", resourceName)
		}

		identifier, key, err := tftags.GetResourceID(rs.Primary.ID)

		if err != nil {
			return err
		}

		conn := acctest.Provider.Meta().(*conns.AWSClient).{{ .AWSServiceUpper }}Conn

		_, err = tf{{ .ServicePackage }}.GetTag(conn, identifier, key)

		if err != nil {
			return err
		}

		return nil
	}
}
`
)
