package subcmd

import (
	"context"
	"flag"
	"io/ioutil"
	"log"
	"os"
	"path"
	"strings"

	"code.justin.tv/beefcake/server/internal/client"
	"code.justin.tv/beefcake/server/internal/config"
	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/aws/aws-sdk-go/service/s3/s3iface"
	"github.com/google/subcommands"
)

// UploadLambdas creates a guardian app task definition
type UploadLambdas struct {
	Logger *log.Logger

	buildID string
	inDir   string
	env     string

	config *config.Config
	s3     s3iface.S3API
}

// Name implements subcommands
func (cmd UploadLambdas) Name() string {
	return "upload-lambdas"
}

// Synopsis implements subcommands
func (cmd UploadLambdas) Synopsis() string {
	return "Upload lambda zips."
}

// Usage implements subcommands
func (cmd UploadLambdas) Usage() string {
	return `upload-lambdas -env <env> -in-dir <in-dir>

`
}

// SetFlags implements subcommands
func (cmd *UploadLambdas) SetFlags(f *flag.FlagSet) {
	f.StringVar(&cmd.buildID, "build-id", "", "Git commit to store as metadata.")
	f.StringVar(&cmd.env, "env", "", "Environment to deploy to.")
	f.StringVar(&cmd.inDir, "in-dir", "", "Path to the zip files to deploy.")
}

// Execute implements subcommands
func (cmd UploadLambdas) Execute(ctx context.Context, f *flag.FlagSet, args ...interface{}) subcommands.ExitStatus {
	if err := cmd.init(); err != nil {
		cmd.Logger.Print(err)
		return subcommands.ExitFailure
	}

	if err := cmd.execute(ctx); err != nil {
		cmd.Logger.Print(err)
		return subcommands.ExitFailure
	}

	return subcommands.ExitSuccess
}

func (cmd *UploadLambdas) init() error {
	clientLoader, err := client.EnvironmentLoader(cmd.env)
	if err != nil {
		return err
	}

	clientLoader = clientLoader.
		WithCustomSessionRole(clientLoader.Config().ECSDeployRoleArn.Get())

	cmd.config = clientLoader.Config()
	cmd.s3 = s3.New(clientLoader.Session())
	return nil
}

func (cmd UploadLambdas) execute(ctx context.Context) error {
	lambdas, err := cmd.listLambdas()
	if err != nil {
		return err
	}

	for _, lambda := range lambdas {
		s3Key, err := cmd.config.LambdaS3Key(lambda)
		if err != nil {
			return err
		}

		inPath := path.Join(cmd.inDir, lambda+".zip")

		if err := cmd.uploadLambda(ctx, s3Key, inPath); err != nil {
			return err
		}
	}

	return nil
}

func (cmd UploadLambdas) listLambdas() ([]string, error) {
	dirs, err := ioutil.ReadDir("./lambdas")
	if err != nil {
		return nil, err
	}

	lambdas := make([]string, 0)
	for _, dir := range dirs {
		if strings.HasPrefix(dir.Name(), ".") {
			continue
		}

		lambdas = append(lambdas, dir.Name())
	}
	return lambdas, nil
}

func (cmd UploadLambdas) uploadLambda(ctx context.Context, s3Key, lambdaPath string) (err error) {
	body, err := os.Open(lambdaPath)
	if err != nil {
		return err
	}
	defer func() {
		if subErr := body.Close(); subErr != nil {
			cmd.Logger.Printf("error closing file: %s", err)
			if err == nil {
				err = subErr
			}
		}
	}()

	cmd.Logger.Printf("Uploading %s to %s", lambdaPath, s3Key)
	_, err = cmd.s3.PutObjectWithContext(ctx, &s3.PutObjectInput{
		Body:   body,
		Bucket: aws.String(cmd.config.LambdasS3Bucket.Get()),
		Key:    aws.String(s3Key),
		Metadata: map[string]*string{
			"build-id": aws.String(cmd.buildID),
		},
	})
	return err
}
