package templates

import (
	"strings"
	"testing"
	textTpl "text/template"
)

func mkTemplate(current, replacement interface{}) textTemplate {
	tmpl := textTemplate{}
	if current != nil {
		switch val := current.(type) {
		case string:
			tmpl.current = textTpl.Must(newTemplate().Parse(val))
		}
	}
	if replacement != nil {
		switch val := replacement.(type) {
		case string:
			tmpl.replacement = textTpl.Must(newTemplate().Parse(val))
		}
	}
	return tmpl
}

func equalTemplates(tmpls ...*textTpl.Template) bool {
	var cmp *textTpl.Template
	for i, tmpl := range tmpls {
		if i == 0 {
			cmp = tmpl
		} else {
			if cmp == nil || tmpl == nil {
				if cmp != tmpl {
					return false
				}
				continue
			}
			if len(tmpl.Templates()) != len(cmp.Templates()) {
				return false
			}
			for _, t := range tmpl.Templates() {
				tp := cmp.Lookup(t.Name())
				if tp == nil {
					return false
				}
				if tp.Root.String() != t.Root.String() {
					return false
				}
			}
		}
	}
	return true
}

func TestTemplates_Load(t *testing.T) {
	testCases := []struct {
		name             string
		initialTemplate  textTemplate
		pathPatterns     []string
		overwrite        bool
		expectedTemplate textTemplate
		expErr           string
	}{
		{
			"non existing path undefined template override",
			mkTemplate(nil, nil),
			[]string{
				"templates/non-existing/good-*.tpl",
				"templates/absent/good-*.tpl",
			},
			true,
			mkTemplate(``, nil),
			"",
		},
		{
			"non existing path defined template override",
			mkTemplate(`
				{{- define "test.1" -}}
					{{- printf "value" -}}
				{{- end -}}
			`, nil),
			[]string{
				"templates/non-existing/good-*.tpl",
				"templates/absent/good-*.tpl",
			},
			true,
			mkTemplate(``, nil),
			"",
		},
		{
			"existing path undefined template override",
			mkTemplate(nil, nil),
			[]string{
				"templates/other/nested/good0-*.tpl",
				"templates/test/good0-*.tpl",
			},
			false,
			mkTemplate(`
				{{- define "good0-test.tpl" -}}{{- end -}}
				{{- define "test.0" -}}
					{{ printf "Hello %s!" externalURL }}
				{{- end -}}
				{{- define "test.1" -}}
					{{ printf "Hello %s!" externalURL }}
				{{- end -}}
				{{- define "test.2" -}}
					{{ printf "Hello %s!" externalURL }}
				{{- end -}}
				{{- define "test.3" -}}
					{{ printf "Hello %s!" externalURL }}
				{{- end -}}
			`, nil),
			"",
		},
		{
			"existing path defined template override",
			mkTemplate(`
				{{- define "test.1" -}}
					{{ printf "Hello %s!" "world" }}
				{{- end -}}
			`, nil),
			[]string{
				"templates/other/nested/good0-*.tpl",
				"templates/test/good0-*.tpl",
			},
			false,
			mkTemplate(`
				{{- define "good0-test.tpl" -}}{{- end -}}
				{{- define "test.0" -}}
					{{ printf "Hello %s!" externalURL }}
				{{- end -}}
				{{- define "test.1" -}}
					{{ printf "Hello %s!" "world" }}
				{{- end -}}
				{{- define "test.2" -}}
					{{ printf "Hello %s!" externalURL }}
				{{- end -}}
				{{- define "test.3" -}}
					{{ printf "Hello %s!" externalURL }}
				{{- end -}}
				`, `
				{{- define "good0-test.tpl" -}}{{- end -}}
				{{- define "test.0" -}}
					{{ printf "Hello %s!" externalURL }}
				{{- end -}}
				{{- define "test.1" -}}
					{{ printf "Hello %s!" externalURL }}
				{{- end -}}
				{{- define "test.2" -}}
					{{ printf "Hello %s!" externalURL }}
				{{- end -}}
				{{- define "test.3" -}}
					{{ printf "Hello %s!" externalURL }}
				{{- end -}}
			`),
			"",
		},
		{
			"load template with syntax error",
			mkTemplate(`
				{{- define "test.1" -}}
					{{ printf "Hello %s!" "world" }}
				{{- end -}}
			`, nil),
			[]string{
				"templates/other/nested/bad0-*.tpl",
				"templates/test/good0-*.tpl",
			},
			false,
			mkTemplate(`
				{{- define "test.1" -}}
					{{ printf "Hello %s!" "world" }}
				{{- end -}}
			`, nil),
			"failed to parse template glob",
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			masterTmpl = tc.initialTemplate
			err := Load(tc.pathPatterns, tc.overwrite)
			if tc.expErr == "" && err != nil {
				t.Error("happened error that wasn't expected: %w", err)
			}
			if tc.expErr != "" && err == nil {
				t.Error("%+w", err)
				t.Error("expected error that didn't happend")
			}
			if err != nil && !strings.Contains(err.Error(), tc.expErr) {
				t.Error("%+w", err)
				t.Error("expected string doesn't exist in error message")
			}
			if !equalTemplates(masterTmpl.replacement, tc.expectedTemplate.replacement) {
				t.Fatalf("replacement template is not as expected")
			}
			if !equalTemplates(masterTmpl.current, tc.expectedTemplate.current) {
				t.Fatalf("current template is not as expected")
			}
		})
	}
}

func TestTemplates_Reload(t *testing.T) {
	testCases := []struct {
		name             string
		initialTemplate  textTemplate
		expectedTemplate textTemplate
	}{
		{
			"empty current and replacement templates",
			mkTemplate(nil, nil),
			mkTemplate(nil, nil),
		},
		{
			"empty current template only",
			mkTemplate(`
				{{- define "test.1" -}}
					{{- printf "value" -}}
				{{- end -}}
			`, nil),
			mkTemplate(`
				{{- define "test.1" -}}
					{{- printf "value" -}}
				{{- end -}}
			`, nil),
		},
		{
			"empty replacement template only",
			mkTemplate(nil, `
				{{- define "test.1" -}}
					{{- printf "value" -}}
				{{- end -}}
			`),
			mkTemplate(`
				{{- define "test.1" -}}
					{{- printf "value" -}}
				{{- end -}}
			`, nil),
		},
		{
			"defined both templates",
			mkTemplate(`
				{{- define "test.0" -}}
					{{- printf "value" -}}
				{{- end -}}
				{{- define "test.1" -}}
					{{- printf "before" -}}
				{{- end -}}
			`, `
				{{- define "test.1" -}}
					{{- printf "after" -}}
				{{- end -}}
			`),
			mkTemplate(`
				{{- define "test.1" -}}
					{{- printf "after" -}}
				{{- end -}}
			`, nil),
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			masterTmpl = tc.initialTemplate
			Reload()
			if !equalTemplates(masterTmpl.replacement, tc.expectedTemplate.replacement) {
				t.Fatalf("replacement template is not as expected")
			}
			if !equalTemplates(masterTmpl.current, tc.expectedTemplate.current) {
				t.Fatalf("current template is not as expected")
			}
		})
	}
}