package aws

import (
	"context"
	"log"
	"path/filepath"
	"strings"
	"testing"
	"time"

	"github.com/BishopFox/cloudfox/internal"
	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/service/s3"
	"github.com/aws/aws-sdk-go-v2/service/s3/types"
	"github.com/aws/aws-sdk-go-v2/service/sts"
	"github.com/spf13/afero"
)

type MockedS3Client struct {
}

func (m *MockedS3Client) ListBuckets(ctx context.Context, params *s3.ListBucketsInput, optFns ...func(*s3.Options)) (*s3.ListBucketsOutput, error) {
	buckets := make([]types.Bucket, 1)
	mockBucket := types.Bucket{
		CreationDate: aws.Time(time.Now()),
		Name:         aws.String("mockBucket123"),
	}
	buckets[0] = mockBucket
	output := &s3.ListBucketsOutput{
		Buckets: buckets,
	}
	return output, nil

}

func (m *MockedS3Client) GetBucketPolicy(ctx context.Context, params *s3.GetBucketPolicyInput, optFns ...func(*s3.Options)) (*s3.GetBucketPolicyOutput, error) {
	bucketPolicy := `{
		"Version": "2012-10-17",
		"Statement": [
			{
				"Sid": "AddPerm",
				"Effect": "Allow",
				"Principal": "*",
				"Action": "s3:GetObject",
				"Resource": "arn:aws:s3:::examplebucket/*"
			}
		]
	}`
	output := &s3.GetBucketPolicyOutput{
		Policy: aws.String(bucketPolicy),
	}
	return output, nil
}

func (m *MockedS3Client) GetBucketLocation(ctx context.Context, params *s3.GetBucketLocationInput, optFns ...func(*s3.Options)) (*s3.GetBucketLocationOutput, error) {
	locationConstraint := types.BucketLocationConstraint("us-east-2")
	output := &s3.GetBucketLocationOutput{
		LocationConstraint: locationConstraint,
	}
	return output, nil
}

func (m *MockedS3Client) GetPublicAccessBlock(ctx context.Context, params *s3.GetPublicAccessBlockInput, optFns ...func(*s3.Options)) (*s3.GetPublicAccessBlockOutput, error) {
	output := &s3.GetPublicAccessBlockOutput{
		PublicAccessBlockConfiguration: &types.PublicAccessBlockConfiguration{
			BlockPublicAcls:       aws.Bool(true),
			BlockPublicPolicy:     aws.Bool(true),
			IgnorePublicAcls:      aws.Bool(false),
			RestrictPublicBuckets: aws.Bool(true),
		},
	}
	return output, nil
}

func TestListBuckets(t *testing.T) {

	m := BucketsModule{

		S3Client: &MockedS3Client{},
		Caller: sts.GetCallerIdentityOutput{
			Arn:     aws.String("arn:aws:iam::123456789012:user/cloudfox_unit_tests"),
			Account: aws.String("123456789012"),
		},
		AWSRegions:          []string{"us-east-1", "us-west-1", "us-west-2"},
		AWSProfile:          "unittesting",
		AWSOutputType:       "",
		CheckBucketPolicies: true,
		Goroutines:          3,
	}

	subtests := []struct {
		name            string
		outputDirectory string
		verbosity       int
		testModule      BucketsModule
		expectedResult  []BucketRow
	}{
		{
			name:            "test1",
			outputDirectory: ".",
			verbosity:       2,
			testModule:      m,
			expectedResult: []BucketRow{{
				Name: "mockBucket123",
			}},
		},
	}

	fs := internal.MockFileSystem(true)
	defer internal.MockFileSystem(false)
	tmpDir := "."

	for _, subtest := range subtests {
		t.Run(subtest.name, func(t *testing.T) {
			subtest.testModule.PrintBuckets(subtest.outputDirectory, subtest.verbosity)
			for index, expectedBucket := range subtest.expectedResult {
				resultsFilePath := filepath.Join(tmpDir, "cloudfox-output/aws/unittesting-123456789012/table/buckets.txt")
				resultsFile, err := afero.ReadFile(fs, resultsFilePath)
				if err != nil {
					t.Fatalf("Cannot read output file at %s: %s", resultsFilePath, err)
				}

				expectedResults := strings.TrimLeft(`
╭───────────────┬───────────┬─────────┬───────────────────────────╮
│     Name      │  Region   │ Public? │  Resource Policy Summary  │
├───────────────┼───────────┼─────────┼───────────────────────────┤
│ mockBucket123 │ us-east-2 │ YES     │ Everyone can s3:GetObject │
╰───────────────┴───────────┴─────────┴───────────────────────────╯
`, "\n")
				if string(resultsFile) != expectedResults {
					t.Fatalf("Unexpected results:\n%s\n", resultsFile)
				}

				if expectedBucket.Name != subtest.testModule.Buckets[index].Name {
					log.Fatal("Bucket name does not match expected name")
				}

			}
		})
	}
}
