package cmd

import (
	"fmt"
	"sort"
	"strings"

	"github.com/spf13/cobra"
)

var getCmd = &cobra.Command{
	Use:   "get <language> <keyword>",
	Short: "Get documentation for a language/framework",
	Long: `Quickly fetch documentation for popular languages and frameworks.
This command maps language+keyword combinations to official documentation sources.

Examples:
  devour get go http
  devour get python asyncio
  devour get react hooks
  devour get nextjs routing
  devour get express middleware`,
	Args: cobra.ExactArgs(2),
	RunE: runGet,
}

func init() {
	getCmd.Flags().StringVarP(&scrapeFormat, "format", "f", "json", "output format (json, markdown)")
	getCmd.Flags().StringVarP(&scrapeOutput, "output", "o", "", "output directory (default: configured docs dir)")
	getCmd.Flags().IntVar(&scrapeConcurrency, "concurrency", 10, "parallel scraping workers")
}

func runGet(cmd *cobra.Command, args []string) error {
	langIn := strings.ToLower(strings.TrimSpace(args[0]))
	keyword := strings.TrimSpace(args[1])
	if keyword == "" {
		return fmt.Errorf("keyword is required")
	}

	language, ok := normalizeLanguage(langIn)
	if !ok {
		return fmt.Errorf("unsupported language: %s. Supported: %s", langIn, strings.Join(supportedLanguages(), ", "))
	}

	url, err := constructDocURL(language, keyword)
	if err != nil {
		return fmt.Errorf("construct documentation url for %s/%s: %w", language, keyword, err)
	}

	sourceType := mapLanguageToType(language)
	scrapeType = sourceType

	fmt.Printf("Getting docs for: %s %s\n", language, keyword)
	fmt.Printf("URL: %s\n", url)
	fmt.Printf("Type: %s\n\n", sourceType)

	if err := runScrape(cmd, []string{url}); err != nil {
		return fmt.Errorf("run scrape for get command: %w", err)
	}
	return nil
}

func constructDocURL(language, keyword string) (string, error) {
	language = strings.ToLower(strings.TrimSpace(language))
	keyword = strings.TrimSpace(keyword)
	lowerKeyword := strings.ToLower(keyword)

	switch language {
	case "go":
		return fmt.Sprintf("https://pkg.go.dev/%s", lowerKeyword), nil
	case "rust":
		return fmt.Sprintf("https://docs.rs/%s/latest/%s/", lowerKeyword, lowerKeyword), nil
	case "python":
		if lowerKeyword == "stdlib" || lowerKeyword == "standard" {
			return "https://docs.python.org/3/library/", nil
		}
		return fmt.Sprintf("https://docs.python.org/3/library/%s.html", lowerKeyword), nil
	case "java":
		return fmt.Sprintf("https://docs.oracle.com/javase/8/docs/api/%s.html", lowerKeyword), nil
	case "spring":
		if lowerKeyword == "mcp" || lowerKeyword == "mcp-overview" {
			return "https://docs.spring.io/spring-ai/reference/api/mcp/mcp-overview.html", nil
		}
		return fmt.Sprintf("https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#%s", lowerKeyword), nil
	case "typescript":
		return fmt.Sprintf("https://www.typescriptlang.org/docs/handbook/%s.html", lowerKeyword), nil
	case "react":
		if lowerKeyword == "hooks" {
			return "https://react.dev/reference/react", nil
		}
		return fmt.Sprintf("https://react.dev/reference/react/%s", lowerKeyword), nil
	case "vue":
		if strings.Contains(lowerKeyword, "api") {
			return "https://vuejs.org/api/", nil
		}
		return fmt.Sprintf("https://vuejs.org/guide/%s.html", lowerKeyword), nil
	case "nuxt":
		return fmt.Sprintf("https://nuxt.com/docs/guide/%s", lowerKeyword), nil
	case "docker":
		return fmt.Sprintf("https://docs.docker.com/%s", lowerKeyword), nil
	case "cloudflare":
		return fmt.Sprintf("https://developers.cloudflare.com/%s", lowerKeyword), nil
	case "astro":
		path := lowerKeyword
		switch lowerKeyword {
		case "components":
			path = "basics/astro-components"
		case "api":
			path = "reference/api-reference"
		case "install", "setup", "getting-started":
			path = "install-and-setup"
		default:
			if !strings.Contains(lowerKeyword, "/") {
				path = "guides/" + lowerKeyword
			}
		}
		return fmt.Sprintf("https://docs.astro.build/en/%s/", path), nil
	case "csharp":
		lowerKeyword = strings.TrimPrefix(lowerKeyword, "/")
		if strings.Contains(lowerKeyword, "regex") || strings.Contains(lowerKeyword, "regular-expression") {
			return "https://learn.microsoft.com/en-us/dotnet/standard/base-types/regular-expressions", nil
		}
		return fmt.Sprintf("https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/%s", lowerKeyword), nil
	case "kotlin":
		lowerKeyword = strings.TrimPrefix(lowerKeyword, "/")
		if lowerKeyword == "regex" || lowerKeyword == "regexp" {
			lowerKeyword = "strings"
		}
		if strings.HasSuffix(lowerKeyword, ".html") {
			return fmt.Sprintf("https://kotlinlang.org/docs/%s", lowerKeyword), nil
		}
		return fmt.Sprintf("https://kotlinlang.org/docs/%s.html", lowerKeyword), nil
	case "php":
		lowerKeyword = strings.TrimPrefix(lowerKeyword, "/")
		if strings.HasSuffix(lowerKeyword, ".php") || strings.Contains(lowerKeyword, "function.") || strings.Contains(lowerKeyword, "book.") {
			return fmt.Sprintf("https://www.php.net/manual/en/%s", lowerKeyword), nil
		}
		return fmt.Sprintf("https://www.php.net/manual/en/book.%s.php", lowerKeyword), nil
	case "ruby":
		keyword = strings.TrimPrefix(keyword, "/")
		switch strings.ToLower(keyword) {
		case "regex", "regexp":
			keyword = "Regexp"
		case "string":
			keyword = "String"
		case "array":
			keyword = "Array"
		default:
			if !strings.Contains(keyword, "::") && len(keyword) > 0 {
				keyword = strings.ToUpper(keyword[:1]) + strings.ToLower(keyword[1:])
			}
		}
		return fmt.Sprintf("https://ruby-doc.org/core/%s.html", keyword), nil
	case "elixir":
		keyword = strings.TrimPrefix(keyword, "/")
		switch strings.ToLower(keyword) {
		case "regex":
			keyword = "Regex"
		case "string":
			keyword = "String"
		case "enum":
			keyword = "Enum"
		default:
			if len(keyword) > 0 {
				keyword = strings.ToUpper(keyword[:1]) + strings.ToLower(keyword[1:])
			}
		}
		return fmt.Sprintf("https://hexdocs.pm/elixir/%s.html", keyword), nil
	case "nextjs":
		if strings.Contains(lowerKeyword, "routing") {
			return "https://nextjs.org/docs/app/building-your-application/routing", nil
		}
		if strings.Contains(lowerKeyword, "data") || strings.Contains(lowerKeyword, "fetch") {
			return "https://nextjs.org/docs/app/building-your-application/data-fetching", nil
		}
		return "https://nextjs.org/docs", nil
	case "svelte":
		if strings.Contains(lowerKeyword, "kit") {
			return "https://svelte.dev/docs/kit", nil
		}
		return "https://svelte.dev/docs/svelte/overview", nil
	case "angular":
		if strings.Contains(lowerKeyword, "http") {
			return "https://angular.dev/guide/http", nil
		}
		return "https://angular.dev/guide/components", nil
	case "remix":
		if strings.Contains(lowerKeyword, "route") {
			return "https://v2.remix.run/docs/file-conventions/routes", nil
		}
		return "https://v2.remix.run/docs", nil
	case "solid":
		// Solid docs are published from this repository and include solid-router content.
		return "https://github.com/solidjs/solid-docs", nil
	case "express":
		if strings.Contains(lowerKeyword, "routing") {
			return "https://expressjs.com/en/guide/routing.html", nil
		}
		if strings.Contains(lowerKeyword, "middleware") {
			return "https://expressjs.com/en/guide/using-middleware.html", nil
		}
		return "https://expressjs.com/en/guide/writing-middleware.html", nil
	default:
		return "", fmt.Errorf("unsupported language: %s. Supported: %s", language, strings.Join(supportedLanguages(), ", "))
	}
}

