// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

package main

import (
	"context"
	"testing"

	tfe "github.com/hashicorp/go-tfe"
	tfversion "github.com/hashicorp/terraform/version"
)

func Test_migrate_multi_to_tfc_cloud_name_strategy(t *testing.T) {
	t.Parallel()
	skipIfMissingEnvVar(t)
	skipWithoutRemoteTerraformVersion(t)

	ctx := context.Background()

	cases := testCases{
		"migrating multiple workspaces to cloud using name strategy; current workspace is 'default'": {
			operations: []operationSets{
				{
					prep: func(t *testing.T, orgName, dir string) {
						tfBlock := terraformConfigLocalBackend()
						writeMainTF(t, tfBlock, dir)
					},
					commands: []tfCommand{
						{
							command:           []string{"init"},
							expectedCmdOutput: `Successfully configured the backend "local"!`,
						},
						{
							command:         []string{"apply", "-auto-approve"},
							postInputOutput: []string{`Apply complete!`},
						},
						{
							command:           []string{"workspace", "new", "prod"},
							expectedCmdOutput: `Created and switched to workspace "prod"!`,
						},
						{
							command:         []string{"apply", "-auto-approve"},
							postInputOutput: []string{`Apply complete!`},
						},
						{
							command:           []string{"workspace", "select", "default"},
							expectedCmdOutput: `Switched to workspace "default".`,
						},
					},
				},
				{
					prep: func(t *testing.T, orgName, dir string) {
						wsName := "new-workspace"
						tfBlock := terraformConfigCloudBackendName(orgName, wsName)
						writeMainTF(t, tfBlock, dir)
					},
					commands: []tfCommand{
						{
							command:           []string{"init"},
							expectedCmdOutput: `Do you want to copy only your current workspace?`,
							userInput:         []string{"yes"},
							postInputOutput:   []string{`HCP Terraform has been successfully initialized!`},
						},
						{
							command:           []string{"workspace", "show"},
							expectedCmdOutput: `new-workspace`, // this comes from the `prep` function
						},
						{
							command:           []string{"output"},
							expectedCmdOutput: `val = "default"`, // this was the output of the current workspace selected before migration
						},
					},
				},
			},
			validations: func(t *testing.T, orgName string) {
				wsList, err := tfeClient.Workspaces.List(ctx, orgName, nil)
				if err != nil {
					t.Fatal(err)
				}
				if len(wsList.Items) != 1 {
					t.Fatalf("Expected the number of workspaces to be 1, but got %d", len(wsList.Items))
				}
				ws := wsList.Items[0]
				// this workspace name is what exists in the cloud backend configuration block
				if ws.Name != "new-workspace" {
					t.Fatalf("Expected workspace to be `new-workspace`, but is %s", ws.Name)
				}
			},
		},
		"migrating multiple workspaces to cloud using name strategy; current workspace is 'prod'": {
			operations: []operationSets{
				{
					prep: func(t *testing.T, orgName, dir string) {
						tfBlock := terraformConfigLocalBackend()
						writeMainTF(t, tfBlock, dir)
					},
					commands: []tfCommand{
						{
							command:           []string{"init"},
							expectedCmdOutput: `Successfully configured the backend "local"!`,
						},
						{
							command:         []string{"apply", "-auto-approve"},
							postInputOutput: []string{`Apply complete!`},
						},
						{
							command:           []string{"workspace", "new", "prod"},
							expectedCmdOutput: `Created and switched to workspace "prod"!`,
						},
						{
							command:         []string{"apply", "-auto-approve"},
							postInputOutput: []string{`Apply complete!`},
						},
					},
				},
				{
					prep: func(t *testing.T, orgName, dir string) {
						wsName := "new-workspace"
						tfBlock := terraformConfigCloudBackendName(orgName, wsName)
						writeMainTF(t, tfBlock, dir)
					},
					commands: []tfCommand{
						{
							command:           []string{"init"},
							expectedCmdOutput: `Do you want to copy only your current workspace?`,
							userInput:         []string{"yes"},
							postInputOutput:   []string{`HCP Terraform has been successfully initialized!`},
						},
						{
							command:           []string{"workspace", "list"},
							expectedCmdOutput: `new-workspace`, // this comes from the `prep` function
						},
						{
							command:           []string{"output"},
							expectedCmdOutput: `val = "prod"`,
						},
					},
				},
			},
			validations: func(t *testing.T, orgName string) {
				wsList, err := tfeClient.Workspaces.List(ctx, orgName, nil)
				if err != nil {
					t.Fatal(err)
				}
				ws := wsList.Items[0]
				// this workspace name is what exists in the cloud backend configuration block
				if ws.Name != "new-workspace" {
					t.Fatalf("Expected workspace to be `new-workspace`, but is %s", ws.Name)
				}
			},
		},
		"migrating multiple workspaces to cloud using name strategy; 'default' workspace is empty": {
			operations: []operationSets{
				{
					prep: func(t *testing.T, orgName, dir string) {
						tfBlock := terraformConfigLocalBackend()
						writeMainTF(t, tfBlock, dir)
					},
					commands: []tfCommand{
						{
							command:           []string{"init"},
							expectedCmdOutput: `Successfully configured the backend "local"!`,
						},
						{
							command:           []string{"workspace", "new", "workspace1"},
							expectedCmdOutput: `Created and switched to workspace "workspace1"!`,
						},
						{
							command:         []string{"apply", "-auto-approve"},
							postInputOutput: []string{`Apply complete!`},
						},
						{
							command:           []string{"workspace", "new", "workspace2"},
							expectedCmdOutput: `Created and switched to workspace "workspace2"!`,
						},
						{
							command:         []string{"apply", "-auto-approve"},
							postInputOutput: []string{`Apply complete!`},
						},
					},
				},
				{
					prep: func(t *testing.T, orgName, dir string) {
						wsName := "new-workspace"
						tfBlock := terraformConfigCloudBackendName(orgName, wsName)
						writeMainTF(t, tfBlock, dir)
					},
					commands: []tfCommand{
						{
							command:           []string{"init"},
							expectedCmdOutput: `Do you want to copy only your current workspace?`,
							userInput:         []string{"yes"},
							postInputOutput:   []string{`HCP Terraform has been successfully initialized!`},
						},
						{
							command:     []string{"workspace", "select", "default"},
							expectError: true,
						},
						{
							command:           []string{"output"},
							expectedCmdOutput: `val = "workspace2"`, // this was the output of the current workspace selected before migration
						},
					},
				},
			},
			validations: func(t *testing.T, orgName string) {
				wsList, err := tfeClient.Workspaces.List(ctx, orgName, nil)
				if err != nil {
					t.Fatal(err)
				}
				if len(wsList.Items) != 1 {
					t.Fatalf("Expected the number of workspaces to be 1, but got %d", len(wsList.Items))
				}
				ws := wsList.Items[0]
				// this workspace name is what exists in the cloud backend configuration block
				if ws.Name != "new-workspace" {
					t.Fatalf("Expected workspace to be `new-workspace`, but is %s", ws.Name)
				}
			},
		},
	}

	testRunner(t, cases, 1)
}

