// Package i18n provides internationalization for the CLI.
package i18n

import (
	"strings"
	"text/template"
	"unicode"
)

// GetGrammarData returns the grammar data for the specified language.
// Returns nil if no grammar data is loaded for the language.
func GetGrammarData(lang string) *GrammarData {
	grammarCacheMu.RLock()
	defer grammarCacheMu.RUnlock()
	return grammarCache[lang]
}

// SetGrammarData sets the grammar data for a language.
// Called by the Service when loading locale files.
func SetGrammarData(lang string, data *GrammarData) {
	grammarCacheMu.Lock()
	defer grammarCacheMu.Unlock()
	grammarCache[lang] = data
}

// getVerbForm retrieves a verb form from JSON data.
// Returns empty string if not found, allowing fallback to computed form.
func getVerbForm(lang, verb, form string) string {
	data := GetGrammarData(lang)
	if data == nil || data.Verbs == nil {
		return ""
	}
	verb = strings.ToLower(verb)
	if forms, ok := data.Verbs[verb]; ok {
		switch form {
		case "past":
			return forms.Past
		case "gerund":
			return forms.Gerund
		}
	}
	return ""
}

// getWord retrieves a base word translation from JSON data.
// Returns empty string if not found, allowing fallback to the key itself.
func getWord(lang, word string) string {
	data := GetGrammarData(lang)
	if data == nil || data.Words == nil {
		return ""
	}
	return data.Words[strings.ToLower(word)]
}

// getPunct retrieves a punctuation rule for the language.
// Returns the default if not found.
func getPunct(lang, rule, defaultVal string) string {
	data := GetGrammarData(lang)
	if data == nil {
		return defaultVal
	}
	switch rule {
	case "label":
		if data.Punct.LabelSuffix != "" {
			return data.Punct.LabelSuffix
		}
	case "progress":
		if data.Punct.ProgressSuffix != "" {
			return data.Punct.ProgressSuffix
		}
	}
	return defaultVal
}

// getNounForm retrieves a noun form from JSON data.
// Returns empty string if not found, allowing fallback to computed form.
func getNounForm(lang, noun, form string) string {
	data := GetGrammarData(lang)
	if data == nil || data.Nouns == nil {
		return ""
	}
	noun = strings.ToLower(noun)
	if forms, ok := data.Nouns[noun]; ok {
		switch form {
		case "one":
			return forms.One
		case "other":
			return forms.Other
		case "gender":
			return forms.Gender
		}
	}
	return ""
}

// currentLangForGrammar returns the current language for grammar lookups.
// Uses the default service's language if available.
func currentLangForGrammar() string {
	if svc := Default(); svc != nil {
		return svc.Language()
	}
	return "en-GB"
}

// PastTense returns the past tense of a verb.
// Checks JSON locale data first, then irregular verbs, then applies regular rules.
//
//	PastTense("delete") // "deleted"
//	PastTense("run")    // "ran"
//	PastTense("copy")   // "copied"
func PastTense(verb string) string {
	verb = strings.ToLower(strings.TrimSpace(verb))
	if verb == "" {
		return ""
	}

	// Check JSON data first (for current language)
	if form := getVerbForm(currentLangForGrammar(), verb, "past"); form != "" {
		return form
	}

	// Check irregular verbs
	if forms, ok := irregularVerbs[verb]; ok {
		return forms.Past
	}

	return applyRegularPastTense(verb)
}

