package country

import (
	"sort"
	"strings"
)

type entry struct {
	Code     string
	Name     string
	Keywords []string
}

var (
	sortedKeywords []keywordMatch
	nameByCode     map[string]string
)

type keywordMatch struct {
	keyword string
	code    string
}

func init() {
	nameByCode = make(map[string]string, len(catalog))
	for _, e := range catalog {
		nameByCode[e.Code] = e.Name
		for _, kw := range e.Keywords {
			sortedKeywords = append(sortedKeywords, keywordMatch{keyword: strings.ToLower(kw), code: e.Code})
		}
	}
	sort.Slice(sortedKeywords, func(i, j int) bool {
		return len(sortedKeywords[i].keyword) > len(sortedKeywords[j].keyword)
	})
}

// Detect returns ISO-like country code from node name, or empty string.
func Detect(name string) string {
	lower := strings.ToLower(strings.TrimSpace(name))
	if lower == "" {
		return ""
	}
	normalized := strings.NewReplacer(
		"_", " ", "-", " ", "|", " ", "/", " ", "[", " ", "]", " ",
		"(", " ", ")", " ", "【", " ", "】", " ", "（", " ", "）", " ",
	).Replace(lower)
	padded := " " + normalized + " "
	for _, m := range sortedKeywords {
		kw := m.keyword
		if len(kw) <= 2 {
			continue
		}
		if keywordMatches(padded, kw) {
			return m.code
		}
	}
	for _, tok := range strings.Fields(normalized) {
		tok = strings.TrimSpace(tok)
		if tok == "" {
			continue
		}
		if len(tok) == 2 {
			if _, ok := nameByCode[strings.ToUpper(tok)]; ok {
				return strings.ToUpper(tok)
			}
			continue
		}
		if len(tok) > 2 {
			prefix := strings.ToUpper(tok[:2])
			if _, ok := nameByCode[prefix]; !ok {
				continue
			}
			suffix := tok[2:]
			if suffix == "" {
				continue
			}
			allDigits := true
			for _, r := range suffix {
				if r < '0' || r > '9' {
					allDigits = false
					break
				}
			}
			if allDigits {
				return prefix
			}
		}
	}
	return ""
}

// Name returns Chinese display name for a country code.
func Name(code string) string {
	if n, ok := nameByCode[strings.ToUpper(code)]; ok {
		return n
	}
	return code
}

// GroupRuntime returns Mihomo proxy-group name for a country pool.
func GroupRuntime(code string) string {
	return "country_" + strings.ToUpper(strings.TrimSpace(code))
}

// TargetAlias returns rule target alias stored in DB, e.g. country:HK.
func TargetAlias(code string) string {
	return "country:" + strings.ToUpper(strings.TrimSpace(code))
}

// ParseTargetAlias parses country:XX to code, ok false if not a country target.
func ParseTargetAlias(target string) (string, bool) {
	if !strings.HasPrefix(strings.ToLower(target), "country:") {
		return "", false
	}
	code := strings.TrimSpace(target[len("country:"):])
	if code == "" {
		return "", false
	}
	return strings.ToUpper(code), true
}

// All returns catalog entries for UI.
func All() []struct{ Code, Name string } {
	out := make([]struct{ Code, Name string }, 0, len(catalog))
	for _, e := range catalog {
		out = append(out, struct{ Code, Name string }{Code: e.Code, Name: e.Name})
	}
	return out
}

func keywordMatches(text, kw string) bool {
	if !strings.Contains(text, kw) {
		return false
	}
	if containsHan(kw) {
		return true
	}
	start := 0
	for {
		idx := strings.Index(text[start:], kw)
		if idx < 0 {
			return false
		}
		pos := start + idx
		before := pos == 0 || !isASCIIAlphaNum(text[pos-1])
		afterEnd := pos + len(kw)
		after := afterEnd >= len(text) || !isASCIIAlphaNum(text[afterEnd])
		if before && after {
			return true
		}
		start = pos + 1
	}
}

func isASCIIAlphaNum(b byte) bool {
	return (b >= 'a' && b <= 'z') || (b >= '0' && b <= '9')
}

func containsHan(s string) bool {
	for _, r := range s {
		if r >= 0x4e00 && r <= 0x9fff {
			return true
		}
	}
	return false
}
