package catalog

import (
	"fmt"
	"rubric/modules/devopsmodules"
	"rubric/modules/gomodules"
	"rubric/modules/infosecmodules"
	"rubric/modules/linuxmodules"
	"rubric/utils"
	"sort"
	"strings"
)

// SearchResult — одно совпадение поиска
type SearchResult struct {
	BookKey   string
	BookTitle string
	ModKey    string
	ModTitle  string
	ModDesc   string
	RunFunc   func()
	Score     int // количество совпадений
}

// searchEntry — унифицированная запись для поиска
type searchEntry struct {
	bookKey   string
	bookTitle string
	modKey    string
	modTitle  string
	modDesc   string
	keywords  []string
	run       func()
}

// Все записи для поиска — строим один раз
var allEntries = buildSearchIndex()

func buildSearchIndex() []searchEntry {
	entries := []searchEntry{}

	// Go
	for _, m := range gomodules.All {
		entries = append(entries, searchEntry{
			bookKey: "go", bookTitle: "Go",
			modKey: m.Key, modTitle: m.Title, modDesc: m.Description,
			keywords: gomodules.Keywords(m.Key),
			run:      m.Run,
		})
	}

	// Linux
	for _, m := range linuxmodules.All {
		entries = append(entries, searchEntry{
			bookKey: "linux", bookTitle: "Linux",
			modKey: m.Key, modTitle: m.Title, modDesc: m.Description,
			keywords: linuxmodules.Keywords(m.Key),
			run:      m.Run,
		})
	}

	// DevOps
	for _, m := range devopsmodules.All {
		entries = append(entries, searchEntry{
			bookKey: "devops", bookTitle: "DevOps",
			modKey: m.Key, modTitle: m.Title, modDesc: m.Description,
			keywords: devopsmodules.Keywords(m.Key),
			run:      m.Run,
		})
	}

	// InfoSec
	for _, m := range infosecmodules.All {
		entries = append(entries, searchEntry{
			bookKey: "infosec", bookTitle: "InfoSec",
			modKey: m.Key, modTitle: m.Title, modDesc: m.Description,
			keywords: infosecmodules.Keywords(m.Key),
			run:      m.Run,
		})
	}

	return entries
}

// Search выполняет поиск по запросу, возвращает отсортированные результаты
func Search(query string) []SearchResult {
	query = strings.ToLower(strings.TrimSpace(query))
	if query == "" {
		return nil
	}

	// Разбить запрос на слова
	words := strings.Fields(query)

	var results []SearchResult

	for _, e := range allEntries {
		score := 0

		// Текст для поиска: ключевые слова + название + описание
		searchText := strings.ToLower(
			e.modKey + " " +
				e.modTitle + " " +
				e.modDesc + " " +
				strings.Join(e.keywords, " "),
		)

		for _, word := range words {
			if len(word) < 2 {
				continue
			}
			// Точное совпадение в ключевых словах — больший вес
			for _, kw := range e.keywords {
				if strings.EqualFold(kw, word) {
					score += 10
				} else if strings.Contains(strings.ToLower(kw), word) {
					score += 5
				}
			}
			// Совпадение в ключе модуля
			if strings.Contains(e.modKey, word) {
				score += 8
			}
			// Совпадение в названии
			if strings.Contains(strings.ToLower(e.modTitle), word) {
				score += 6
			}
			// Совпадение в описании
			if strings.Contains(strings.ToLower(e.modDesc), word) {
				score += 3
			}
			// Совпадение в общем тексте
			if strings.Contains(searchText, word) && score == 0 {
				score += 1
			}
		}

		if score > 0 {
			results = append(results, SearchResult{
				BookKey:   e.bookKey,
				BookTitle: e.bookTitle,
				ModKey:    e.modKey,
				ModTitle:  e.modTitle,
				ModDesc:   e.modDesc,
				RunFunc:   e.run,
				Score:     score,
			})
		}
	}

	// Сортировать по релевантности
	sort.Slice(results, func(i, j int) bool {
		return results[i].Score > results[j].Score
	})

	return results
}

// ShowSearchResults выводит результаты поиска
func ShowSearchResults(query string, results []SearchResult) {
	fmt.Printf("\n%s%s🔍 Поиск: \"%s\"%s\n\n",
		utils.ColorBold, utils.ColorCyan, query, utils.ColorReset)

	if len(results) == 0 {
		fmt.Printf("  %sНичего не найдено по запросу \"%s\"%s\n\n",
			utils.ColorDim, query, utils.ColorReset)
		fmt.Printf("  Попробуйте другие слова. Примеры: ansible, nmap, docker, jwt, selinux\n\n")
		return
	}

	fmt.Printf("  %s%sНайдено разделов: %d%s\n\n",
		utils.ColorBold, utils.ColorGreen, len(results), utils.ColorReset)

	for i, r := range results {
		bookColor := bookColorFor(r.BookKey)
		fmt.Printf("  %s%s%2d.%s  %s[%s]%s %s%-12s%s — %s%s%s\n",
			utils.ColorBold, utils.ColorCyan, i+1, utils.ColorReset,
			utils.ColorBold+bookColor, r.BookTitle, utils.ColorReset,
			utils.ColorYellow+utils.ColorBold, r.ModKey, utils.ColorReset,
			utils.ColorWhite, r.ModTitle, utils.ColorReset,
		)
		fmt.Printf("        %s%s%s\n",
			utils.ColorDim, r.ModDesc, utils.ColorReset)
		fmt.Printf("        %sОткрыть: ./rubric -book %s -topic %s%s\n\n",
			utils.ColorDim, r.BookKey, r.ModKey, utils.ColorReset)
	}
}

func bookColorFor(key string) string {
	switch key {
	case "go":
		return utils.ColorCyan
	case "linux":
		return utils.ColorGreen
	case "devops":
		return utils.ColorMagenta
	case "infosec":
		return utils.ColorRed
	}
	return utils.ColorWhite
}

// InteractiveSearch — интерактивный поиск с возможностью открыть раздел
func InteractiveSearch(query string, scanner interface {
	Scan() bool
	Text() string
}) {
	results := Search(query)
	ShowSearchResults(query, results)

	if len(results) == 0 {
		return
	}

	fmt.Printf("  %s%sВведите номер раздела чтобы открыть (Enter — пропустить):%s ",
		utils.ColorBold, utils.ColorGreen, utils.ColorReset)

	if !scanner.Scan() {
		return
	}
	input := strings.TrimSpace(scanner.Text())
	if input == "" {
		return
	}

	n := 0
	for _, ch := range input {
		if ch >= '0' && ch <= '9' {
			n = n*10 + int(ch-'0')
		} else {
			return
		}
	}

	if n >= 1 && n <= len(results) {
		fmt.Println()
		results[n-1].RunFunc()
	}
}
