package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"testing"
)

func TestMissingEnvVariable(t *testing.T) {
	tempDir, err := ioutil.TempDir("", "secrets")
	if err != nil {
		t.Fatal(err)
	}

	_, err = writeTestTemplate(tempDir)
	if err != nil {
		t.Fatal(err)
	}

	generationDriverFile, err := writeGenerationDriverFile(tempDir)
	if err != nil {
		t.Fatal(err)
	}

	generatedFile := filepath.Join(tempDir, "appsecrets.go")
	err = run("CLIENT_ID,CLIENT_SECRET", generatedFile, []string{generationDriverFile})
	if !strings.Contains(err.Error(), "CLIENT_ID") || !strings.HasSuffix(err.Error(), "not found") {
		t.Fatalf("Error should mention missing environment variable CLIENT_ID but was [%s]", err.Error())
	}
}

func TestMissingSafekeeperFile(t *testing.T) {
	tempDir, err := ioutil.TempDir("", "secrets")
	if err != nil {
		t.Fatal(err)
	}

	generationDriverFile, err := writeGenerationDriverFile(tempDir)
	if err != nil {
		t.Fatal(err)
	}

	os.Setenv("CLIENT_ID", "safeid")
	os.Setenv("CLIENT_SECRET", "safesecret")

	generatedFile := filepath.Join(tempDir, "appsecrets.go")
	err = run("CLIENT_ID,CLIENT_SECRET", generatedFile, []string{generationDriverFile})
	if !strings.Contains(err.Error(), "secrets.go.safekeeper") || !strings.HasSuffix(err.Error(), "no such file or directory") {
		t.Fatalf("Error should mention missing .safekeeper file but was [%s]", err.Error())
	}
}

func TestValidCase(t *testing.T) {
	tempDir, err := ioutil.TempDir("", "secrets")
	if err != nil {
		t.Fatal(err)
	}

	_, err = writeTestTemplate(tempDir)
	if err != nil {
		t.Fatal(err)
	}

	generationDriverFile, err := writeGenerationDriverFile(tempDir)
	if err != nil {
		t.Fatal(err)
	}

	os.Setenv("CLIENT_ID", "safeid")
	os.Setenv("CLIENT_SECRET", "safesecret")

	generatedFile := filepath.Join(tempDir, "appsecrets.go")
	err = run("CLIENT_ID,CLIENT_SECRET", generatedFile, []string{generationDriverFile})
	if err != nil {
		t.Fatal(err)
	}

	outputFile, err := os.Open(generatedFile)
	if err != nil {
		t.Fatal(err)
	}

	output, err := ioutil.ReadAll(outputFile)
	if err != nil {
		t.Fatalf("Can't read generated file [%s]", err)
	}

	fileGenerationLine := "// GENERATED by safekeeper (https://github.com/alexandre-normand/safekeeper, DO NOT EDIT"
	if !strings.Contains(string(output), fileGenerationLine) {
		t.Errorf("Result file should contain a line with [%s] but was: \n\n%s", fileGenerationLine, string(output))
	}

	expectedGenerateLine := "//go:generate safekeeper --keys=CLIENT_ID,CLIENT_SECRET"
	if !strings.Contains(string(output), expectedGenerateLine) {
		t.Errorf("Result file should contain a go:generate line [%s] but was: \n\n%s", expectedGenerateLine, string(output))
	}

	expectedClientIdLine := "appSecrets.ClientId = \"safeid\""
	if !strings.Contains(string(output), expectedClientIdLine) {
		t.Errorf("Result file should have replaced ENV_CLIENT_ID with the client id value \"safeid\" but was: \n\n%s", expectedClientIdLine, string(output))
	}

	expectedClientSecretLine := "appSecrets.ClientSecret = \"safesecret\""
	if !strings.Contains(string(output), expectedClientSecretLine) {
		t.Errorf("Result file should have replaced ENV_CLIENT_SECRET with the client secret value \"safesecret\" but was: \n\n%s", expectedClientIdLine, string(output))
	}
}

func Example() {
	tempDir, _ := ioutil.TempDir("", "secrets")
	writeTestTemplate(tempDir)
	generationDriverFile, _ := writeGenerationDriverFile(tempDir)
	os.Setenv("CLIENT_ID", "safeid")
	os.Setenv("CLIENT_SECRET", "safesecret")

	generatedFile := filepath.Join(tempDir, "appsecrets.go")
	run("CLIENT_ID,CLIENT_SECRET", generatedFile, []string{generationDriverFile})

	ouputFile, _ := os.Open(generatedFile)

	output, _ := ioutil.ReadAll(ouputFile)
	fmt.Print(string(output))
	// Output:
	// package secrets

	// // AppSecrets is the source for all application secrets (client ids/secrets/passwords)
	// type AppSecrets struct {
	// ClientId       string
	// ClientSecret   string
	// }
	//
	// // NewAppSecrets returns the AppSecrets with all values set
	// func NewAppSecrets() *AppSecrets {
	// appSecrets := new(AppSecrets)
	// appSecrets.ClientId = "safeid"
	// appSecrets.ClientSecret = "safesecret"
	//
	//return appSecrets
}

// writeTestTemplate writes a .safekeeper template file with two ENV variables: CLIENT_ID and CLIENT_SECRET
func writeTestTemplate(tempDir string) (templatePath string, err error) {
	safekeeperFile := filepath.Join(tempDir, "secrets.go.safekeeper")
	err = ioutil.WriteFile(safekeeperFile, []byte("package secrets\n\n// AppSecrets is the source for all application secrets (client ids/secrets/passwords)\ntype AppSecrets struct {\nClientId       string\nClientSecret   string\n}\n// NewAppSecrets returns the AppSecrets with all values set\nfunc NewAppSecrets() *AppSecrets {\nappSecrets := new(AppSecrets)\nappSecrets.ClientId = \"ENV_CLIENT_ID\"\nappSecrets.ClientSecret = \"ENV_CLIENT_SECRET\"\n\n    return appSecrets\n}"), 0777)
	if err != nil {
		return "", err
	}

	return safekeeperFile, nil
}

// writeGenerationDriverFile writes a file that drives the safekeeper generation (i.e. the go file that would include the go:generate command)
func writeGenerationDriverFile(tempDir string) (driverPath string, err error) {
	driverFile := filepath.Join(tempDir, "secrets.go")
	err = ioutil.WriteFile(driverFile, []byte("package secrets\n//go:generate safekeeper --output=appsecrets.go --keys=CLIENT_ID,CLIENT_SECRET $GOFILE"), 0777)
	if err != nil {
		return "", err
	}

	return driverFile, nil
}
