package skills

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

func TestParseSkillWithYAML(t *testing.T) {
	data := []byte(`---
name: test-skill
description: A test skill
author: test
version: "1.0"
target: both
tags:
  - test
  - demo
---
# Test Skill Content
This is the body.
`)

	skill, err := parseSkill(data)
	if err != nil {
		t.Fatalf("parseSkill failed: %v", err)
	}
	if skill.Description != "A test skill" {
		t.Errorf("Expected 'A test skill', got %s", skill.Description)
	}
	if skill.Author != "test" {
		t.Errorf("Expected 'test', got %s", skill.Author)
	}
	if skill.Version != "1.0" {
		t.Errorf("Expected '1.0', got %s", skill.Version)
	}
	if skill.Target != "both" {
		t.Errorf("Expected 'both', got %s", skill.Target)
	}
	if len(skill.Tags) != 2 {
		t.Errorf("Expected 2 tags, got %d", len(skill.Tags))
	}
	if skill.Content == "" {
		t.Error("Content should not be empty")
	}
}

func TestParseSkillNoFrontmatter(t *testing.T) {
	data := []byte("Just plain content here")
	skill, err := parseSkill(data)
	if err != nil {
		t.Fatalf("parseSkill failed: %v", err)
	}
	if skill.Content != "Just plain content here" {
		t.Errorf("Unexpected content: %s", skill.Content)
	}
}

func TestParseSkillIncompleteFrontmatter(t *testing.T) {
	data := []byte("---\nname: incomplete\n---\nBody content")
	skill, err := parseSkill(data)
	if err != nil {
		t.Fatalf("parseSkill failed: %v", err)
	}
	if skill.Content != "Body content" {
		t.Errorf("Expected 'Body content', got %s", skill.Content)
	}
}

func TestRenderSkill(t *testing.T) {
	skill := &Skill{
		Name:        "test",
		Description: "A test",
		Author:      "author",
		Version:     "1.0",
		Target:      "both",
		Tags:        []string{"a", "b"},
		Content:     "Body",
	}

	rendered := renderSkill(skill)
	if rendered == "" {
		t.Error("Rendered skill should not be empty")
	}
	if len(rendered) < 20 {
		t.Error("Rendered skill seems too short")
	}
}

func TestListEmpty(t *testing.T) {
	tmpDir := t.TempDir()
	origHome := os.Getenv("HOME")
	os.Setenv("HOME", tmpDir)
	defer os.Setenv("HOME", origHome)

	skills, err := List()
	if err != nil {
		t.Fatalf("List failed: %v", err)
	}
	if len(skills) != 0 {
		t.Errorf("Expected 0 skills, got %d", len(skills))
	}
}

func TestCreateAndGet(t *testing.T) {
	tmpDir := t.TempDir()
	origHome := os.Getenv("HOME")
	os.Setenv("HOME", tmpDir)
	defer os.Setenv("HOME", origHome)

	skill := &Skill{
		Name:        "test-skill",
		Description: "Test description",
		Content:     "Test content body",
		Author:      "tester",
		Version:     "1.0.0",
		Target:      "both",
	}

	if err := Create(skill); err != nil {
		t.Fatalf("Create failed: %v", err)
	}

	dir, _ := SkillsDir()
	skillPath := filepath.Join(dir, "test-skill", "SKILL.md")
	if _, err := os.Stat(skillPath); os.IsNotExist(err) {
		t.Error("Skill file should exist")
	}

	got, err := Get("test-skill")
	if err != nil {
		t.Fatalf("Get failed: %v", err)
	}
	if got.Name != "test-skill" {
		t.Errorf("Expected test-skill, got %s", got.Name)
	}
}

func TestDelete(t *testing.T) {
	tmpDir := t.TempDir()
	origHome := os.Getenv("HOME")
	os.Setenv("HOME", tmpDir)
	defer os.Setenv("HOME", origHome)

	skill := &Skill{
		Name:        "to-delete",
		Description: "Will be deleted",
		Content:     "content",
		Target:      "both",
	}
	Create(skill)

	if err := Delete("to-delete"); err != nil {
		t.Fatalf("Delete failed: %v", err)
	}

	_, err := Get("to-delete")
	if err == nil {
		t.Error("Skill should be deleted")
	}
}

func TestBuildAIGeneratePrompt(t *testing.T) {
	prompt := BuildAIGeneratePrompt("docker", "Set up Docker", "both")
	if prompt == "" {
		t.Error("Prompt should not be empty")
	}
	if len(prompt) < 50 {
		t.Error("Prompt seems too short")
	}
}

func TestInstallBuiltinSkills(t *testing.T) {
	tmpDir := t.TempDir()
	origHome := os.Getenv("HOME")
	os.Setenv("HOME", tmpDir)
	defer os.Setenv("HOME", origHome)

	if err := InstallBuiltinSkills(); err != nil {
		t.Fatalf("InstallBuiltinSkills failed: %v", err)
	}

	skills, err := List()
	if err != nil {
		t.Fatalf("List failed: %v", err)
	}
	if len(skills) == 0 {
		t.Error("Expected at least one builtin skill")
	}

	found := false
	for _, s := range skills {
		if s.Name == "env-setup" {
			found = true
		}
	}
	if !found {
		t.Error("Expected env-setup skill")
	}
}

func TestValidate(t *testing.T) {
	skill := &Skill{
		Name:        "valid-skill",
		Description: "A valid skill",
		Content:     "## Steps\nDo things",
		Version:     "1.0.0",
		Target:      "both",
	}

	errs := Validate(skill)
	if len(errs) != 0 {
		t.Errorf("Valid skill should have no errors, got %v", errs)
	}
}