// applyRegularPastTense applies regular past tense rules.
func applyRegularPastTense(verb string) string {
	// Already ends in -ed (but not -eed, -ied which need different handling)
	// Words like "proceed", "succeed", "exceed" end in -eed and are NOT past tense
	if strings.HasSuffix(verb, "ed") && len(verb) > 2 {
		// Check if it's actually a past tense suffix (consonant + ed)
		// vs a word root ending (e.g., "proceed" = proc + eed, "feed" = feed)
		thirdFromEnd := verb[len(verb)-3]
		if !isVowel(rune(thirdFromEnd)) && thirdFromEnd != 'e' {
			// Consonant before -ed means it's likely already past tense
			return verb
		}
		// Words ending in vowel + ed (like "proceed") need -ed added
	}

	// Ends in -e: just add -d
	if strings.HasSuffix(verb, "e") {
		return verb + "d"
	}

	// Ends in consonant + y: change y to ied
	if strings.HasSuffix(verb, "y") && len(verb) > 1 {
		prev := rune(verb[len(verb)-2])
		if !isVowel(prev) {
			return verb[:len(verb)-1] + "ied"
		}
	}

	// Ends in single vowel + single consonant (CVC pattern): double consonant
	if len(verb) >= 2 && shouldDoubleConsonant(verb) {
		return verb + string(verb[len(verb)-1]) + "ed"
	}

	// Default: add -ed
	return verb + "ed"
}

// shouldDoubleConsonant checks if the final consonant should be doubled.
// Applies to CVC (consonant-vowel-consonant) endings in single-syllable words
// and stressed final syllables in multi-syllable words.
func shouldDoubleConsonant(verb string) bool {
	if len(verb) < 3 {
		return false
	}

	// Check explicit exceptions
	if noDoubleConsonant[verb] {
		return false
	}

	lastChar := rune(verb[len(verb)-1])
	secondLast := rune(verb[len(verb)-2])

	// Last char must be consonant (not w, x, y)
	if isVowel(lastChar) || lastChar == 'w' || lastChar == 'x' || lastChar == 'y' {
		return false
	}

	// Second to last must be a single vowel
	if !isVowel(secondLast) {
		return false
	}

	// For short words (3-4 chars), always double if CVC pattern
	if len(verb) <= 4 {
		thirdLast := rune(verb[len(verb)-3])
		return !isVowel(thirdLast)
	}

	// For longer words, only double if the pattern is strongly CVC
	// (stressed final syllable). This is a simplification - in practice,
	// most common multi-syllable verbs either:
	// 1. End in a doubled consonant already (e.g., "submit" -> "submitted")
	// 2. Don't double (e.g., "open" -> "opened")
	// We err on the side of not doubling for longer words
	return false
}

// Gerund returns the present participle (-ing form) of a verb.
// Checks JSON locale data first, then irregular verbs, then applies regular rules.
//
//	Gerund("delete")  // "deleting"
//	Gerund("run")     // "running"
//	Gerund("die")     // "dying"
func Gerund(verb string) string {
	verb = strings.ToLower(strings.TrimSpace(verb))
	if verb == "" {
		return ""
	}

	// Check JSON data first (for current language)
	if form := getVerbForm(currentLangForGrammar(), verb, "gerund"); form != "" {
		return form
	}

	// Check irregular verbs
	if forms, ok := irregularVerbs[verb]; ok {
		return forms.Gerund
	}

	return applyRegularGerund(verb)
}

// applyRegularGerund applies regular gerund rules.
func applyRegularGerund(verb string) string {
	// Ends in -ie: change to -ying
	if strings.HasSuffix(verb, "ie") {
		return verb[:len(verb)-2] + "ying"
	}

	// Ends in -e (but not -ee, -ye, -oe): drop e, add -ing
	if strings.HasSuffix(verb, "e") && len(verb) > 1 {
		secondLast := rune(verb[len(verb)-2])
		if secondLast != 'e' && secondLast != 'y' && secondLast != 'o' {
			return verb[:len(verb)-1] + "ing"
		}
	}

	// CVC pattern: double final consonant
	if shouldDoubleConsonant(verb) {
		return verb + string(verb[len(verb)-1]) + "ing"
	}

	// Default: add -ing
	return verb + "ing"
}

