package awsutil

import (
	"fmt"
	"sync"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/rds"
	"github.com/aws/aws-sdk-go/service/rds/rdsiface"
	multierror "github.com/hashicorp/go-multierror"
	"github.com/pkg/errors"
)

type RdsTagCollector struct {
	rdsClient         rdsiface.RDSAPI
	collectedTags     ResourceTags
	collectorWait     sync.WaitGroup
	tagWait           sync.WaitGroup
	collectionChannel chan ResourceTags
	errorChannel      chan error
	errOutput         *multierror.Error
}

type RdsResource struct {
	Name string
	Arn  string
	Data map[string]string
	Tags []*rds.Tag
}

type ResourceTags map[string]*RdsResource

func NewTagCollector(rdsClient rdsiface.RDSAPI) *RdsTagCollector {
	return &RdsTagCollector{
		rdsClient:         rdsClient,
		collectedTags:     make(ResourceTags),
		collectionChannel: make(chan ResourceTags),
		errorChannel:      make(chan error),
		errOutput:         new(multierror.Error),
	}
}

func (coll *RdsTagCollector) Start() {
	coll.collectorWait.Add(1)
	go func(coll *RdsTagCollector) {
		defer coll.collectorWait.Done()
		for resourceTag := range coll.collectionChannel {
			for key, value := range resourceTag {
				coll.collectedTags[key] = value
			}
		}
		for err := range coll.errorChannel {
			coll.errOutput = multierror.Append(coll.errOutput, err)
		}
		return
	}(coll)
}

func (coll *RdsTagCollector) End() error {
	coll.tagWait.Wait()
	close(coll.collectionChannel)
	close(coll.errorChannel)
	coll.collectorWait.Wait()
	return coll.errOutput.ErrorOrNil()
}

func (coll *RdsTagCollector) CollectedTags() ResourceTags {
	return coll.collectedTags
}

func (coll *RdsTagCollector) AddResourcePage(resourcePage []*RdsResource) {
	coll.tagWait.Add(len(resourcePage))
	for _, resource := range resourcePage {
		coll.AddResource(resource)
	}
	fmt.Println("Scanned", len(resourcePage), "rds resources for tags.")
}

func (coll *RdsTagCollector) AddResource(resource *RdsResource) {
	go func(resource *RdsResource) {
		defer coll.tagWait.Done()
		tag_params := &rds.ListTagsForResourceInput{
			ResourceName: aws.String(resource.Arn),
		}

		tags, err := coll.rdsClient.ListTagsForResource(tag_params)
		if err != nil {
			coll.errorChannel <- errors.Wrap(err, fmt.Sprintf("couldn't get tags for %s", resource.Arn))
		}

		for _, tag := range tags.TagList {
			resource.Tags = append(resource.Tags, tag)
		}
		coll.collectionChannel <- ResourceTags{(resource.Name): resource}
	}(resource)
}

func DefaultRdsTags(sandstormTeam string) []*rds.Tag {
	return []*rds.Tag{
		// TODO: Add a contact email in the owner tag somehow?
		&rds.Tag{
			Key:   aws.String("Owner"),
			Value: aws.String(sandstormTeam),
		},
		&rds.Tag{
			Key:   aws.String("Project"),
			Value: aws.String(fmt.Sprintf("rds-buddy-%s", sandstormTeam)),
		},
	}
}
