package yandexexport

import (
	"context"
	"fmt"
	"log"
	"net/url"
	"path/filepath"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/aws/session"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/hashicorp/packer/builder/yandex"
	"github.com/hashicorp/packer/packer-plugin-sdk/multistep"
	"github.com/hashicorp/packer/packer-plugin-sdk/packer"
	"github.com/yandex-cloud/go-genproto/yandex/cloud/iam/v1/awscompatibility"
)

type StepCreateS3Keys struct {
	ServiceAccountID string
	Paths            []string
}

func (c *StepCreateS3Keys) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
	driver := state.Get("driver").(yandex.Driver)
	ui := state.Get("ui").(packer.Ui)

	ui.Say("Create temporary storage Access Key")
	// Create temporary storage Access Key
	respWithKey, err := driver.SDK().IAM().AWSCompatibility().AccessKey().Create(ctx, &awscompatibility.CreateAccessKeyRequest{
		ServiceAccountId: c.ServiceAccountID,
		Description:      "this temporary key is for upload image to storage; created by Packer",
	})
	if err != nil {
		err := fmt.Errorf("Error waiting for cloud-init script to finish: %s", err)
		return yandex.StepHaltWithError(state, err)
	}
	state.Put("s3_secret", respWithKey)

	ui.Say("Verify access to paths")
	if err := verfiyAccess(respWithKey.GetAccessKey().GetKeyId(), respWithKey.Secret, c.Paths); err != nil {
		return yandex.StepHaltWithError(state, err)
	}
	return multistep.ActionContinue
}

func (s *StepCreateS3Keys) Cleanup(state multistep.StateBag) {
	driver := state.Get("driver").(yandex.Driver)
	ui := state.Get("ui").(packer.Ui)

	if val, ok := state.GetOk("s3_secret"); ok {
		ui.Say("S3 secrets have been found")
		s3Secret := val.(*awscompatibility.CreateAccessKeyResponse)

		ui.Message("Cleanup empty objects...")
		cleanUpEmptyObjects(s3Secret.GetAccessKey().GetKeyId(), s3Secret.GetSecret(), s.Paths)

		ui.Say("Delete S3 secrets...")
		_, err := driver.SDK().IAM().AWSCompatibility().AccessKey().Delete(context.Background(), &awscompatibility.DeleteAccessKeyRequest{
			AccessKeyId: s3Secret.GetAccessKey().GetId(),
		})

		if err != nil {
			ui.Error(err.Error())
		}
	}
}

func verfiyAccess(keyID, secret string, paths []string) error {
	newSession, err := session.NewSession(&aws.Config{
		Endpoint: aws.String(defaultStorageEndpoint),
		Region:   aws.String(defaultStorageRegion),
		Credentials: credentials.NewStaticCredentials(
			keyID, secret, "",
		),
	})
	if err != nil {
		return err
	}

	s3Conn := s3.New(newSession)

	for _, path := range paths {
		u, err := url.Parse(path)
		if err != nil {
			return err
		}
		key := u.Path
		if strings.HasSuffix(key, "/") {
			key = filepath.Join(key, "disk.qcow2")
		}
		_, err = s3Conn.PutObject(&s3.PutObjectInput{
			Body:   aws.ReadSeekCloser(strings.NewReader("")),
			Bucket: aws.String(u.Host),
			Key:    aws.String(key),
		})
		if err != nil {
			return err
		}
	}

	return nil
}

func cleanUpEmptyObjects(keyID, secret string, paths []string) {
	newSession, err := session.NewSession(&aws.Config{
		Endpoint: aws.String(defaultStorageEndpoint),
		Region:   aws.String(defaultStorageRegion),
		Credentials: credentials.NewStaticCredentials(
			keyID, secret, "",
		),
	})
	if err != nil {
		log.Printf("[WARN] %s", err)
		return
	}
	s3Conn := s3.New(newSession)

	for _, path := range paths {
		u, err := url.Parse(path)
		if err != nil {
			log.Printf("[WARN] %s", err)
			continue
		}
		key := u.Path
		if strings.HasSuffix(key, "/") {
			key = filepath.Join(key, "disk.qcow2")
		}

		log.Printf("Check object: '%s'", path)
		respHead, err := s3Conn.HeadObject(&s3.HeadObjectInput{
			Bucket: aws.String(u.Host),
			Key:    aws.String(key),
		})
		if err != nil {
			log.Printf("[WARN] %s", err)
			continue
		}
		if *respHead.ContentLength > 0 {
			continue
		}

		log.Printf("[DEBUG] Delete object: '%s'", path)
		_, err = s3Conn.DeleteObject(&s3.DeleteObjectInput{
			Bucket: aws.String(u.Host),
			Key:    aws.String(key),
		})
		if err != nil {
			log.Printf("[WARN] %s", err)
		}
	}
}
