package store

import (
	"fmt"
	"strings"

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

	"time"

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

func ensureSubnetGroup(rdsClient rdsiface.RDSAPI, subnets []*string, cfg *config.ConfigFile) (string, error) {
	tagKey := fmt.Sprintf(buddyStoreTag, cfg.SandstormTeam)

	subnetResources, err := awsutil.SubnetGroupTags(rdsClient)
	if err != nil {
		return "", err
	}

	for name, resource := range subnetResources {
		foundCluster := ""
		foundEnvironment := ""

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

		if foundCluster != "" && (foundEnvironment == "" || foundEnvironment == cfg.Environment) {
			return name, nil
		}
	}

	subnetTags := append([]*rds.Tag{
		&rds.Tag{
			Key:   aws.String(tagKey),
			Value: aws.String("true"),
		},
		&rds.Tag{
			Key:   aws.String("rds-buddy-environment"),
			Value: aws.String(cfg.Environment),
		}}, awsutil.DefaultRdsTags(cfg.SandstormTeam)...)

	output, err := rdsClient.CreateDBSubnetGroup(&rds.CreateDBSubnetGroupInput{
		DBSubnetGroupDescription: aws.String(fmt.Sprintf("DB Subnet Group for %s RDS Buddy Datastore", cfg.SandstormTeam)),
		DBSubnetGroupName:        aws.String(fmt.Sprintf("rds-buddy-%s-%s-store-subnet", cfg.SandstormTeam, cfg.Environment)),
		SubnetIds:                subnets,
		Tags:                     subnetTags,
	})

	if err != nil {
		return "", err
	}

	return *output.DBSubnetGroup.DBSubnetGroupName, nil
}

func createBuddyStore(rdsClient rdsiface.RDSAPI, autoscalingClient autoscalingiface.AutoScalingAPI, localInstance *ec2.Instance, sandstormClient sandstorm.SandstormAPI, cfg *config.ConfigFile) (*rds.DBInstance, error) {

	asg, err := awsutil.FetchASG(autoscalingClient, localInstance.InstanceId)
	if err != nil {
		return nil, err
	}

	var subnets []*string
	rawSubnets := strings.Split(*asg.VPCZoneIdentifier, ",")
	for _, subnet := range rawSubnets {
		subnets = append(subnets, aws.String(subnet))
	}

	var securityGroups []*string
	for _, secGroup := range localInstance.SecurityGroups {
		securityGroups = append(securityGroups, secGroup.GroupId)
	}

	masterPassword, err := sandstormClient.GetStorePassword()
	if err != nil || masterPassword == "" {
		masterPassword = sandstorm.GeneratePassword()
		err = sandstormClient.WriteStorePassword(masterPassword)
		if err != nil {
			return nil, err
		}
	}

	subnetName, err := ensureSubnetGroup(rdsClient, subnets, cfg)
	if err != nil {
		return nil, err
	}

	instanceTags := append([]*rds.Tag{
		&rds.Tag{
			Key:   aws.String(fmt.Sprintf(buddyStoreTag, cfg.SandstormTeam)),
			Value: aws.String("true"),
		},
		&rds.Tag{
			Key:   aws.String("rds-buddy-environment"),
			Value: aws.String(cfg.Environment),
		},
	}, awsutil.DefaultRdsTags(cfg.SandstormTeam)...)

	output, err := rdsClient.CreateDBInstance(
		&rds.CreateDBInstanceInput{
			AllocatedStorage:           aws.Int64(200),
			DBInstanceClass:            aws.String("db.r3.large"),
			DBInstanceIdentifier:       aws.String(fmt.Sprintf("rds-buddy-%s-%s-store", cfg.SandstormTeam, cfg.Environment)),
			DBName:                     aws.String("buddy"),
			DBSubnetGroupName:          aws.String(subnetName),
			Engine:                     aws.String("postgres"),
			EngineVersion:              aws.String("9.5.2"),
			MasterUserPassword:         aws.String(masterPassword),
			MasterUsername:             aws.String("buddy"),
			MultiAZ:                    aws.Bool(true),
			PreferredBackupWindow:      aws.String("10:24-10:54"),
			PreferredMaintenanceWindow: aws.String("wed:07:40-wed:08:10"),
			PubliclyAccessible:         aws.Bool(false),
			StorageType:                aws.String("gp2"),
			VpcSecurityGroupIds:        securityGroups,
			Tags:                       instanceTags,
		},
	)
	if err != nil {
		return nil, err
	}

	err = terminal.WaitForCondition("buddy store to be live", 10*time.Minute, 10*time.Second, func() (bool, error) {
		output, err := rdsClient.DescribeDBInstances(&rds.DescribeDBInstancesInput{
			DBInstanceIdentifier: output.DBInstance.DBInstanceIdentifier,
		})
		if err != nil {
			return false, err
		}

		if len(output.DBInstances) < 1 || output.DBInstances[0] == nil {
			return false, nil
		}

		if output.DBInstances[0].DBInstanceStatus == nil || *output.DBInstances[0].DBInstanceStatus == "creating" {
			return false, nil
		}

		return true, nil
	})

	if err != nil {
		return nil, err
	}

	describeOutput, err := rdsClient.DescribeDBInstances(&rds.DescribeDBInstancesInput{
		DBInstanceIdentifier: output.DBInstance.DBInstanceIdentifier,
	})

	if err != nil || len(describeOutput.DBInstances) < 1 || describeOutput.DBInstances[0] == nil {
		return nil, err
	}

	return describeOutput.DBInstances[0], nil
}
