package setup

import (
	"fmt"
	"strconv"

	"code.justin.tv/d8a/buddy/cmd/buddy-cli/data"
	"code.justin.tv/d8a/buddy/lib/awsutil"
	"code.justin.tv/d8a/buddy/lib/config"
	"code.justin.tv/d8a/buddy/lib/git"
	"code.justin.tv/d8a/buddy/lib/store"
	"code.justin.tv/d8a/buddy/lib/terminal"
	"code.justin.tv/twitch/cli/ghutil"
	"github.com/aws/aws-sdk-go/service/rds/rdsiface"
	"github.com/hashicorp/go-multierror"
	"github.com/pkg/errors"
	"github.com/spf13/cobra"
)

var scrapeCmd = &cobra.Command{
	Use:   "scrape",
	Short: "Execute an iceman command against a target cluster",
	Long:  "",
	RunE: func(cmd *cobra.Command, args []string) error {
		fmt.Println("Locating buddy's datastore...")
		rdsInstance, err := data.RootData.LocateDataStore(true)
		if err != nil {
			return err
		}

		db, err := store.OpenDbConn(rdsInstance, data.RootData.SandstormClient())
		if err != nil {
			return err
		}
		fmt.Println("Connected to datastore.")

		configFile := data.RootData.ConfigFile()

		clusters, err := clustersFromTags(data.RootData.RdsClient(), fmt.Sprintf("rds-buddy-%s-managed", configFile.SandstormTeam), configFile.Environment, configFile.GithubTeam)
		if err != nil {
			return err
		}
		configFile.Cluster = clusters

		fmt.Println("Writing clusters to store")
		err = store.PutClusters(db, configFile, data.RootData.SandstormClient())
		if err != nil {
			return err
		}

		configFile.Cluster, err = store.GetClusters(db)
		if err != nil {
			return err
		}

		client, err := ghutil.ClientFromToken(configFile.GithubKey)
		if err != nil {
			return err
		}

		//Clear out dead repositories which have stuck around on the file system somehow
		repositories, err := git.FindDeadRepositories(configFile)
		if err != nil {
			return err
		}

		for _, repository := range repositories {
			err = git.CleanupRepository(repository, client)
			if err != nil {
				return err
			}
		}

		for index := range configFile.Cluster {
			repoObject, err := git.RetrieveRepository(configFile.Cluster[index].Repository, client)
			if err != nil || repoObject == nil {
				confirm, err := terminal.AskForConfirmation(fmt.Sprintf("Github repository %s, for cluster %s doesn't exist.  Create it?", configFile.Cluster[index].Repository, configFile.Cluster[index].Name))
				if err != nil {
					continue
				}
				if !confirm {
					repo, err := terminal.AskForInput("name of repo to create in form <github org>/<repo name>")
					if err != nil {
						fmt.Println("Failed to get user input for repo for:", configFile.Cluster[index].Name)
					} else {
						fmt.Println("using:", repo, "for:", configFile.Cluster[index].Name)
						configFile.Cluster[index].Repository = repo
					}
				}
			}
		}

		return nil
	},
}

func init() {
	RootCmd.AddCommand(scrapeCmd)
}

// clustersFromTags is a method that retrieves all buddy clusters by scanning AWS for instances that have the correct tags, and then filling
// out their cluster data
func clustersFromTags(client rdsiface.RDSAPI, tagKey string, environment string, githubTeam string) ([]*config.Cluster, error) {
	errGroup := new(multierror.Error)

	instanceTags, err := awsutil.InstanceTags(client)
	if err != nil {
		errGroup = multierror.Append(errGroup, errors.Wrap(err, "couldn't get rds instance tags"))
	}

	var builtClusters config.ClusterList
	for dbIdentifier, resource := range instanceTags {
		foundCluster := ""
		foundEnvironment := ""

		for _, tag := range resource.Tags {
			if *tag.Key == tagKey {
				foundCluster = *tag.Value
			} else if *tag.Key == "rds-buddy-environment" {
				foundEnvironment = *tag.Value
			}
		}

		if foundCluster != "" && (foundEnvironment == "" || foundEnvironment == environment) {
			fmt.Println("Tag Scan Found:", dbIdentifier)
			repo := fmt.Sprintf("%s/%s-migrations", githubTeam, foundCluster)

			_, ok := resource.Data["master-id"]
			if ok {
				//Don't build clusters off replicas
				continue
			}

			_, ok = resource.Data["cluster-id"]
			if ok {
				//Build aurora clusters off the actual cluster object instead of master instances
				continue
			}

			instancePort, err := strconv.Atoi(resource.Data["port"])
			if err != nil {
				errGroup = multierror.Append(errGroup, errors.Wrap(err, fmt.Sprintf("couldn't decode resource port for %s", resource.Name)))
			}

			dbname, ok := resource.Data["dbname"]
			if !ok {
				dbname = ""
			}

			builtClusters = append(builtClusters, &config.Cluster{
				Name:           foundCluster,
				Environment:    environment,
				Repository:     repo,
				Driver:         resource.Data["driver"],
				Host:           resource.Data["host"],
				Port:           instancePort,
				IsAurora:       false,
				RootIdentifier: resource.Name,
				Database:       dbname,
				SuperUser:      resource.Data["superuser"],
			})
		}
	}

	clusterTags, err := awsutil.ClusterTags(client)
	if err != nil {
		errGroup = multierror.Append(errGroup, errors.Wrap(err, "couldn't get rds cluster tags"))
	}

	for dbIdentifier, resource := range clusterTags {
		for _, tag := range resource.Tags {
			if *tag.Key == tagKey {
				fmt.Println("Tag Scan Find:", dbIdentifier)
				repo := fmt.Sprintf("%s/%s-migrations", githubTeam, *tag.Value)

				_, ok := resource.Data["master-id"]
				if ok {
					//Don't build clusters off aurora replica clusters
					continue
				}

				clusterPort, err := strconv.Atoi(resource.Data["port"])
				if err != nil {
					errGroup = multierror.Append(errGroup, errors.Wrap(err, fmt.Sprintf("couldn't decode resource port for %s", resource.Name)))
				}

				dbname, ok := resource.Data["dbname"]
				if !ok {
					dbname = ""
				}

				builtClusters = append(builtClusters, &config.Cluster{
					Name:           *tag.Value,
					Environment:    environment,
					Repository:     repo,
					Driver:         resource.Data["driver"],
					Host:           resource.Data["host"],
					Port:           clusterPort,
					IsAurora:       true,
					RootIdentifier: resource.Name,
					Database:       dbname,
					SuperUser:      resource.Data["superuser"],
				})
			}
		}
	}

	fmt.Println("Found", len(builtClusters), "clusters from AWS Tags")
	return builtClusters, errGroup.ErrorOrNil()
}
