package main

import (
	"context"
	"errors"
	"flag"
	"log"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/service/s3"
	"github.com/aws/smithy-go"
)

const cmdName = "s3-delete-all"

func main() {
	var err error
	ctx := context.Background()

	cfg, err := config.LoadDefaultConfig(ctx)
	if err != nil {
		log.Fatal(err)
	}
	s3Client := s3.NewFromConfig(cfg)

	var bucket string
	flag.StringVar(&bucket, "bucket", "", "name of S3 bucket")
	flag.Parse()
	if bucket == "" {
		log.Fatalf("usage: %s -bucket bucket", cmdName)
	}

	// Delete S3 objects

	listObjectsInput := s3.ListObjectsV2Input{Bucket: aws.String(bucket), MaxKeys: 10_000}
	listObjectsOutput, err := s3Client.ListObjectsV2(ctx, &listObjectsInput)
	if err != nil {
		log.Fatal(err)
	}

	for _, object := range listObjectsOutput.Contents {
		deleteObjectInput := s3.DeleteObjectInput{Bucket: aws.String(bucket), Key: object.Key}
		_, err = s3Client.DeleteObject(ctx, &deleteObjectInput)
		if err != nil {
			log.Fatal(err)
		}

		log.Printf("Deleted object %s", *object.Key)
	}

	// Delete S3 uploads and parts

	listUploadsInput := s3.ListMultipartUploadsInput{Bucket: aws.String(bucket)}
	listUploadsOutput, err := s3Client.ListMultipartUploads(ctx, &listUploadsInput)
	if err != nil {
		log.Fatal(err)
	}
	if listObjectsOutput.IsTruncated {
		log.Fatal("TODO: handle IsTruncated")
	}

	for _, upload := range listUploadsOutput.Uploads {
		abortUploadInput := s3.AbortMultipartUploadInput{Bucket: aws.String(bucket), Key: upload.Key, UploadId: upload.UploadId}
		_, err := s3Client.AbortMultipartUpload(ctx, &abortUploadInput)
		if err != nil {
			log.Fatal(err)
		}

		listPartsInput := s3.ListPartsInput{Bucket: aws.String(bucket), Key: upload.Key, UploadId: upload.UploadId}
		listPartsOutput, err := s3Client.ListParts(ctx, &listPartsInput)
		if err != nil {
			var ae smithy.APIError
			if errors.As(err, &ae) && ae.ErrorCode() == "NoSuchUpload" {
				log.Printf("Ignoring upload not found (id = %s)", *upload.UploadId)
				continue
			}
			log.Fatal(err)
		}
		if len(listPartsOutput.Parts) > 0 {
			log.Fatalf("some parts remain for key %s, upload %s (count = %d)", *upload.UploadId, *upload.Key, len(listPartsOutput.Parts))
		}

		log.Printf("Aborted upload %s", *upload.UploadId)
	}
}
