package skills

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"time"
)

type ImprovementSuggestion struct {
	Type        string    `json:"type"`
	Section     string    `json:"section"`
	Current     string    `json:"current"`
	Suggested   string    `json:"suggested"`
	Reason      string    `json:"reason"`
	Confidence  float64   `json:"confidence"`
	CreatedAt   time.Time `json:"created_at"`
}

type ImprovementHistory struct {
	SkillName   string                  `json:"skill_name"`
	Version     string                  `json:"version"`
	Improvements []ImprovementSuggestion `json:"improvements"`
	AppliedAt   time.Time               `json:"applied_at"`
	Result      string                  `json:"result"`
}

type SkillImprover struct {
	historyDir string
}

func NewSkillImprover() (*SkillImprover, error) {
	dir, err := improvementHistoryDir()
	if err != nil {
		return nil, err
	}
	return &SkillImprover{historyDir: dir}, nil
}

func (si *SkillImprover) Analyze(skill *Skill, conversationContext string) ([]ImprovementSuggestion, error) {
	var suggestions []ImprovementSuggestion

	if skill.Content == "" {
		return nil, fmt.Errorf("skill has no content to analyze")
	}

	suggestions = append(suggestions, si.checkMissingSections(skill)...)
	suggestions = append(suggestions, si.checkErrorHandling(skill)...)
	suggestions = append(suggestions, si.checkStepCompleteness(skill)...)
	suggestions = append(suggestions, si.analyzeContextRelevance(skill, conversationContext)...)

	return suggestions, nil
}

func (si *SkillImprover) ApplyImprovement(skillName string, suggestion ImprovementSuggestion) error {
	skill, err := Get(skillName)
	if err != nil {
		return fmt.Errorf("get skill: %w", err)
	}

	switch suggestion.Section {
	case "content":
		skill.Content = applyContentSuggestion(skill.Content, suggestion)
	case "description":
		skill.Description = suggestion.Suggested
	default:
		skill.Content = applyContentSuggestion(skill.Content, suggestion)
	}

	now := time.Now()
	skill.LastImprovedAt = &now
	skill.ImprovementCount++

	if err := Update(skill); err != nil {
		return fmt.Errorf("update skill: %w", err)
	}

	history := ImprovementHistory{
		SkillName:    skillName,
		Version:      skill.Version,
		Improvements: []ImprovementSuggestion{suggestion},
		AppliedAt:    now,
		Result:       "applied",
	}

	return si.saveHistory(&history)
}

func (si *SkillImprover) GetHistory(skillName string) ([]ImprovementHistory, error) {
	if err := os.MkdirAll(si.historyDir, 0755); err != nil {
		return nil, err
	}

	entries, err := os.ReadDir(si.historyDir)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}
		return nil, err
	}

	var histories []ImprovementHistory
	for _, e := range entries {
		if e.IsDir() || !strings.HasSuffix(e.Name(), ".json") {
			continue
		}
		if skillName != "" && !strings.HasPrefix(e.Name(), skillName+"_") {
			continue
		}

		data, err := os.ReadFile(filepath.Join(si.historyDir, e.Name()))
		if err != nil {
			continue
		}
		var h ImprovementHistory
		if err := json.Unmarshal(data, &h); err != nil {
			continue
		}
		histories = append(histories, h)
	}

	return histories, nil
}

func (si *SkillImprover) checkMissingSections(skill *Skill) []ImprovementSuggestion {
	var suggestions []ImprovementSuggestion
	content := strings.ToLower(skill.Content)

	requiredSections := []struct {
		keyword string
		label   string
	}{
		{"error handling", "Error Handling"},
		{"steps", "Steps"},
		{"when to", "Activation"},
	}

	for _, req := range requiredSections {
		if !strings.Contains(content, req.keyword) {
			suggestions = append(suggestions, ImprovementSuggestion{
				Type:       "missing_section",
				Section:    "content",
				Current:    "",
				Suggested:  fmt.Sprintf("Add a '%s' section", req.label),
				Reason:     fmt.Sprintf("Skill is missing a '%s' section which is important for completeness", req.label),
				Confidence: 0.8,
				CreatedAt:  time.Now(),
			})
		}
	}

	return suggestions
}

