package s3api

import (
	"context"
	"fmt"
	"testing"

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

// TestListObjectVersionsDelimiter tests delimiter functionality in ListObjectVersions
func TestListObjectVersionsDelimiter(t *testing.T) {
	client := getS3Client(t)
	bucketName := getNewBucketName()

	// Create bucket and enable versioning
	createBucket(t, client, bucketName)
	defer deleteBucket(t, client, bucketName)
	enableVersioning(t, client, bucketName)

	// Create test structure:
	// - folder1/file1.txt
	// - folder1/file2.txt
	// - folder2/file3.txt
	// - root-file.txt
	testObjects := []string{
		"folder1/file1.txt",
		"folder1/file2.txt",
		"folder2/file3.txt",
		"root-file.txt",
	}

	for _, key := range testObjects {
		putObject(t, client, bucketName, key, fmt.Sprintf("Content of %s", key))
	}

	t.Run("Delimiter groups folders correctly", func(t *testing.T) {
		// List with delimiter='/' and no prefix
		// Should return: root-file.txt and CommonPrefixes: folder1/, folder2/
		resp, err := client.ListObjectVersions(context.TODO(), &s3.ListObjectVersionsInput{
			Bucket:    aws.String(bucketName),
			Delimiter: aws.String("/"),
		})
		require.NoError(t, err)

		// Extract keys and prefixes
		versionKeys := make([]string, 0)
		for _, v := range resp.Versions {
			versionKeys = append(versionKeys, *v.Key)
		}

		prefixValues := make([]string, 0)
		for _, p := range resp.CommonPrefixes {
			prefixValues = append(prefixValues, *p.Prefix)
		}

		// Verify results
		assert.Contains(t, versionKeys, "root-file.txt", "Should include root-file.txt")
		assert.Contains(t, prefixValues, "folder1/", "Should include folder1/ prefix")
		assert.Contains(t, prefixValues, "folder2/", "Should include folder2/ prefix")
		assert.NotContains(t, versionKeys, "folder1/file1.txt", "folder1/file1.txt should be grouped under folder1/")
		assert.NotContains(t, versionKeys, "folder2/file3.txt", "folder2/file3.txt should be grouped under folder2/")

		t.Logf("✓ Versions: %v", versionKeys)
		t.Logf("✓ CommonPrefixes: %v", prefixValues)
	})

	t.Run("Prefix filtering with delimiter", func(t *testing.T) {
		// List with delimiter='/' and prefix='folder1/'
		// Should return: folder1/file1.txt, folder1/file2.txt
		resp, err := client.ListObjectVersions(context.TODO(), &s3.ListObjectVersionsInput{
			Bucket:    aws.String(bucketName),
			Prefix:    aws.String("folder1/"),
			Delimiter: aws.String("/"),
		})
		require.NoError(t, err)

		versionKeys := make([]string, 0)
		for _, v := range resp.Versions {
			versionKeys = append(versionKeys, *v.Key)
		}

		assert.Len(t, versionKeys, 2, "Should have 2 versions")
		assert.Contains(t, versionKeys, "folder1/file1.txt")
		assert.Contains(t, versionKeys, "folder1/file2.txt")
		assert.Empty(t, resp.CommonPrefixes, "Should have no common prefixes")

		t.Logf("✓ Prefix filtering works: %v", versionKeys)
	})

	t.Run("Without delimiter returns all versions", func(t *testing.T) {
		// List without delimiter - should return all files
		resp, err := client.ListObjectVersions(context.TODO(), &s3.ListObjectVersionsInput{
			Bucket: aws.String(bucketName),
		})
		require.NoError(t, err)

		assert.Len(t, resp.Versions, 4, "Should have all 4 versions")
		assert.Empty(t, resp.CommonPrefixes, "Should have no common prefixes without delimiter")

		t.Logf("✓ Without delimiter returns all %d versions", len(resp.Versions))
	})
}

// TestListObjectVersionsDelimiterTruncation tests MaxKeys with delimiter
func TestListObjectVersionsDelimiterTruncation(t *testing.T) {
	client := getS3Client(t)
	bucketName := getNewBucketName()

	// Create bucket and enable versioning
	createBucket(t, client, bucketName)
	defer deleteBucket(t, client, bucketName)
	enableVersioning(t, client, bucketName)

	// Create multiple folders to test truncation
	for i := 0; i < 5; i++ {
		key := fmt.Sprintf("folder%d/file.txt", i)
		putObject(t, client, bucketName, key, fmt.Sprintf("Content %d", i))
	}
	// Add a root file
	putObject(t, client, bucketName, "root.txt", "Root content")

	t.Run("MaxKeys limits total items", func(t *testing.T) {
		resp, err := client.ListObjectVersions(context.TODO(), &s3.ListObjectVersionsInput{
			Bucket:    aws.String(bucketName),
			Delimiter: aws.String("/"),
			MaxKeys:   aws.Int32(3),
		})
		require.NoError(t, err)

		assert.NotNil(t, resp.IsTruncated, "IsTruncated should not be nil")
		assert.True(t, *resp.IsTruncated, "Should be truncated")
		assert.NotNil(t, resp.NextKeyMarker, "Should have NextKeyMarker for pagination")

		count := len(resp.Versions) + len(resp.CommonPrefixes)
		t.Logf("✓ MaxKeys truncation: %d items (versions: %d, prefixes: %d)",
			count, len(resp.Versions), len(resp.CommonPrefixes))
	})

	t.Run("Pagination with delimiter", func(t *testing.T) {
		// Collect all items through pagination
		allKeys := make([]string, 0)
		allPrefixes := make([]string, 0)
		var keyMarker *string
		var versionMarker *string

		for {
			input := &s3.ListObjectVersionsInput{
				Bucket:    aws.String(bucketName),
				Delimiter: aws.String("/"),
				MaxKeys:   aws.Int32(2),
			}
			if keyMarker != nil {
				input.KeyMarker = keyMarker
			}
			if versionMarker != nil {
				input.VersionIdMarker = versionMarker
			}

			resp, err := client.ListObjectVersions(context.TODO(), input)
			require.NoError(t, err)

			// Collect versions
			for _, v := range resp.Versions {
				allKeys = append(allKeys, *v.Key)
			}

			// Collect prefixes
			for _, p := range resp.CommonPrefixes {
				allPrefixes = append(allPrefixes, *p.Prefix)
			}

			require.NotNil(t, resp.IsTruncated, "IsTruncated should not be nil")
			if !*resp.IsTruncated {
				break
			}

			keyMarker = resp.NextKeyMarker
			versionMarker = resp.NextVersionIdMarker
		}

		// Should have collected all items
		itemsCount := len(allKeys) + len(allPrefixes)
		assert.GreaterOrEqual(t, itemsCount, 6, "Should collect all items through pagination")

		t.Logf("✓ Pagination collected %d total items (keys: %d, prefixes: %d)",
			itemsCount, len(allKeys), len(allPrefixes))
	})

	t.Run("CommonPrefixes are filtered by keyMarker (exclusive)", func(t *testing.T) {
		// List with keyMarker that should skip some prefixes
		// We have folder0/, folder1/, folder2/, folder3/, folder4/
		// Setting keyMarker to "folder2/" should return folder3/, folder4/ and root.txt (if it's > folder2/)
		resp, err := client.ListObjectVersions(context.TODO(), &s3.ListObjectVersionsInput{
			Bucket:    aws.String(bucketName),
			Delimiter: aws.String("/"),
			KeyMarker: aws.String("folder2/"),
		})
		require.NoError(t, err)

		prefixValues := make([]string, 0)
		for _, p := range resp.CommonPrefixes {
			prefixValues = append(prefixValues, *p.Prefix)
		}

		assert.NotContains(t, prefixValues, "folder0/", "Should skip folder0/")
		assert.NotContains(t, prefixValues, "folder1/", "Should skip folder1/")
		assert.NotContains(t, prefixValues, "folder2/", "Should skip folder2/ (exclusive marker)")
		assert.Contains(t, prefixValues, "folder3/", "Should include folder3/")
		assert.Contains(t, prefixValues, "folder4/", "Should include folder4/")

		t.Logf("✓ CommonPrefixes filtered by keyMarker: %v", prefixValues)
	})
}

// TestListObjectVersionsDelimiterWithMultipleVersions tests delimiter with multiple versions of same object
func TestListObjectVersionsDelimiterWithMultipleVersions(t *testing.T) {
	client := getS3Client(t)
	bucketName := getNewBucketName()

	// Create bucket and enable versioning
	createBucket(t, client, bucketName)
	defer deleteBucket(t, client, bucketName)
	enableVersioning(t, client, bucketName)

	// Create multiple versions of objects in different folders
	for i := 1; i <= 3; i++ {
		putObject(t, client, bucketName, "folder1/file.txt", fmt.Sprintf("Version %d", i))
		putObject(t, client, bucketName, "folder2/file.txt", fmt.Sprintf("Version %d", i))
	}

	t.Run("Delimiter groups all versions under prefix", func(t *testing.T) {
		resp, err := client.ListObjectVersions(context.TODO(), &s3.ListObjectVersionsInput{
			Bucket:    aws.String(bucketName),
			Delimiter: aws.String("/"),
		})
		require.NoError(t, err)

		// Should have no versions at root level, only common prefixes
		assert.Empty(t, resp.Versions, "Should have no versions at root")
		assert.Len(t, resp.CommonPrefixes, 2, "Should have 2 common prefixes")

		prefixValues := make([]string, 0)
		for _, p := range resp.CommonPrefixes {
			prefixValues = append(prefixValues, *p.Prefix)
		}
		assert.Contains(t, prefixValues, "folder1/")
		assert.Contains(t, prefixValues, "folder2/")

		t.Logf("✓ All versions grouped under prefixes: %v", prefixValues)
	})

	t.Run("Listing within prefix shows all versions", func(t *testing.T) {
		resp, err := client.ListObjectVersions(context.TODO(), &s3.ListObjectVersionsInput{
			Bucket:    aws.String(bucketName),
			Prefix:    aws.String("folder1/"),
			Delimiter: aws.String("/"),
		})
		require.NoError(t, err)

		assert.Len(t, resp.Versions, 3, "Should have 3 versions of folder1/file.txt")
		assert.Empty(t, resp.CommonPrefixes, "Should have no common prefixes")

		// Verify all versions are for the same key
		for _, v := range resp.Versions {
			assert.Equal(t, "folder1/file.txt", *v.Key)
		}

		t.Logf("✓ Found %d versions within prefix", len(resp.Versions))
	})
}