func TestValidateMissingFields(t *testing.T) {
	skill := &Skill{}
	errs := Validate(skill)
	if len(errs) == 0 {
		t.Error("Empty skill should have validation errors")
	}

	fields := map[string]bool{}
	for _, e := range errs {
		fields[e.Field] = true
	}
	if !fields["name"] {
		t.Error("Should require name")
	}
	if !fields["description"] {
		t.Error("Should require description")
	}
	if !fields["content"] {
		t.Error("Should require content")
	}
}

func TestValidateBadVersion(t *testing.T) {
	skill := &Skill{
		Name:        "test-skill",
		Description: "desc",
		Content:     "content",
		Version:     "not-semver",
	}
	errs := Validate(skill)
	hasVersionErr := false
	for _, e := range errs {
		if e.Field == "version" {
			hasVersionErr = true
		}
	}
	if !hasVersionErr {
		t.Error("Should reject non-semver version")
	}
}

func TestValidateBadTarget(t *testing.T) {
	skill := &Skill{
		Name:        "test",
		Description: "desc",
		Content:     "content",
		Target:      "invalid",
	}
	errs := Validate(skill)
	hasTargetErr := false
	for _, e := range errs {
		if e.Field == "target" {
			hasTargetErr = true
		}
	}
	if !hasTargetErr {
		t.Error("Should reject invalid target")
	}
}

func TestValidateBadName(t *testing.T) {
	skill := &Skill{
		Name:        "INVALID",
		Description: "desc",
		Content:     "content",
	}
	errs := Validate(skill)
	hasNameErr := false
	for _, e := range errs {
		if e.Field == "name" {
			hasNameErr = true
		}
	}
	if !hasNameErr {
		t.Error("Should reject uppercase name")
	}
}

func TestValidateDependencies(t *testing.T) {
	skill := &Skill{
		Name:        "test",
		Description: "desc",
		Content:     "content",
		Dependencies: []SkillDependency{
			{Type: "mcp_server", Name: "github", Required: true},
			{Type: "invalid_type", Name: "test"},
		},
	}
	errs := Validate(skill)
	hasDepErr := false
	for _, e := range errs {
		if e.Field == "dependencies[1].type" {
			hasDepErr = true
		}
	}
	if !hasDepErr {
		t.Error("Should reject invalid dependency type")
	}
}

func TestExportImport(t *testing.T) {
	tmpDir := t.TempDir()
	os.Setenv("HOME", tmpDir)
	defer os.Setenv("HOME", tmpDir)

	skill := &Skill{
		Name:        "export-test",
		Description: "Export test skill",
		Content:     "## Content",
		Author:      "tester",
		Version:     "1.0.0",
		Target:      "both",
		Tags:        []string{"test"},
	}
	Create(skill)

	exportPath := filepath.Join(tmpDir, "export", "export-test.md")
	if err := Export("export-test", exportPath); err != nil {
		t.Fatalf("Export failed: %v", err)
	}

	if _, err := os.Stat(exportPath); os.IsNotExist(err) {
		t.Error("Export file should exist")
	}

	imported, err := Import(exportPath)
	if err != nil {
		t.Fatalf("Import failed: %v", err)
	}
	if imported.Description != "Export test skill" {
		t.Errorf("Expected 'Export test skill', got %s", imported.Description)
	}
}

func TestDryRun(t *testing.T) {
	tmpDir := t.TempDir()
	os.Setenv("HOME", tmpDir)
	defer os.Setenv("HOME", tmpDir)

	skill := &Skill{
		Name:        "dry-run-test",
		Description: "Dry run test",
		Content:     "## Steps\nDo something",
		Version:     "1.0.0",
		Target:      "both",
		Tags:        []string{"test"},
	}
	Create(skill)

	result := DryRun("dry-run-test", "test something")
	if !result.Passed {
		t.Errorf("DryRun should pass, got: %s", result.Message)
	}
}

func TestDryRunMissing(t *testing.T) {
	result := DryRun("nonexistent", "")
	if result.Passed {
		t.Error("DryRun of nonexistent skill should fail")
	}
}

func TestUpdate(t *testing.T) {
	tmpDir := t.TempDir()
	os.Setenv("HOME", tmpDir)
	defer os.Setenv("HOME", tmpDir)

	skill := &Skill{
		Name:        "update-test",
		Description: "Original",
		Content:     "Original content",
		Version:     "1.0.0",
		Target:      "both",
	}
	Create(skill)

	skill.Description = "Updated"
	skill.Content = "Updated content"
	skill.Version = "2.0.0"
	if err := Update(skill); err != nil {
		t.Fatalf("Update failed: %v", err)
	}

	got, err := Get("update-test")
	if err != nil {
		t.Fatalf("Get failed: %v", err)
	}
	if got.Description != "Updated" {
		t.Errorf("Expected 'Updated', got %s", got.Description)
	}
}

func TestBuiltinSkillCount(t *testing.T) {
	if len(builtinSkills) < 5 {
		t.Errorf("Expected at least 5 builtin skills, got %d", len(builtinSkills))
	}

	expectedSkills := []string{"env-setup", "git-workflow", "api-design", "debug-assist", "code-review", "docker-setup", "security-audit", "mcp-setup", "lsp-setup", "workflow-design"}
	for _, name := range expectedSkills {
		found := false
		for _, s := range builtinSkills {
			if s.Name == name {
				found = true
				break
			}
		}
		if !found {
			t.Errorf("Expected builtin skill: %s", name)
		}
	}
}

func TestBuiltinSkillsHaveDependencies(t *testing.T) {
	hasDeps := 0
	for _, s := range builtinSkills {
		if len(s.Dependencies) > 0 {
			hasDeps++
		}
	}
	if hasDeps == 0 {
		t.Error("At least some builtin skills should declare dependencies")
	}
}