func (si *SkillImprover) checkErrorHandling(skill *Skill) []ImprovementSuggestion {
	var suggestions []ImprovementSuggestion
	content := strings.ToLower(skill.Content)

	if !strings.Contains(content, "error") && !strings.Contains(content, "fail") {
		suggestions = append(suggestions, ImprovementSuggestion{
			Type:       "missing_error_handling",
			Section:    "content",
			Current:    "",
			Suggested:  "Add error handling guidance covering common failure modes",
			Reason:     "Skill lacks error handling guidance, which may lead to poor user experience when things go wrong",
			Confidence: 0.85,
			CreatedAt:  time.Now(),
		})
	}

	return suggestions
}

func (si *SkillImprover) checkStepCompleteness(skill *Skill) []ImprovementSuggestion {
	var suggestions []ImprovementSuggestion

	lines := strings.Split(skill.Content, "\n")
	stepCount := 0
	for _, line := range lines {
		trimmed := strings.TrimSpace(line)
		if strings.HasPrefix(trimmed, "1.") || strings.HasPrefix(trimmed, "Step 1") {
			stepCount++
		}
	}

	if stepCount == 0 && len(lines) > 10 {
		suggestions = append(suggestions, ImprovementSuggestion{
			Type:       "no_clear_steps",
			Section:    "content",
			Current:    "",
			Suggested:  "Add numbered step-by-step instructions for clarity",
			Reason:     "Long skill content without clear step-by-step structure can be hard to follow",
			Confidence: 0.7,
			CreatedAt:  time.Now(),
		})
	}

	return suggestions
}

func (si *SkillImprover) analyzeContextRelevance(skill *Skill, context string) []ImprovementSuggestion {
	if context == "" {
		return nil
	}

	var suggestions []ImprovementSuggestion
	contextLower := strings.ToLower(context)
	tags := skill.Tags
	relevance := 0

	for _, tag := range tags {
		if strings.Contains(contextLower, strings.ToLower(tag)) {
			relevance++
		}
	}

	if len(tags) > 0 && relevance == 0 && skill.Category != "" && !strings.Contains(contextLower, strings.ToLower(skill.Category)) {
		suggestions = append(suggestions, ImprovementSuggestion{
			Type:       "tag_relevance",
			Section:    "tags",
			Current:    strings.Join(tags, ", "),
			Suggested:  "Review tags for better context matching",
			Reason:     "Current tags do not match recent conversation context, suggesting tags may need updating",
			Confidence: 0.5,
			CreatedAt:  time.Now(),
		})
	}

	return suggestions
}

func applyContentSuggestion(content string, suggestion ImprovementSuggestion) string {
	switch suggestion.Type {
	case "missing_section":
		return content + "\n\n## " + strings.Title(suggestion.Type) + "\n\n" + suggestion.Suggested + ".\n"
	case "missing_error_handling":
		return content + "\n\n## Error Handling\n\n- Handle common failure modes gracefully\n- Provide clear error messages\n- Suggest alternative approaches\n"
	case "no_clear_steps":
		return "## Steps\n\n1. Review the skill context\n2. Apply the appropriate pattern\n3. Verify the result\n\n" + content
	default:
		return content
	}
}

func (si *SkillImprover) saveHistory(history *ImprovementHistory) error {
	if err := os.MkdirAll(si.historyDir, 0755); err != nil {
		return err
	}

	filename := fmt.Sprintf("%s_%s.json", history.SkillName, history.AppliedAt.Format("20060102-150405"))
	data, err := json.MarshalIndent(history, "", "  ")
	if err != nil {
		return err
	}

	return os.WriteFile(filepath.Join(si.historyDir, filename), data, 0644)
}

func improvementHistoryDir() (string, error) {
	dir, err := SkillsDir()
	if err != nil {
		return "", err
	}
	return filepath.Join(filepath.Dir(dir), ".muyue", "improvements"), nil
}
