package templates

import (
	"os"
	"path/filepath"
	"testing"

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

func createTestTemplate() *WorkflowTemplate {
	return &WorkflowTemplate{
		Name:        "golang-project",
		Version:     "1.0.0",
		Description: "Template for Go projects",
		Author:      "test-author",
		Orchestrator: OrchestratorConfig{
			PlannerURL:     "http://planner:8000",
			JudgeURL:       "http://judge:8000",
			ImplementerURL: "http://implementer:8000",
			TimeoutSeconds: 300,
			RetryPolicy:    "exponential",
			MaxConcurrency: 10,
		},
		Tasks: []TaskConfig{
			{
				ID:          "T0.1",
				Title:       "Analyze Requirements",
				Description: "Analyze project requirements",
				Type:        "feature",
				Priority:    "high",
			},
			{
				ID:          "T0.2",
				Title:       "Implement Solution",
				Description: "Implement the solution",
				Type:        "feature",
				Priority:    "high",
				DependsOn:   []string{"T0.1"},
			},
		},
	}
}

func TestNewWorkflowTemplateManager(t *testing.T) {
	tmpDir := t.TempDir()
	manager := NewWorkflowTemplateManager(tmpDir)

	assert.NotNil(t, manager)
	assert.Equal(t, tmpDir, manager.templatePath)
}

func TestSaveTemplate(t *testing.T) {
	tmpDir := t.TempDir()
	manager := NewWorkflowTemplateManager(tmpDir)

	template := createTestTemplate()
	err := manager.SaveTemplate(template)

	assert.NoError(t, err)

	// Check file was created
	filePath := filepath.Join(tmpDir, "golang-project.yaml")
	_, err = os.Stat(filePath)
	assert.NoError(t, err)
}

func TestSaveTemplateNil(t *testing.T) {
	tmpDir := t.TempDir()
	manager := NewWorkflowTemplateManager(tmpDir)

	err := manager.SaveTemplate(nil)
	assert.Error(t, err)
}

func TestLoadTemplate(t *testing.T) {
	tmpDir := t.TempDir()
	manager := NewWorkflowTemplateManager(tmpDir)

	template := createTestTemplate()
	manager.SaveTemplate(template)

	loaded, err := manager.LoadTemplate("golang-project")
	assert.NoError(t, err)
	assert.NotNil(t, loaded)
	assert.Equal(t, "golang-project", loaded.Name)
}

func TestLoadTemplateNotFound(t *testing.T) {
	tmpDir := t.TempDir()
	manager := NewWorkflowTemplateManager(tmpDir)

	_, err := manager.LoadTemplate("nonexistent")
	assert.Error(t, err)
}

func TestGetTemplate(t *testing.T) {
	tmpDir := t.TempDir()
	manager := NewWorkflowTemplateManager(tmpDir)

	template := createTestTemplate()
	manager.SaveTemplate(template)

	retrieved, exists := manager.GetTemplate("golang-project")
	assert.True(t, exists)
	assert.Equal(t, "golang-project", retrieved.Name)
}

func TestGetTemplateNotFound(t *testing.T) {
	tmpDir := t.TempDir()
	manager := NewWorkflowTemplateManager(tmpDir)

	_, exists := manager.GetTemplate("nonexistent")
	assert.False(t, exists)
}

func TestListTemplates(t *testing.T) {
	tmpDir := t.TempDir()
	manager := NewWorkflowTemplateManager(tmpDir)

	for i := 0; i < 3; i++ {
		template := createTestTemplate()
		template.Name = "template-" + string(rune(48+i))
		manager.SaveTemplate(template)
	}

	templates := manager.ListTemplates()
	assert.Equal(t, 3, len(templates))
}

func TestDeleteTemplate(t *testing.T) {
	tmpDir := t.TempDir()
	manager := NewWorkflowTemplateManager(tmpDir)

	template := createTestTemplate()
	manager.SaveTemplate(template)

	err := manager.DeleteTemplate("golang-project")
	assert.NoError(t, err)

	_, exists := manager.GetTemplate("golang-project")
	assert.False(t, exists)
}

func TestDeleteTemplateNotFound(t *testing.T) {
	tmpDir := t.TempDir()
	manager := NewWorkflowTemplateManager(tmpDir)

	err := manager.DeleteTemplate("nonexistent")
	assert.Error(t, err)
}

func TestValidateTemplateValid(t *testing.T) {
	template := createTestTemplate()
	err := NewWorkflowTemplateManager("/tmp").ValidateTemplate(template)
	assert.NoError(t, err)
}

func TestValidateTemplateEmptyName(t *testing.T) {
	template := createTestTemplate()
	template.Name = ""
	err := NewWorkflowTemplateManager("/tmp").ValidateTemplate(template)
	assert.Error(t, err)
}

func TestValidateTemplateNoTasks(t *testing.T) {
	template := createTestTemplate()
	template.Tasks = make([]TaskConfig, 0)
	err := NewWorkflowTemplateManager("/tmp").ValidateTemplate(template)
	assert.Error(t, err)
}

func TestValidateTemplateInvalidDependency(t *testing.T) {
	template := createTestTemplate()
	template.Tasks[1].DependsOn = []string{"nonexistent"}
	err := NewWorkflowTemplateManager("/tmp").ValidateTemplate(template)
	assert.Error(t, err)
}

func TestExportTemplateJSON(t *testing.T) {
	tmpDir := t.TempDir()
	manager := NewWorkflowTemplateManager(tmpDir)

	template := createTestTemplate()
	manager.SaveTemplate(template)

	json, err := manager.ExportTemplateJSON("golang-project")
	assert.NoError(t, err)
	assert.NotEmpty(t, json)
	assert.Contains(t, json, "golang-project")
}

func TestExportTemplateJSONNotFound(t *testing.T) {
	tmpDir := t.TempDir()
	manager := NewWorkflowTemplateManager(tmpDir)

	_, err := manager.ExportTemplateJSON("nonexistent")
	assert.Error(t, err)
}

func TestRecordUsage(t *testing.T) {
	tmpDir := t.TempDir()
	manager := NewWorkflowTemplateManager(tmpDir)

	template := createTestTemplate()
	manager.SaveTemplate(template)

	err := manager.RecordUsage("golang-project")
	assert.NoError(t, err)

	stats := manager.GetStats()
	assert.Equal(t, 1, stats.ExecutedTemplates)
}

func TestRecordUsageNotFound(t *testing.T) {
	tmpDir := t.TempDir()
	manager := NewWorkflowTemplateManager(tmpDir)

	err := manager.RecordUsage("nonexistent")
	assert.Error(t, err)
}

func TestTemplateGetStats(t *testing.T) {
	tmpDir := t.TempDir()
	manager := NewWorkflowTemplateManager(tmpDir)

	template := createTestTemplate()
	manager.SaveTemplate(template)

	stats := manager.GetStats()
	assert.Equal(t, 1, stats.TotalTemplates)
	assert.Equal(t, 1, stats.LoadedTemplates)
}

func TestLoadTemplateDirectory(t *testing.T) {
	tmpDir := t.TempDir()
	manager := NewWorkflowTemplateManager(tmpDir)

	for i := 0; i < 3; i++ {
		template := createTestTemplate()
		template.Name = "template-" + string(rune(48+i))
		manager.SaveTemplate(template)
	}

	manager.ClearCache()
	err := manager.LoadTemplateDirectory()
	assert.NoError(t, err)

	templates := manager.ListTemplates()
	assert.Equal(t, 3, len(templates))
}

func TestClearCache(t *testing.T) {
	tmpDir := t.TempDir()
	manager := NewWorkflowTemplateManager(tmpDir)

	template := createTestTemplate()
	manager.SaveTemplate(template)

	assert.Equal(t, 1, len(manager.ListTemplates()))

	manager.ClearCache()
	assert.Equal(t, 0, len(manager.ListTemplates()))
}

func TestTemplateTimestamps(t *testing.T) {
	tmpDir := t.TempDir()
	manager := NewWorkflowTemplateManager(tmpDir)

	template := createTestTemplate()
	manager.SaveTemplate(template)

	retrieved, _ := manager.GetTemplate("golang-project")
	assert.False(t, retrieved.CreatedAt.IsZero())
	assert.False(t, retrieved.UpdatedAt.IsZero())
	assert.True(t, retrieved.UpdatedAt.After(retrieved.CreatedAt) || retrieved.UpdatedAt.Equal(retrieved.CreatedAt))
}

func TestTemplateWithDefaults(t *testing.T) {
	tmpDir := t.TempDir()
	manager := NewWorkflowTemplateManager(tmpDir)

	template := createTestTemplate()
	template.Defaults = map[string]interface{}{
		"language": "go",
		"version":  "1.20",
	}

	manager.SaveTemplate(template)

	retrieved, _ := manager.GetTemplate("golang-project")
	assert.NotNil(t, retrieved.Defaults)
	assert.Equal(t, "go", retrieved.Defaults["language"])
}

func TestTemplateWithTags(t *testing.T) {
	tmpDir := t.TempDir()
	manager := NewWorkflowTemplateManager(tmpDir)

	template := createTestTemplate()
	template.Tags = map[string]string{
		"environment": "production",
		"team":        "backend",
	}

	manager.SaveTemplate(template)

	retrieved, _ := manager.GetTemplate("golang-project")
	assert.NotNil(t, retrieved.Tags)
	assert.Equal(t, "production", retrieved.Tags["environment"])
}

func TestMultipleTemplates(t *testing.T) {
	tmpDir := t.TempDir()
	manager := NewWorkflowTemplateManager(tmpDir)

	for i := 0; i < 5; i++ {
		template := createTestTemplate()
		template.Name = "template-" + string(rune(48+i))
		manager.SaveTemplate(template)
	}

	templates := manager.ListTemplates()
	assert.Equal(t, 5, len(templates))

	for i := 0; i < 5; i++ {
		name := "template-" + string(rune(48+i))
		manager.RecordUsage(name)
	}

	stats := manager.GetStats()
	assert.Equal(t, 5, stats.ExecutedTemplates)
}

func BenchmarkSaveTemplate(b *testing.B) {
	tmpDir := b.TempDir()
	manager := NewWorkflowTemplateManager(tmpDir)

	for i := 0; i < b.N; i++ {
		template := createTestTemplate()
		template.Name = "template-" + string(rune(48+i%100))
		manager.SaveTemplate(template)
	}
}

func BenchmarkLoadTemplate(b *testing.B) {
	tmpDir := b.TempDir()
	manager := NewWorkflowTemplateManager(tmpDir)

	template := createTestTemplate()
	manager.SaveTemplate(template)

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		manager.LoadTemplate("golang-project")
	}
}