// Pluralize returns the plural form of a noun based on count.
// If count is 1, returns the singular form unchanged.
//
//	Pluralize("file", 1)    // "file"
//	Pluralize("file", 5)    // "files"
//	Pluralize("child", 3)   // "children"
//	Pluralize("box", 2)     // "boxes"
func Pluralize(noun string, count int) string {
	if count == 1 {
		return noun
	}
	return PluralForm(noun)
}

// PluralForm returns the plural form of a noun.
// Checks JSON locale data first, then irregular nouns, then applies regular rules.
//
//	PluralForm("file")   // "files"
//	PluralForm("child")  // "children"
//	PluralForm("box")    // "boxes"
func PluralForm(noun string) string {
	noun = strings.TrimSpace(noun)
	if noun == "" {
		return ""
	}

	lower := strings.ToLower(noun)

	// Check JSON data first (for current language)
	if form := getNounForm(currentLangForGrammar(), lower, "other"); form != "" {
		// Preserve original casing if title case
		if unicode.IsUpper(rune(noun[0])) && len(form) > 0 {
			return strings.ToUpper(string(form[0])) + form[1:]
		}
		return form
	}

	// Check irregular nouns
	if plural, ok := irregularNouns[lower]; ok {
		// Preserve original casing if title case
		if unicode.IsUpper(rune(noun[0])) {
			return strings.ToUpper(string(plural[0])) + plural[1:]
		}
		return plural
	}

	return applyRegularPlural(noun)
}

// applyRegularPlural applies regular plural rules.
func applyRegularPlural(noun string) string {
	lower := strings.ToLower(noun)

	// Words ending in -s, -ss, -sh, -ch, -x, -z: add -es
	if strings.HasSuffix(lower, "s") ||
		strings.HasSuffix(lower, "ss") ||
		strings.HasSuffix(lower, "sh") ||
		strings.HasSuffix(lower, "ch") ||
		strings.HasSuffix(lower, "x") ||
		strings.HasSuffix(lower, "z") {
		return noun + "es"
	}

	// Words ending in consonant + y: change y to ies
	if strings.HasSuffix(lower, "y") && len(noun) > 1 {
		prev := rune(lower[len(lower)-2])
		if !isVowel(prev) {
			return noun[:len(noun)-1] + "ies"
		}
	}

	// Words ending in -f or -fe: change to -ves (some exceptions already in irregulars)
	if strings.HasSuffix(lower, "f") {
		return noun[:len(noun)-1] + "ves"
	}
	if strings.HasSuffix(lower, "fe") {
		return noun[:len(noun)-2] + "ves"
	}

	// Words ending in -o preceded by consonant: add -es
	if strings.HasSuffix(lower, "o") && len(noun) > 1 {
		prev := rune(lower[len(lower)-2])
		if !isVowel(prev) {
			// Many exceptions (photos, pianos) - but common tech terms add -es
			if lower == "hero" || lower == "potato" || lower == "tomato" || lower == "echo" || lower == "veto" {
				return noun + "es"
			}
		}
	}

	// Default: add -s
	return noun + "s"
}

// Article returns the appropriate indefinite article ("a" or "an") for a word.
//
//	Article("file")     // "a"
//	Article("error")    // "an"
//	Article("user")     // "a" (sounds like "yoo-zer")
//	Article("hour")     // "an" (silent h)
func Article(word string) string {
	if word == "" {
		return ""
	}

	lower := strings.ToLower(strings.TrimSpace(word))

	// Check for consonant sounds (words starting with vowels but sounding like consonants)
	for key := range consonantSounds {
		if strings.HasPrefix(lower, key) {
			return "a"
		}
	}

	// Check for vowel sounds (words starting with consonants but sounding like vowels)
	for key := range vowelSounds {
		if strings.HasPrefix(lower, key) {
			return "an"
		}
	}

	// Check first letter
	if len(lower) > 0 && isVowel(rune(lower[0])) {
		return "an"
	}

	return "a"
}

