package aws

import (
	"context"
	"fmt"
	"path/filepath"
	"strings"
	"testing"

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

type mockedCodeBuildClient struct {
}

func (m *mockedCodeBuildClient) ListProjects(ctx context.Context, params *codebuild.ListProjectsInput, optFns ...func(*codebuild.Options)) (*codebuild.ListProjectsOutput, error) {
	out := codebuild.ListProjectsOutput{
		Projects: []string{"test1", "test2"},
	}

	return &out, nil
}

func (m *mockedCodeBuildClient) BatchGetProjects(ctx context.Context, params *codebuild.BatchGetProjectsInput, optFns ...func(*codebuild.Options)) (*codebuild.BatchGetProjectsOutput, error) {
	project1out := codebuild.BatchGetProjectsOutput{
		Projects: []types.Project{
			{
				Name: aws.String("test1"),
				Environment: &types.ProjectEnvironment{
					Image: aws.String("aws/codebuild/standard:4.0"),
				},
				ServiceRole: aws.String("arn:aws:iam::123456789012:role/test1"),
				Source: &types.ProjectSource{
					Type:     "CODECOMMIT",
					Location: aws.String("https://git-codecommit.us-east-1.amazonaws.com/v1/repos/test1"),
				},
				Artifacts: &types.ProjectArtifacts{
					Type:     "S3",
					Location: aws.String("s3://test1"),
				},
				TimeoutInMinutes: aws.Int32(60),
			},
		},
	}

	project2out := codebuild.BatchGetProjectsOutput{
		Projects: []types.Project{
			{
				Name: aws.String("test2"),
				Environment: &types.ProjectEnvironment{
					Image: aws.String("aws/codebuild/standard:4.0"),
				},
				ServiceRole: aws.String("arn:aws:iam::123456789012:role/test2"),
				Source: &types.ProjectSource{
					Type:     "GITHUB",
					Location: aws.String("https://test.github.com/test2"),
				},
				Artifacts: &types.ProjectArtifacts{
					Type:     "S3",
					Location: aws.String("s3://test2"),
				},
				TimeoutInMinutes: aws.Int32(60),
			},
		},
	}

	if params.Names[0] == "test1" {
		return &project1out, nil
	} else if params.Names[0] == "test2" {
		return &project2out, nil
	} else {
		return nil, nil
	}
}

func (m *mockedCodeBuildClient) GetResourcePolicy(ctx context.Context, params *codebuild.GetResourcePolicyInput, optFns ...func(*codebuild.Options)) (*codebuild.GetResourcePolicyOutput, error) {
	out := codebuild.GetResourcePolicyOutput{
		Policy: aws.String(`{
			"Version": "2012-10-17",
			"Statement": [
				{
					"Effect": "Allow",
					"Principal": {
						"AWS": "*"
					},
					"Action": [
					"codebuild:StartBuild",
					"codebuild:BatchGetBuilds",
					"codebuild:StopBuild",
					"codebuild:ListProjects",
					"codebuild:BatchGetProjects"
					],
					"Resource": "*"
				}
			]
		}`),
	}

	return &out, nil
}

func TestCodeBuildProjects(t *testing.T) {

	m := CodeBuildModule{
		CodeBuildClient: &mockedCodeBuildClient{},
		AWSProfile:      "unittesting",
		AWSRegions:      []string{"us-west-1"},
		Caller: sts.GetCallerIdentityOutput{
			Arn:     aws.String("arn:aws:iam::123456789012:user/Alice"),
			Account: aws.String("123456789012"),
		},
		Goroutines:     3,
		WrapTable:      true,
		SkipAdminCheck: true,
	}

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

	// execute the module with verbosity set to 2
	m.PrintCodeBuildProjects(tmpDir, 2)

	resultsFilePath := filepath.Join(tmpDir, "cloudfox-output/aws/unittesting-123456789012/table/codebuild.txt")
	resultsFile, err := afero.ReadFile(fs, resultsFilePath)
	if err != nil {
		t.Fatalf("Cannot read output file at %s: %s", resultsFilePath, err)
	}

	expectedResults := strings.TrimLeft(`
╭───────────┬───────┬──────────────────────────────────────┬──────────────╮
│  Region   │ Name  │                 Role                 │ IsAdminRole? │
├───────────┼───────┼──────────────────────────────────────┼──────────────┤
│ us-west-1 │ test1 │ arn:aws:iam::123456789012:role/test1 │ Skipped      │
│ us-west-1 │ test2 │ arn:aws:iam::123456789012:role/test2 │ Skipped      │
╰───────────┴───────┴──────────────────────────────────────┴──────────────╯
`, "\n")
	fmt.Println(expectedResults)
	fmt.Println("results file")
	fmt.Println(string(resultsFile))

	if string(resultsFile) != expectedResults {
		t.Fatalf("Unexpected results:\n%s\n", resultsFile)
	}
}
