package store

import (
	"errors"
	"fmt"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/autoscaling/autoscalingiface"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/aws/aws-sdk-go/service/ec2/ec2iface"
	"github.com/aws/aws-sdk-go/service/rds"
	"github.com/aws/aws-sdk-go/service/rds/rdsiface"

	"code.justin.tv/d8a/buddy/lib/awsutil"
	"code.justin.tv/d8a/buddy/lib/config"
	"code.justin.tv/d8a/buddy/lib/sandstorm"
)

const buddyStoreTag string = "rds-buddy-%s-store"

// GetBuddyStore will retrieve an rds.DBInstance object for the current buddy store if it exists (and in some cases will provision a buddy store if it does not)
// the DBInstance can be used to open a DB connection or just obtain basic information about the buddy store
// - rdsClient - an AWS SDK client interface for RDS
// - ec2Client - an AWS SDK client interface for EC2
// - autoscalingClient - an AWS SDK client interface for autoscaling
// - localInstance - an ec2.Instance object for the EC2 instance this code is running on, if any
// - sandstorm - a SandstormAPI client
// - cfg - A parsed buddy configuration file
// - isInSetup - Performs additional searching/provisioning to get a DBInstance
//
// This code will try the following things in order:
//   - Check `localInstance`'s tags to find the identifier for the instance
//   - If isInSetup is false, return an error
//   - Scan all RDS instances for a tagged instance & tag the local EC2 instance if found
//   - Provision a new RDS instance & tag the local EC2 instance
func GetBuddyStore(rdsClient rdsiface.RDSAPI, ec2Client ec2iface.EC2API, autoscalingClient autoscalingiface.AutoScalingAPI, localInstance *ec2.Instance, sandstorm sandstorm.SandstormAPI, cfg *config.ConfigFile, isInSetup bool) (*rds.DBInstance, error) {
	storeTag := fmt.Sprintf(buddyStoreTag, cfg.SandstormTeam)
	dbInstance, err := getStoreFromLocalTag(rdsClient, localInstance, storeTag)
	if dbInstance != nil || err != nil {
		return dbInstance, err
	}

	if !isInSetup {
		return nil, errors.New("the buddy store is not cached in the local instance's tags- you may need to run `buddy-cli setup` to properly configure the store")
	}

	fmt.Println("Could not find buddy store from local tag- scanning datastores.")
	dbInstance, err = getStoreFromScannedTag(rdsClient, storeTag, cfg.Environment)

	if dbInstance == nil && err == nil {
		fmt.Println("Could not find a buddy store- provisioning a new one.")
		dbInstance, err = createBuddyStore(rdsClient, autoscalingClient, localInstance, sandstorm, cfg)
	}

	if dbInstance != nil && err == nil {
		//Write the DB's identifier to the local instance so we can get it easier next time
		_, err := ec2Client.CreateTags(&ec2.CreateTagsInput{
			Resources: []*string{
				localInstance.InstanceId,
			},
			Tags: []*ec2.Tag{
				&ec2.Tag{
					Key:   aws.String(storeTag),
					Value: dbInstance.DBInstanceIdentifier,
				},
			},
		})
		if err != nil {
			return dbInstance, err
		}
	}

	return dbInstance, err
}

func getStoreFromLocalTag(rdsClient rdsiface.RDSAPI, localInstance *ec2.Instance, tagKey string) (*rds.DBInstance, error) {
	dbIdentifier := ""
	for _, tag := range localInstance.Tags {
		if tagKey == *tag.Key {
			dbIdentifier = *tag.Value
			break
		}
	}

	if dbIdentifier == "" {
		return nil, nil
	}

	output, err := rdsClient.DescribeDBInstances(&rds.DescribeDBInstancesInput{
		DBInstanceIdentifier: aws.String(dbIdentifier),
	})
	if err != nil {
		awsErr, ok := err.(awserr.Error)
		if ok && awsErr.Code() == rds.ErrCodeDBInstanceNotFoundFault {
			return nil, nil
		}
		return nil, err
	}

	if len(output.DBInstances) > 0 {
		return output.DBInstances[0], nil
	}
	return nil, nil
}

func getStoreFromScannedTag(rdsClient rdsiface.RDSAPI, tagKey string, environment string) (*rds.DBInstance, error) {
	instanceTags, err := awsutil.InstanceTags(rdsClient)
	if err != nil {
		return nil, err
	}

	for dbIdentifier, instanceResource := range instanceTags {
		foundStoreTag := ""
		foundStoreEnv := ""

		for _, tag := range instanceResource.Tags {
			if *tag.Key == tagKey {
				foundStoreTag = *tag.Value
			} else if *tag.Key == "rds-buddy-environment" {
				foundStoreEnv = *tag.Value
			}
		}

		if foundStoreTag != "" && (foundStoreEnv == "" || foundStoreEnv == environment) {
			instances, err := awsutil.FetchInstancesByIds(rdsClient, []*string{aws.String(dbIdentifier)})
			if err != nil {
				return nil, err
			}
			if len(instances) > 0 {
				return instances[0], nil
			}
		}
	}
	return nil, nil
}
