package template

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

func TestBuiltinTemplatesAreParseable(t *testing.T) {
	funcs := DefaultFuncMap()
	builtins := builtinTemplates()

	if len(builtins) != 5 {
		t.Fatalf("expected 5 built-in templates, got %d", len(builtins))
	}

	required := []string{"env", "docker-compose", "k8s-secret", "github-actions", "terraform"}
	for _, name := range required {
		if _, ok := builtins[name]; !ok {
			t.Errorf("missing required built-in template: %q", name)
		}
	}

	for name, src := range builtins {
		t.Run(name, func(t *testing.T) {
			tmpl, err := template.New(name).Funcs(funcs).Parse(src)
			if err != nil {
				t.Fatalf("template %q failed to parse: %v", name, err)
			}
			if tmpl == nil {
				t.Fatal("template is nil")
			}
		})
	}
}

func TestBuiltinEnvTemplate(t *testing.T) {
	funcs := DefaultFuncMap()
	tmpl, err := template.New("env").Funcs(funcs).Parse(builtinTemplates()["env"])
	if err != nil {
		t.Fatalf("parse env template: %v", err)
	}

	data := RenderData{
		Name:   "app",
		Refs:   map[string]string{"API_KEY": "op://prod/api/key", "DB_PASS": "prod/db.password"},
		Values: map[string]string{"API_KEY": "secret1", "DB_PASS": "secret2"},
	}

	var buf strings.Builder
	if err := tmpl.Execute(&buf, data); err != nil {
		t.Fatalf("execute env template: %v", err)
	}

	output := buf.String()
	if !strings.Contains(output, "export API_KEY=secret1") {
		t.Errorf("env template missing API_KEY export: %s", output)
	}
	if !strings.Contains(output, "export DB_PASS=secret2") {
		t.Errorf("env template missing DB_PASS export: %s", output)
	}
	if !strings.Contains(output, "# Generated by OpenPass") {
		t.Errorf("env template missing header: %s", output)
	}
}

func TestBuiltinK8sSecretTemplate(t *testing.T) {
	funcs := DefaultFuncMap()
	tmpl, err := template.New("k8s-secret").Funcs(funcs).Parse(builtinTemplates()["k8s-secret"])
	if err != nil {
		t.Fatalf("parse k8s-secret template: %v", err)
	}

	data := RenderData{
		Name:   "my-secret",
		Refs:   map[string]string{"API_KEY": "op://prod/api/key"},
		Values: map[string]string{"API_KEY": "my-secret-value"},
	}

	var buf strings.Builder
	if err := tmpl.Execute(&buf, data); err != nil {
		t.Fatalf("execute k8s-secret template: %v", err)
	}

	output := buf.String()
	if !strings.Contains(output, "name: my-secret") {
		t.Errorf("k8s-secret template missing metadata name: %s", output)
	}
	if !strings.Contains(output, "apiVersion: v1") {
		t.Errorf("k8s-secret template missing apiVersion: %s", output)
	}
	if !strings.Contains(output, "kind: Secret") {
		t.Errorf("k8s-secret template missing kind: %s", output)
	}

	expectedB64 := base64Encode("my-secret-value")
	if !strings.Contains(output, expectedB64) {
		t.Errorf("k8s-secret template missing base64-encoded value; expected %q in:\n%s", expectedB64, output)
	}
}

//nolint:dupl // similar test structure for different builtin templates
func TestBuiltinDockerComposeTemplate(t *testing.T) {
	funcs := DefaultFuncMap()
	tmpl, err := template.New("docker-compose").Funcs(funcs).Parse(builtinTemplates()["docker-compose"])
	if err != nil {
		t.Fatalf("parse docker-compose template: %v", err)
	}

	data := RenderData{
		Name:   "app",
		Refs:   map[string]string{"API_KEY": "op://prod/api/key"},
		Values: map[string]string{"API_KEY": "secret"},
	}

	var buf strings.Builder
	if err := tmpl.Execute(&buf, data); err != nil {
		t.Fatalf("execute docker-compose template: %v", err)
	}

	output := buf.String()
	if !strings.Contains(output, "version: \"3.8\"") {
		t.Errorf("docker-compose template missing version: %s", output)
	}
	if !strings.Contains(output, "services:") {
		t.Errorf("docker-compose template missing services: %s", output)
	}
	if !strings.Contains(output, "api_key_secret") {
		t.Errorf("docker-compose template missing secret reference: %s", output)
	}
}

//nolint:dupl // similar test structure for different builtin templates
func TestBuiltinGitHubActionsTemplate(t *testing.T) {
	funcs := DefaultFuncMap()
	tmpl, err := template.New("github-actions").Funcs(funcs).Parse(builtinTemplates()["github-actions"])
	if err != nil {
		t.Fatalf("parse github-actions template: %v", err)
	}

	data := RenderData{
		Name:   "app",
		Refs:   map[string]string{"API_KEY": "op://prod/api/key"},
		Values: map[string]string{"API_KEY": "secret"},
	}

	var buf strings.Builder
	if err := tmpl.Execute(&buf, data); err != nil {
		t.Fatalf("execute github-actions template: %v", err)
	}

	output := buf.String()
	if !strings.Contains(output, "name: Deploy app") {
		t.Errorf("github-actions template missing workflow name: %s", output)
	}
	if !strings.Contains(output, "runs-on: ubuntu-latest") {
		t.Errorf("github-actions template missing runs-on: %s", output)
	}
	if !strings.Contains(output, "API_KEY: secret") {
		t.Errorf("github-actions template missing env var: %s", output)
	}
}

//nolint:dupl // similar test structure for different builtin templates
func TestBuiltinTerraformTemplate(t *testing.T) {
	funcs := DefaultFuncMap()
	tmpl, err := template.New("terraform").Funcs(funcs).Parse(builtinTemplates()["terraform"])
	if err != nil {
		t.Fatalf("parse terraform template: %v", err)
	}

	data := RenderData{
		Name:   "infra",
		Refs:   map[string]string{"API_KEY": "op://prod/api/key"},
		Values: map[string]string{"API_KEY": "secret"},
	}

	var buf strings.Builder
	if err := tmpl.Execute(&buf, data); err != nil {
		t.Fatalf("execute terraform template: %v", err)
	}

	output := buf.String()
	if !strings.Contains(output, `variable "api_key"`) {
		t.Errorf("terraform template missing variable declaration: %s", output)
	}
	if !strings.Contains(output, "sensitive = true") {
		t.Errorf("terraform template missing sensitive flag: %s", output)
	}
	if !strings.Contains(output, `"secret"`) {
		t.Errorf("terraform template missing default value: %s", output)
	}
}

func TestBuiltinTemplatesWithEmptyRefs(t *testing.T) {
	funcs := DefaultFuncMap()
	builtins := builtinTemplates()

	for name, src := range builtins {
		t.Run(name, func(t *testing.T) {
			tmpl, err := template.New(name).Funcs(funcs).Parse(src)
			if err != nil {
				t.Fatalf("parse %q: %v", name, err)
			}

			data := RenderData{
				Name:   "test",
				Refs:   map[string]string{},
				Values: map[string]string{},
			}

			var buf strings.Builder
			if err := tmpl.Execute(&buf, data); err != nil {
				t.Fatalf("execute %q with empty refs: %v", name, err)
			}
		})
	}
}
