package delete

import (
	"bytes"
	"context"
	"fmt"
	"testing"
	"time"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/credentials"
	"github.com/aws/aws-sdk-go-v2/service/s3"
	"github.com/aws/aws-sdk-go-v2/service/s3/types"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

const (
	testEndpoint  = "http://localhost:8333"
	testAccessKey = "admin"
	testSecretKey = "admin"
	testRegion    = "us-east-1"
)

func getTestClient(t *testing.T) *s3.Client {
	cfg, err := config.LoadDefaultConfig(context.TODO(),
		config.WithRegion(testRegion),
		config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(
			testAccessKey,
			testSecretKey,
			"",
		)),
	)
	require.NoError(t, err)

	client := s3.NewFromConfig(cfg, func(o *s3.Options) {
		o.BaseEndpoint = aws.String(testEndpoint)
		o.UsePathStyle = true
	})

	return client
}

func createTestBucket(t *testing.T, client *s3.Client) string {
	bucketName := fmt.Sprintf("test-multi-delete-%d", time.Now().UnixNano())
	_, err := client.CreateBucket(context.TODO(), &s3.CreateBucketInput{
		Bucket: aws.String(bucketName),
	})
	require.NoError(t, err)

	_, err = client.PutBucketVersioning(context.TODO(), &s3.PutBucketVersioningInput{
		Bucket: aws.String(bucketName),
		VersioningConfiguration: &types.VersioningConfiguration{
			Status: types.BucketVersioningStatusEnabled,
		},
	})
	require.NoError(t, err)

	return bucketName
}

func cleanupBucket(t *testing.T, client *s3.Client, bucket string) {
	listResp, _ := client.ListObjectVersions(context.TODO(), &s3.ListObjectVersionsInput{
		Bucket: aws.String(bucket),
	})

	if listResp != nil {
		var objectsToDelete []types.ObjectIdentifier

		for _, version := range listResp.Versions {
			objectsToDelete = append(objectsToDelete, types.ObjectIdentifier{
				Key:       version.Key,
				VersionId: version.VersionId,
			})
		}

		for _, marker := range listResp.DeleteMarkers {
			objectsToDelete = append(objectsToDelete, types.ObjectIdentifier{
				Key:       marker.Key,
				VersionId: marker.VersionId,
			})
		}

		if len(objectsToDelete) > 0 {
			_, _ = client.DeleteObjects(context.TODO(), &s3.DeleteObjectsInput{
				Bucket: aws.String(bucket),
				Delete: &types.Delete{
					Objects: objectsToDelete,
					Quiet:   aws.Bool(false),
				},
			})
		}
	}

	_, _ = client.DeleteBucket(context.TODO(), &s3.DeleteBucketInput{
		Bucket: aws.String(bucket),
	})
}

func TestVersioningMultiObjectDelete(t *testing.T) {
	client := getTestClient(t)
	bucket := createTestBucket(t, client)
	defer cleanupBucket(t, client, bucket)

	key := "key"
	numVersions := 2
	var versionIds []string

	for i := 0; i < numVersions; i++ {
		content := fmt.Sprintf("content-%d", i)
		putResp, err := client.PutObject(context.TODO(), &s3.PutObjectInput{
			Bucket: aws.String(bucket),
			Key:    aws.String(key),
			Body:   bytes.NewReader([]byte(content)),
		})
		require.NoError(t, err)
		require.NotNil(t, putResp.VersionId)
		versionIds = append(versionIds, *putResp.VersionId)
	}

	assert.Len(t, versionIds, 2)

	var objectsToDelete []types.ObjectIdentifier
	for _, vid := range versionIds {
		objectsToDelete = append(objectsToDelete, types.ObjectIdentifier{
			Key:       aws.String(key),
			VersionId: aws.String(vid),
		})
	}

	deleteResp, err := client.DeleteObjects(context.TODO(), &s3.DeleteObjectsInput{
		Bucket: aws.String(bucket),
		Delete: &types.Delete{
			Objects: objectsToDelete,
		},
	})
	require.NoError(t, err)
	t.Logf("Delete response: Deleted=%d, Errors=%d", len(deleteResp.Deleted), len(deleteResp.Errors))

	listResp, err := client.ListObjectVersions(context.TODO(), &s3.ListObjectVersionsInput{
		Bucket: aws.String(bucket),
	})
	require.NoError(t, err)

	if listResp.Versions != nil && len(listResp.Versions) > 0 {
		t.Errorf("FAIL: Expected no versions, but found %d versions:", len(listResp.Versions))
		for _, v := range listResp.Versions {
			t.Logf("  - Key=%s, VersionId=%s, IsLatest=%v", *v.Key, *v.VersionId, v.IsLatest)
		}
	} else {
		t.Logf("PASS: Versions correctly deleted")
	}
	assert.Nil(t, listResp.Versions, "Expected no versions after deletion")

	deleteResp2, err := client.DeleteObjects(context.TODO(), &s3.DeleteObjectsInput{
		Bucket: aws.String(bucket),
		Delete: &types.Delete{
			Objects: objectsToDelete,
		},
	})
	require.NoError(t, err)
	assert.Empty(t, deleteResp2.Errors, "Idempotent delete should not return errors")
}