func mapLanguageToType(language string) string {
	language, _ = normalizeLanguage(language)
	switch language {
	case "go":
		return "godocs"
	case "rust":
		return "rustdocs"
	case "python":
		return "pythondocs"
	case "java":
		return "javadocs"
	case "spring":
		return "springdocs"
	case "typescript":
		return "tsdocs"
	case "react":
		return "reactdocs"
	case "vue":
		return "vuedocs"
	case "nuxt":
		return "nuxtdocs"
	case "docker":
		return "dockerdocs"
	case "cloudflare":
		return "cloudflaredocs"
	case "astro":
		return "astrodocs"
	case "csharp", "kotlin", "php", "ruby", "elixir", "nextjs", "svelte", "angular", "remix", "express":
		return "url"
	case "solid":
		return "github"
	default:
		return ""
	}
}

func normalizeLanguage(language string) (string, bool) {
	language = strings.ToLower(strings.TrimSpace(language))
	if language == "" {
		return "", false
	}
	if canonical, ok := languageAliases()[language]; ok {
		return canonical, true
	}
	return "", false
}

func languageAliases() map[string]string {
	return map[string]string{
		"go":         "go",
		"golang":     "go",
		"rust":       "rust",
		"python":     "python",
		"py":         "python",
		"java":       "java",
		"spring":     "spring",
		"typescript": "typescript",
		"ts":         "typescript",
		"react":      "react",
		"vue":        "vue",
		"nuxt":       "nuxt",
		"docker":     "docker",
		"cloudflare": "cloudflare",
		"cf":         "cloudflare",
		"astro":      "astro",
		"csharp":     "csharp",
		"cs":         "csharp",
		"kotlin":     "kotlin",
		"kt":         "kotlin",
		"php":        "php",
		"ruby":       "ruby",
		"rb":         "ruby",
		"elixir":     "elixir",
		"ex":         "elixir",
		"next":       "nextjs",
		"nextjs":     "nextjs",
		"svelte":     "svelte",
		"angular":    "angular",
		"ng":         "angular",
		"remix":      "remix",
		"solid":      "solid",
		"solidjs":    "solid",
		"express":    "express",
		"expressjs":  "express",
	}
}

func supportedLanguages() []string {
	seen := map[string]bool{}
	out := make([]string, 0)
	for _, canonical := range languageAliases() {
		if seen[canonical] {
			continue
		}
		seen[canonical] = true
		out = append(out, canonical)
	}
	sort.Strings(out)
	return out
}
