package contacts

import (
	"sort"
	"strings"
	"unicode"

	"github.com/ultisuite/ulti-backend/internal/nextcloud"
)

func normalizeContactSearchText(value string) string {
	var b strings.Builder
	b.Grow(len(value))
	for _, r := range strings.ToLower(strings.TrimSpace(value)) {
		if unicode.IsMark(r) {
			continue
		}
		b.WriteRune(r)
	}
	return b.String()
}

func queryTokens(query string) []string {
	n := normalizeContactSearchText(query)
	if n == "" {
		return nil
	}
	return strings.Fields(n)
}

func fieldMatchScore(haystack, needle string) float64 {
	h := normalizeContactSearchText(haystack)
	n := normalizeContactSearchText(needle)
	if n == "" || !strings.Contains(h, n) {
		return 0
	}
	if h == n {
		return 1
	}
	if strings.HasPrefix(h, n) {
		return 0.95 + 0.05*float64(len(n))/float64(max(len(h), 1))
	}
	for _, word := range strings.FieldsFunc(h, func(r rune) bool {
		return r == ' ' || r == '@' || r == '.' || r == '_' || r == '+' || r == '-'
	}) {
		if word == "" {
			continue
		}
		if strings.HasPrefix(word, n) {
			return 0.88 + 0.07*float64(len(n))/float64(max(len(word), 1))
		}
	}
	idx := strings.Index(h, n)
	positionBonus := 1 - float64(idx)/float64(max(len(h), 1))*0.35
	lengthBonus := float64(len(n)) / float64(max(len(h), 1))
	return 0.42 + 0.28*positionBonus + 0.22*lengthBonus
}

func contactSearchFields(c nextcloud.Contact) []string {
	out := make([]string, 0, 4)
	for _, v := range []string{c.FullName, c.Email, c.Phone, c.Org} {
		if strings.TrimSpace(v) != "" {
			out = append(out, v)
		}
	}
	return out
}

func contactQueryMatchScore(c nextcloud.Contact, query string) float64 {
	fields := contactSearchFields(c)
	tokens := queryTokens(query)
	if len(tokens) == 0 {
		return 0
	}

	var total float64
	for _, token := range tokens {
		best := 0.0
		for _, field := range fields {
			best = max(best, fieldMatchScore(field, token))
			best = max(best, fieldMatchScore(field, query))
		}
		if best == 0 {
			return 0
		}
		total += best
	}
	return total / float64(len(tokens))
}

func rankContactsByQuery(contacts []nextcloud.Contact, query string) []nextcloud.Contact {
	query = strings.TrimSpace(query)
	if query == "" {
		return contacts
	}

	type scored struct {
		contact nextcloud.Contact
		score   float64
	}
	matches := make([]scored, 0, len(contacts))
	for _, c := range contacts {
		if score := contactQueryMatchScore(c, query); score > 0 {
			matches = append(matches, scored{contact: c, score: score})
		}
	}

	sort.SliceStable(matches, func(i, j int) bool {
		if matches[i].score != matches[j].score {
			return matches[i].score > matches[j].score
		}
		return strings.ToLower(matches[i].contact.FullName) < strings.ToLower(matches[j].contact.FullName)
	})

	out := make([]nextcloud.Contact, len(matches))
	for i, m := range matches {
		out[i] = m.contact
	}
	return out
}
