package cmd

import (
	"io/ioutil"
	"os"
	"testing"

	"github.com/SAP/jenkins-library/pkg/cloudfoundry"
	"github.com/SAP/jenkins-library/pkg/mock"
	"github.com/SAP/jenkins-library/pkg/telemetry"
	"github.com/stretchr/testify/assert"
)

func cfMockCleanup(m *mock.ExecMockRunner) {
	m.ShouldFailOnCommand = map[string]error{}
	m.StdoutReturn = map[string]string{}
	m.Calls = []mock.ExecCall{}
}

func TestCloudFoundryCreateService(t *testing.T) {

	m := &mock.ExecMockRunner{}
	cf := cloudfoundry.CFUtils{Exec: m}

	var telemetryData telemetry.CustomData
	t.Run("Create service: no broker, no config, no tags", func(t *testing.T) {
		defer cfMockCleanup(m)
		config := cloudFoundryCreateServiceOptions{
			CfAPIEndpoint:         "https://api.endpoint.com",
			CfOrg:                 "testOrg",
			CfSpace:               "testSpace",
			Username:              "testUser",
			Password:              "testPassword",
			CfService:             "testService",
			CfServiceInstanceName: "testName",
			CfServicePlan:         "testPlan",
		}
		error := runCloudFoundryCreateService(&config, &telemetryData, cf)
		if assert.NoError(t, error) {
			assert.Equal(t, []mock.ExecCall{{Execution: (*mock.Execution)(nil), Async: false, Exec: "cf", Params: []string{"login", "-a", "https://api.endpoint.com", "-o", "testOrg", "-s", "testSpace", "-u", "testUser", "-p", "testPassword"}},
				{Execution: (*mock.Execution)(nil), Async: false, Exec: "cf", Params: []string{"create-service", "testService", "testPlan", "testName"}},
				{Execution: (*mock.Execution)(nil), Async: false, Exec: "cf", Params: []string{"logout"}}},
				m.Calls)
		}
	})
	t.Run("Create service: only tags", func(t *testing.T) {
		defer cfMockCleanup(m)

		config := cloudFoundryCreateServiceOptions{
			CfAPIEndpoint:         "https://api.endpoint.com",
			CfOrg:                 "testOrg",
			CfSpace:               "testSpace",
			Username:              "testUser",
			Password:              "testPassword",
			CfService:             "testService",
			CfServiceInstanceName: "testName",
			CfServicePlan:         "testPlan",
			CfServiceTags:         "testTag, testTag2",
		}
		error := runCloudFoundryCreateService(&config, &telemetryData, cf)
		if assert.NoError(t, error) {
			assert.Equal(t, []mock.ExecCall{{Execution: (*mock.Execution)(nil), Async: false, Exec: "cf", Params: []string{"login", "-a", "https://api.endpoint.com", "-o", "testOrg", "-s", "testSpace", "-u", "testUser", "-p", "testPassword"}},
				{Execution: (*mock.Execution)(nil), Async: false, Exec: "cf", Params: []string{"create-service", "testService", "testPlan", "testName", "-t", "testTag, testTag2"}},
				{Execution: (*mock.Execution)(nil), Async: false, Exec: "cf", Params: []string{"logout"}}},
				m.Calls)
		}
	})
	t.Run("Create service: only broker", func(t *testing.T) {
		defer cfMockCleanup(m)
		config := cloudFoundryCreateServiceOptions{
			CfAPIEndpoint:         "https://api.endpoint.com",
			CfOrg:                 "testOrg",
			CfSpace:               "testSpace",
			Username:              "testUser",
			Password:              "testPassword",
			CfService:             "testService",
			CfServiceInstanceName: "testName",
			CfServicePlan:         "testPlan",
			CfServiceBroker:       "testBroker",
		}
		error := runCloudFoundryCreateService(&config, &telemetryData, cf)
		if assert.NoError(t, error) {
			assert.Equal(t, []mock.ExecCall{{Execution: (*mock.Execution)(nil), Async: false, Exec: "cf", Params: []string{"login", "-a", "https://api.endpoint.com", "-o", "testOrg", "-s", "testSpace", "-u", "testUser", "-p", "testPassword"}},
				{Execution: (*mock.Execution)(nil), Async: false, Exec: "cf", Params: []string{"create-service", "testService", "testPlan", "testName", "-b", "testBroker"}},
				{Execution: (*mock.Execution)(nil), Async: false, Exec: "cf", Params: []string{"logout"}}},
				m.Calls)
		}
	})
	t.Run("Create service: only config", func(t *testing.T) {
		defer cfMockCleanup(m)
		config := cloudFoundryCreateServiceOptions{
			CfAPIEndpoint:         "https://api.endpoint.com",
			CfOrg:                 "testOrg",
			CfSpace:               "testSpace",
			Username:              "testUser",
			Password:              "testPassword",
			CfService:             "testService",
			CfServiceInstanceName: "testName",
			CfServicePlan:         "testPlan",
			CfCreateServiceConfig: "testConfig.json",
		}
		error := runCloudFoundryCreateService(&config, &telemetryData, cf)
		if assert.NoError(t, error) {
			assert.Equal(t, []mock.ExecCall{{Execution: (*mock.Execution)(nil), Async: false, Exec: "cf", Params: []string{"login", "-a", "https://api.endpoint.com", "-o", "testOrg", "-s", "testSpace", "-u", "testUser", "-p", "testPassword"}},
				{Execution: (*mock.Execution)(nil), Async: false, Exec: "cf", Params: []string{"create-service", "testService", "testPlan", "testName", "-c", "testConfig.json"}},
				{Execution: (*mock.Execution)(nil), Async: false, Exec: "cf", Params: []string{"logout"}}},
				m.Calls)
		}
	})

	t.Run("Create service: failure, no config", func(t *testing.T) {
		defer cfMockCleanup(m)
		config := cloudFoundryCreateServiceOptions{}
		error := runCloudFoundryCreateService(&config, &telemetryData, cf)
		assert.EqualError(t, error, "Error while logging in: Failed to login to Cloud Foundry: Parameters missing. Please provide the Cloud Foundry Endpoint, Org, Space, Username and Password")
	})

	t.Run("Create service: variable substitution in-line", func(t *testing.T) {
		defer cfMockCleanup(m)

		dir := t.TempDir()
		oldCWD, _ := os.Getwd()
		_ = os.Chdir(dir)
		// clean up tmp dir
		defer func() {
			_ = os.Chdir(oldCWD)
		}()

		manifestFileString := `  
		---
		create-services:
		- name:   ((name))
		  broker: "testBroker"
		  plan:   "testPlan"
		
		- name:   ((name2))
		  broker: "testBroker"
		  plan:   "testPlan"
		
		- name:   "test3"
		  broker: "testBroker"
		  plan:   "testPlan"`

		manifestFileStringBody := []byte(manifestFileString)
		err := ioutil.WriteFile("manifestTest.yml", manifestFileStringBody, 0644)
		assert.NoError(t, err)

		var manifestVariables = []string{"name1=Test1", "name2=Test2"}

		config := cloudFoundryCreateServiceOptions{
			CfAPIEndpoint:     "https://api.endpoint.com",
			CfOrg:             "testOrg",
			CfSpace:           "testSpace",
			Username:          "testUser",
			Password:          "testPassword",
			ServiceManifest:   "manifestTest.yml",
			ManifestVariables: manifestVariables,
		}
		error := runCloudFoundryCreateService(&config, &telemetryData, cf)
		if assert.NoError(t, error) {
			assert.Equal(t, []mock.ExecCall{{Execution: (*mock.Execution)(nil), Async: false, Exec: "cf", Params: []string{"login", "-a", "https://api.endpoint.com", "-o", "testOrg", "-s", "testSpace", "-u", "testUser", "-p", "testPassword"}},
				{Execution: (*mock.Execution)(nil), Async: false, Exec: "cf", Params: []string{"create-service-push", "--no-push", "--service-manifest", "manifestTest.yml", "--var", "name1=Test1", "--var", "name2=Test2"}},
				{Execution: (*mock.Execution)(nil), Async: false, Exec: "cf", Params: []string{"logout"}}},
				m.Calls)
		}
	})

	t.Run("Create service: variable substitution with variable substitution manifest file", func(t *testing.T) {
		defer cfMockCleanup(m)

		dir := t.TempDir()
		oldCWD, _ := os.Getwd()
		_ = os.Chdir(dir)
		// clean up tmp dir
		defer func() {
			_ = os.Chdir(oldCWD)
		}()
		varsFileString := `name: test1
		name2: test2`

		manifestFileString := `  
		---
		create-services:
		- name:   ((name))
		  broker: "testBroker"
		  plan:   "testPlan"
		
		- name:   ((name2))
		  broker: "testBroker"
		  plan:   "testPlan"
		
		- name:   "test3"
		  broker: "testBroker"
		  plan:   "testPlan"`

		varsFileStringBody := []byte(varsFileString)
		manifestFileStringBody := []byte(manifestFileString)
		err := ioutil.WriteFile("varsTest.yml", varsFileStringBody, 0644)
		assert.NoError(t, err)
		err = ioutil.WriteFile("varsTest2.yml", varsFileStringBody, 0644)
		assert.NoError(t, err)
		err = ioutil.WriteFile("manifestTest.yml", manifestFileStringBody, 0644)
		assert.NoError(t, err)

		var manifestVariablesFiles = []string{"varsTest.yml", "varsTest2.yml"}
		config := cloudFoundryCreateServiceOptions{
			CfAPIEndpoint:          "https://api.endpoint.com",
			CfOrg:                  "testOrg",
			CfSpace:                "testSpace",
			Username:               "testUser",
			Password:               "testPassword",
			ServiceManifest:        "manifestTest.yml",
			ManifestVariablesFiles: manifestVariablesFiles,
			ManifestVariables:      []string{"a=b", "x=y"},
		}
		error := runCloudFoundryCreateService(&config, &telemetryData, cf)
		if assert.NoError(t, error) {
			assert.Equal(t, []mock.ExecCall{{Execution: (*mock.Execution)(nil), Async: false, Exec: "cf", Params: []string{"login", "-a", "https://api.endpoint.com", "-o", "testOrg", "-s", "testSpace", "-u", "testUser", "-p", "testPassword"}},
				{Execution: (*mock.Execution)(nil), Async: false, Exec: "cf", Params: []string{"create-service-push", "--no-push", "--service-manifest", "manifestTest.yml", "--vars-file", "varsTest.yml", "--vars-file", "varsTest2.yml", "--var", "a=b", "--var", "x=y"}},
				{Execution: (*mock.Execution)(nil), Async: false, Exec: "cf", Params: []string{"logout"}}},
				m.Calls)
		}
	})
}
