package templates

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestInterpolateVars(t *testing.T) {
	tests := []struct {
		name     string
		content  string
		vars     map[string]string
		expected string
	}{
		{
			name:    "single variable",
			content: "Hello {{PROJECT_NAME}}!",
			vars: map[string]string{
				"PROJECT_NAME": "myapp",
			},
			expected: "Hello myapp!",
		},
		{
			name:    "multiple variables",
			content: "Project {{PROJECT_NAME}} at {{DOMAIN}} from {{GIT_URL}}",
			vars: map[string]string{
				"PROJECT_NAME": "myapp",
				"DOMAIN":       "myapp.threesix.ai",
				"GIT_URL":      "https://git.example.com/org/myapp.git",
			},
			expected: "Project myapp at myapp.threesix.ai from https://git.example.com/org/myapp.git",
		},
		{
			name:     "no variables",
			content:  "Static content with no placeholders",
			vars:     map[string]string{},
			expected: "Static content with no placeholders",
		},
		{
			name:    "variable not in map",
			content: "Hello {{UNKNOWN_VAR}}!",
			vars: map[string]string{
				"PROJECT_NAME": "myapp",
			},
			expected: "Hello {{UNKNOWN_VAR}}!",
		},
		{
			name:     "empty vars map",
			content:  "Hello {{PROJECT_NAME}}!",
			vars:     nil,
			expected: "Hello {{PROJECT_NAME}}!",
		},
		{
			name:    "repeated variable",
			content: "{{NAME}} and {{NAME}} again",
			vars: map[string]string{
				"NAME": "test",
			},
			expected: "test and test again",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := interpolateVars(tt.content, tt.vars)
			assert.Equal(t, tt.expected, result)
		})
	}
}

func TestIsValidTemplateName(t *testing.T) {
	tests := []struct {
		name     string
		input    string
		expected bool
	}{
		{"valid simple", "default", true},
		{"valid with dash", "astro-landing", true},
		{"valid with numbers", "go-api-v2", true},
		{"empty", "", false},
		{"starts with number", "123template", false},
		{"starts with dash", "-invalid", false},
		{"contains uppercase", "MyTemplate", false},
		{"contains underscore", "my_template", false},
		{"contains dot", "my.template", false},
		{"contains slash", "my/template", false},
		{"path traversal attempt", "../evil", false},
		{"too long", "a" + string(make([]byte, 64)), false},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := isValidTemplateName(tt.input)
			assert.Equal(t, tt.expected, result, "isValidTemplateName(%q)", tt.input)
		})
	}
}

func TestListTemplateFiles(t *testing.T) {
	// Test that default template exists and has expected files
	files, err := listTemplateFiles("default")
	require.NoError(t, err)

	// Should have at least these core files
	assert.Contains(t, files, ".woodpecker.yml", "default template should have .woodpecker.yml")
	assert.Contains(t, files, "Dockerfile", "default template should have Dockerfile")
	assert.Contains(t, files, "README.md", "default template should have README.md")
}

func TestListTemplateFiles_InvalidTemplate(t *testing.T) {
	_, err := listTemplateFiles("nonexistent-template")
	assert.Error(t, err, "should error for nonexistent template")
}

func TestAvailableTemplates(t *testing.T) {
	// Verify all declared templates actually exist
	for _, tmpl := range availableTemplates {
		t.Run(tmpl.Name, func(t *testing.T) {
			files, err := listTemplateFiles(tmpl.Name)
			require.NoError(t, err, "template %s should exist", tmpl.Name)
			assert.NotEmpty(t, files, "template %s should have files", tmpl.Name)

			// Every template should have these core files
			assert.Contains(t, files, ".woodpecker.yml", "template %s should have .woodpecker.yml", tmpl.Name)
		})
	}
}

func TestListTemplateFiles_TmplExtensionStripped(t *testing.T) {
	// go-api template has go.mod.tmpl which should be listed as go.mod
	files, err := listTemplateFiles("go-api")
	require.NoError(t, err)

	// Should contain go.mod (not go.mod.tmpl)
	assert.Contains(t, files, "go.mod", "go.mod.tmpl should be listed as go.mod")

	// Should NOT contain the .tmpl extension
	for _, f := range files {
		assert.NotContains(t, f, ".tmpl", "file %s should not have .tmpl extension", f)
	}
}

func TestSkeletonTemplate(t *testing.T) {
	// Test that skeleton template exists and has expected files
	files, err := listTemplateFiles("skeleton")
	require.NoError(t, err)

	// Should have the core monorepo files
	assert.Contains(t, files, "CLAUDE.md", "skeleton template should have CLAUDE.md")
	assert.Contains(t, files, "README.md", "skeleton template should have README.md")
	assert.Contains(t, files, ".woodpecker.yml", "skeleton template should have .woodpecker.yml")
	assert.Contains(t, files, "docker-compose.yml", "skeleton template should have docker-compose.yml")
	assert.Contains(t, files, "go.work", "skeleton template should have go.work")
	assert.Contains(t, files, "Procfile", "skeleton template should have Procfile")
	assert.Contains(t, files, ".gitignore", "skeleton template should have .gitignore")
	assert.Contains(t, files, ".golangci.yml", "skeleton template should have .golangci.yml")

	// Should have scripts
	assert.Contains(t, files, "scripts/dev.sh", "skeleton template should have scripts/dev.sh")
	assert.Contains(t, files, "scripts/install.sh", "skeleton template should have scripts/install.sh")
	assert.Contains(t, files, "scripts/quality.sh", "skeleton template should have scripts/quality.sh")
	assert.Contains(t, files, "scripts/discover.sh", "skeleton template should have scripts/discover.sh")

	// Should have .claude structure
	assert.Contains(t, files, ".claude/settings.local.json", "skeleton template should have .claude/settings.local.json")
	assert.Contains(t, files, ".claude/guides/local/setup.md", "skeleton template should have .claude/guides/local/setup.md")
	assert.Contains(t, files, ".claude/guides/ops/deploying.md", "skeleton template should have .claude/guides/ops/deploying.md")
	assert.Contains(t, files, ".claude/skills/code-review/SKILL.md", "skeleton template should have .claude/skills/code-review/SKILL.md")

	// Should have component directory placeholders
	assert.Contains(t, files, "services/.gitkeep", "skeleton template should have services/.gitkeep")
	assert.Contains(t, files, "workers/.gitkeep", "skeleton template should have workers/.gitkeep")
	assert.Contains(t, files, "apps/.gitkeep", "skeleton template should have apps/.gitkeep")
	assert.Contains(t, files, "cli/.gitkeep", "skeleton template should have cli/.gitkeep")

	// Should have pkg directory files
	assert.Contains(t, files, "pkg/go.mod", "skeleton template should have pkg/go.mod")
	assert.Contains(t, files, "pkg/README.md", "skeleton template should have pkg/README.md")
}

func TestSkeletonTemplateInfo(t *testing.T) {
	// Verify skeleton template metadata
	assert.Equal(t, "skeleton", skeletonTemplate.Name)
	assert.Equal(t, "monorepo", skeletonTemplate.Stack)
	assert.NotEmpty(t, skeletonTemplate.Description)
}
