package search

import (
	"sort"
	"strings"

	"github.com/animenotifier/notify.moe/arn"
	"github.com/animenotifier/notify.moe/arn/stringutils"
)

// Characters searches all characters.
func Characters(originalTerm string, maxLength int) []*arn.Character {
	if maxLength == 0 {
		return nil
	}

	term := strings.ToLower(stringutils.RemoveSpecialCharacters(originalTerm))
	termHasUnicode := stringutils.ContainsUnicodeLetters(term)
	results := make([]*Result, 0, maxLength)

	for character := range arn.StreamCharacters() {
		if character.ID == originalTerm {
			return []*arn.Character{character}
		}

		if character.Image.Extension == "" {
			continue
		}

		// Canonical
		text := strings.ToLower(stringutils.RemoveSpecialCharacters(character.Name.Canonical))

		if text == term {
			results = append(results, &Result{
				obj:        character,
				similarity: float64(20 + len(character.Likes)),
			})
			continue
		}

		spaceCount := 0
		start := 0
		found := false

		for i := 0; i <= len(text); i++ {
			if i == len(text) || text[i] == ' ' {
				part := text[start:i]

				if part == term {
					results = append(results, &Result{
						obj:        character,
						similarity: float64(10 - spaceCount*5 + len(character.Likes)),
					})

					found = true
					break
				}

				start = i + 1
				spaceCount++
			}
		}

		if found {
			continue
		}

		// Japanese
		if termHasUnicode {
			if strings.Contains(character.Name.Japanese, term) {
				results = append(results, &Result{
					obj:        character,
					similarity: float64(len(character.Likes)),
				})
				continue
			}
		}
	}

	// Sort
	sort.Slice(results, func(i, j int) bool {
		similarityA := results[i].similarity
		similarityB := results[j].similarity

		if similarityA == similarityB {
			characterA := results[i].obj.(*arn.Character)
			characterB := results[j].obj.(*arn.Character)

			if characterA.Name.Canonical == characterB.Name.Canonical {
				return characterA.ID < characterB.ID
			}

			return characterA.Name.Canonical < characterB.Name.Canonical
		}

		return similarityA > similarityB
	})

	// Limit
	if len(results) >= maxLength {
		results = results[:maxLength]
	}

	// Final list
	final := make([]*arn.Character, len(results))

	for i, result := range results {
		final[i] = result.obj.(*arn.Character)
	}

	return final
}