func Test_migrate_multi_to_tfc_cloud_tags_strategy(t *testing.T) {
	t.Parallel()
	skipIfMissingEnvVar(t)
	skipWithoutRemoteTerraformVersion(t)

	ctx := context.Background()

	cases := map[string]struct {
		operations  []operationSets
		validations func(t *testing.T, orgName string)
	}{
		"migrating multiple workspaces to cloud using tags strategy; pattern is using prefix `app-*`": {
			operations: []operationSets{
				{
					prep: func(t *testing.T, orgName, dir string) {
						tfBlock := terraformConfigLocalBackend()
						writeMainTF(t, tfBlock, dir)
					},
					commands: []tfCommand{
						{
							command:           []string{"init"},
							expectedCmdOutput: `Successfully configured the backend "local"!`,
						},
						{
							command:         []string{"apply", "-auto-approve"},
							postInputOutput: []string{`Apply complete!`},
						},
						{
							command:           []string{"workspace", "new", "prod"},
							expectedCmdOutput: `Created and switched to workspace "prod"!`,
						},
						{
							command:         []string{"apply", "-auto-approve"},
							postInputOutput: []string{`Apply complete!`},
						},
						{
							command:           []string{"workspace", "select", "default"},
							expectedCmdOutput: `Switched to workspace "default".`,
						},
						{
							command:           []string{"output"},
							expectedCmdOutput: `val = "default"`,
						},
						{
							command:           []string{"workspace", "select", "prod"},
							expectedCmdOutput: `Switched to workspace "prod".`,
						},
						{
							command:           []string{"output"},
							expectedCmdOutput: `val = "prod"`,
						},
					},
				},
				{
					prep: func(t *testing.T, orgName, dir string) {
						tag := "app"
						tfBlock := terraformConfigCloudBackendTags(orgName, tag)
						writeMainTF(t, tfBlock, dir)
					},
					commands: []tfCommand{
						{
							command:           []string{"init"},
							expectedCmdOutput: `HCP Terraform requires all workspaces to be given an explicit name.`,
							userInput:         []string{"dev", "1", "app-*"},
							postInputOutput: []string{
								`Would you like to rename your workspaces?`,
								"How would you like to rename your workspaces?",
								"HCP Terraform has been successfully initialized!"},
						},
						{
							command:           []string{"workspace", "select", "app-dev"},
							expectedCmdOutput: `Switched to workspace "app-dev".`,
						},
						{
							command:           []string{"output"},
							expectedCmdOutput: `val = "default"`,
						},
						{
							command:           []string{"workspace", "select", "app-prod"},
							expectedCmdOutput: `Switched to workspace "app-prod".`,
						},
						{
							command:           []string{"output"},
							expectedCmdOutput: `val = "prod"`,
						},
					},
				},
			},
			validations: func(t *testing.T, orgName string) {
				wsList, err := tfeClient.Workspaces.List(ctx, orgName, &tfe.WorkspaceListOptions{
					Tags: "app",
				})
				if err != nil {
					t.Fatal(err)
				}
				if len(wsList.Items) != 2 {
					t.Fatalf("Expected the number of workspaecs to be 2, but got %d", len(wsList.Items))
				}
				expectedWorkspaceNames := []string{"app-prod", "app-dev"}
				for _, ws := range wsList.Items {
					hasName := false
					for _, expectedNames := range expectedWorkspaceNames {
						if expectedNames == ws.Name {
							hasName = true
						}
					}
					if !hasName {
						t.Fatalf("Worksapce %s is not in the expected list of workspaces", ws.Name)
					}
				}
			},
		},
		"migrating multiple workspaces to cloud using tags strategy; existing workspaces": {
			operations: []operationSets{
				{
					prep: func(t *testing.T, orgName, dir string) {
						tfBlock := terraformConfigLocalBackend()
						writeMainTF(t, tfBlock, dir)
					},
					commands: []tfCommand{
						{
							command:           []string{"init"},
							expectedCmdOutput: `Successfully configured the backend "local"!`,
						},
						{
							command:         []string{"apply", "-auto-approve"},
							postInputOutput: []string{`Apply complete!`},
						},
						{
							command:           []string{"workspace", "new", "identity"},
							expectedCmdOutput: `Created and switched to workspace "identity"!`,
						},
						{
							command:         []string{"apply", "-auto-approve"},
							postInputOutput: []string{`Apply complete!`},
						},
						{
							command:           []string{"workspace", "new", "billing"},
							expectedCmdOutput: `Created and switched to workspace "billing"!`,
						},
						{
							command:         []string{"apply", "-auto-approve"},
							postInputOutput: []string{`Apply complete!`},
						},
						{
							command:           []string{"workspace", "select", "default"},
							expectedCmdOutput: `Switched to workspace "default".`,
						},
					},
				},
				{
					prep: func(t *testing.T, orgName, dir string) {
						tag := "app"
						_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{
							Name:             tfe.String("identity"),
							TerraformVersion: tfe.String(tfversion.String()),
						})
						_ = createWorkspace(t, orgName, tfe.WorkspaceCreateOptions{
							Name:             tfe.String("billing"),
							TerraformVersion: tfe.String(tfversion.String()),
						})
						tfBlock := terraformConfigCloudBackendTags(orgName, tag)
						writeMainTF(t, tfBlock, dir)
					},
					commands: []tfCommand{
						{
							command:           []string{"init"},
							expectedCmdOutput: `HCP Terraform requires all workspaces to be given an explicit name.`,
							userInput:         []string{"dev", "1", "app-*"},
							postInputOutput: []string{
								`Would you like to rename your workspaces?`,
								"How would you like to rename your workspaces?",
								"HCP Terraform has been successfully initialized!"},
						},
						{
							command:           []string{"workspace", "select", "app-billing"},
							expectedCmdOutput: `Switched to workspace "app-billing".`,
						},
						{
							command:           []string{"workspace", "select", "app-identity"},
							expectedCmdOutput: `Switched to workspace "app-identity".`,
						},
						{
							command:           []string{"workspace", "select", "app-dev"},
							expectedCmdOutput: `Switched to workspace "app-dev".`,
						},
					},
				},
			},
			validations: func(t *testing.T, orgName string) {
				wsList, err := tfeClient.Workspaces.List(ctx, orgName, &tfe.WorkspaceListOptions{
					Tags: "app",
				})
				if err != nil {
					t.Fatal(err)
				}
				if len(wsList.Items) != 3 {
					t.Fatalf("Expected the number of workspaecs to be 3, but got %d", len(wsList.Items))
				}
				expectedWorkspaceNames := []string{"app-billing", "app-dev", "app-identity"}
				for _, ws := range wsList.Items {
					hasName := false
					for _, expectedNames := range expectedWorkspaceNames {
						if expectedNames == ws.Name {
							hasName = true
						}
					}
					if !hasName {
						t.Fatalf("Worksapce %s is not in the expected list of workspaces", ws.Name)
					}
				}
			},
		},
	}

	testRunner(t, cases, 1)
}