// isVowel returns true if the rune is a vowel (a, e, i, o, u).
func isVowel(r rune) bool {
	switch unicode.ToLower(r) {
	case 'a', 'e', 'i', 'o', 'u':
		return true
	}
	return false
}

// Title capitalizes the first letter of each word.
// Uses unicode-aware casing for proper internationalization.
// Word boundaries are defined as any non-letter character (matching strings.Title behavior).
func Title(s string) string {
	var b strings.Builder
	b.Grow(len(s))
	prev := ' ' // Treat start of string as word boundary
	for _, r := range s {
		if !unicode.IsLetter(prev) && unicode.IsLetter(r) {
			b.WriteRune(unicode.ToUpper(r))
		} else {
			b.WriteRune(r)
		}
		prev = r
	}
	return b.String()
}

// Quote wraps a string in double quotes.
func Quote(s string) string {
	return `"` + s + `"`
}

// TemplateFuncs returns the template.FuncMap with all grammar functions.
// Use this to add grammar helpers to your templates.
//
//	tmpl := template.New("").Funcs(i18n.TemplateFuncs())
func TemplateFuncs() template.FuncMap {
	return template.FuncMap{
		"title":      Title,
		"lower":      strings.ToLower,
		"upper":      strings.ToUpper,
		"past":       PastTense,
		"gerund":     Gerund,
		"plural":     Pluralize,
		"pluralForm": PluralForm,
		"article":    Article,
		"quote":      Quote,
	}
}

// Progress returns a progress message for a verb.
// Generates "Verbing..." form using language-specific punctuation.
//
//	Progress("build")  // "Building..."
//	Progress("check")  // "Checking..."
//	Progress("fetch")  // "Fetching..."
func Progress(verb string) string {
	lang := currentLangForGrammar()

	// Try translated word first
	word := getWord(lang, verb)
	if word == "" {
		word = verb
	}

	g := Gerund(word)
	if g == "" {
		return ""
	}

	suffix := getPunct(lang, "progress", "...")
	return Title(g) + suffix
}

// ProgressSubject returns a progress message with a subject.
// Generates "Verbing subject..." form using language-specific punctuation.
//
//	ProgressSubject("build", "project")    // "Building project..."
//	ProgressSubject("check", "config.yaml") // "Checking config.yaml..."
func ProgressSubject(verb, subject string) string {
	lang := currentLangForGrammar()

	// Try translated word first
	word := getWord(lang, verb)
	if word == "" {
		word = verb
	}

	g := Gerund(word)
	if g == "" {
		return ""
	}

	suffix := getPunct(lang, "progress", "...")
	return Title(g) + " " + subject + suffix
}

// ActionResult returns a result message for a completed action.
// Generates "Subject verbed" form.
//
//	ActionResult("delete", "file")  // "File deleted"
//	ActionResult("commit", "changes") // "Changes committed"
func ActionResult(verb, subject string) string {
	p := PastTense(verb)
	if p == "" || subject == "" {
		return ""
	}
	return Title(subject) + " " + p
}

// ActionFailed returns a failure message for an action.
// Generates "Failed to verb subject" form.
//
//	ActionFailed("delete", "file")  // "Failed to delete file"
//	ActionFailed("push", "commits") // "Failed to push commits"
func ActionFailed(verb, subject string) string {
	if verb == "" {
		return ""
	}
	if subject == "" {
		return "Failed to " + verb
	}
	return "Failed to " + verb + " " + subject
}

// Label returns a label with a colon suffix.
// Generates "Word:" form using language-specific punctuation.
// French uses " :" (space before colon), English uses ":".
//
//	Label("status")   // EN: "Status:"  FR: "Statut :"
//	Label("version")  // EN: "Version:" FR: "Version :"
func Label(word string) string {
	if word == "" {
		return ""
	}

	lang := currentLangForGrammar()

	// Try translated word first
	translated := getWord(lang, word)
	if translated == "" {
		translated = word
	}

	suffix := getPunct(lang, "label", ":")
	return Title(translated) + suffix
}
