package cmd

import (
	"encoding/json"
	"fmt"
	"net/url"
	"os"
	"os/exec"
	"sort"
	"strings"
	"unicode"

	"github.com/spf13/cobra"
)

var (
	autoDryRun bool
	autoJSON   bool
	autoLang   string
)

var autoCmd = &cobra.Command{
	Use:   "auto <intent>",
	Short: "Route natural-language intent to the best Devour command",
	Long: `Auto-classify intent and run the best matching command (get/scrape/ask/quality).

Examples:
  devour auto "how to parse json in go"
  devour auto "https://pkg.go.dev/net/http"
  devour auto "check code quality" --dry-run
  devour auto "what is useEffect" --lang react`,
	Args: cobra.MinimumNArgs(1),
	RunE: runAuto,
}

func init() {
	autoCmd.Flags().BoolVar(&autoDryRun, "dry-run", false, "print selected command without executing")
	autoCmd.Flags().BoolVar(&autoJSON, "json", false, "output route decision as JSON")
	autoCmd.Flags().StringVar(&autoLang, "lang", "", "optional language override for ask/get routes")
}

type autoDecision struct {
	Intent  string   `json:"intent"`
	Route   string   `json:"route"`
	Reason  string   `json:"reason"`
	Command []string `json:"command"`
}

func runAuto(cmd *cobra.Command, args []string) error {
	intent := strings.TrimSpace(strings.Join(args, " "))
	if intent == "" {
		return fmt.Errorf("intent is required")
	}

	decision, err := classifyIntent(intent, strings.TrimSpace(autoLang))
	if err != nil {
		return err
	}

	if autoJSON {
		enc := json.NewEncoder(cmd.OutOrStdout())
		enc.SetIndent("", "  ")
		return enc.Encode(decision)
	}

	fmt.Printf("Route: %s\n", decision.Route)
	fmt.Printf("Reason: %s\n", decision.Reason)
	fmt.Printf("Command: devour %s\n", strings.Join(decision.Command, " "))

	if autoDryRun {
		return nil
	}

	exe, err := os.Executable()
	if err != nil {
		return err
	}
	run := exec.Command(exe, decision.Command...)
	run.Stdout = cmd.OutOrStdout()
	run.Stderr = cmd.ErrOrStderr()
	return run.Run()
}

func classifyIntent(intent, langOverride string) (*autoDecision, error) {
	lower := strings.ToLower(intent)
	trimmed := strings.TrimSpace(intent)

	if u, err := url.Parse(trimmed); err == nil && (u.Scheme == "http" || u.Scheme == "https") {
		route := []string{"scrape", trimmed}
		return &autoDecision{Intent: intent, Route: "scrape", Reason: "detected URL input", Command: route}, nil
	}

	if strings.Contains(lower, "quality") || strings.Contains(lower, "technical debt") || strings.Contains(lower, "lint") || strings.Contains(lower, "code smell") {
		route := []string{"quality", "status"}
		if strings.Contains(lower, "scan") {
			route = []string{"quality", "scan", "."}
		}
		return &autoDecision{Intent: intent, Route: "quality", Reason: "detected quality-analysis intent", Command: route}, nil
	}

	language := strings.TrimSpace(langOverride)
	if language == "" {
		language = inferLanguageFromText(lower)
	}
	if language != "" {
		if canonical, ok := normalizeLanguage(language); ok {
			language = canonical
		} else {
			language = ""
		}
	}

	if strings.Contains(lower, "?") || strings.Contains(lower, "how") || strings.Contains(lower, "why") || strings.Contains(lower, "what") {
		if language == "" {
			language = "go"
		}
		route := []string{"ask", "--lang", language, intent, "--format", "text"}
		return &autoDecision{Intent: intent, Route: "ask", Reason: "question-style intent", Command: route}, nil
	}

	if language == "" {
		language = "go"
	}
	keyword := inferKeyword(intent)
	if canonical, ok := normalizeLanguage(keyword); ok && canonical == language {
		keyword = "overview"
	}
	route := []string{"get", language, keyword}
	return &autoDecision{Intent: intent, Route: "get", Reason: "default docs retrieval route", Command: route}, nil
}

func inferLanguageFromText(text string) string {
	text = strings.ToLower(text)
	if strings.Contains(text, "c#") {
		return "csharp"
	}
	if strings.Contains(text, "next.js") {
		return "nextjs"
	}

	tokens := strings.FieldsFunc(text, func(r rune) bool {
		return !(unicode.IsLetter(r) || unicode.IsDigit(r))
	})
	tokenSet := make(map[string]bool, len(tokens))
	for _, tok := range tokens {
		if tok != "" {
			tokenSet[tok] = true
		}
	}

	aliases := make([]string, 0, len(languageAliases()))
	for alias := range languageAliases() {
		aliases = append(aliases, alias)
	}
	sort.Slice(aliases, func(i, j int) bool {
		return len(aliases[i]) > len(aliases[j])
	})

	for _, alias := range aliases {
		if tokenSet[alias] {
			return alias
		}
	}
	return ""
}

func inferKeyword(intent string) string {
	words := strings.Fields(strings.ToLower(intent))
	stop := map[string]bool{
		"get": true, "docs": true, "documentation": true, "about": true, "for": true, "on": true,
		"the": true, "a": true, "an": true, "show": true, "me": true, "please": true,
	}
	for _, w := range words {
		w = strings.Trim(w, ",.!?;:")
		if w == "" || stop[w] || len(w) < 2 {
			continue
		}
		return w
	}
	return "overview"
}
