package api

import (
	"fmt"
	"net/http"
	"sort"
	"time"

	"github.com/pulse-lets-go/internal/models"
	"github.com/pulse-lets-go/internal/util"
)

// handleGetRoutingRules — GET /api/routing/rules — список всех правил с hits.
func (a *API) handleGetRoutingRules(w http.ResponseWriter, r *http.Request) {
	rules, err := a.configManager.ReadRoutingRules()
	if err != nil {
		writeError(w, http.StatusInternalServerError, "ошибка чтения правил маршрутизации")
		return
	}

	// Сортировка по priority
	sort.Slice(rules, func(i, j int) bool {
		return rules[i].Priority < rules[j].Priority
	})

	// Подмешиваем hits из engine в модель
	compiledRules := a.engine.GetCompiledRules()
	hitsMap := make(map[string]int64, len(compiledRules))
	for _, cr := range compiledRules {
		hitsMap[cr.ID] = cr.GetHits()
	}

	type RuleWithHits struct {
		models.RouteRule
		Hits int64 `json:"hits"`
	}
	result := make([]RuleWithHits, len(rules))
	for i, r := range rules {
		result[i] = RuleWithHits{RouteRule: r, Hits: hitsMap[r.ID]}
	}

	writeJSON(w, http.StatusOK, result)
}

// handleCreateRoutingRule — POST /api/routing/rules — создание нового правила.
func (a *API) handleCreateRoutingRule(w http.ResponseWriter, r *http.Request) {
	var rule models.RouteRule
	if err := decodeJSON(r, &rule); err != nil {
		writeError(w, http.StatusBadRequest, "некорректный JSON")
		return
	}

	// Генерируем ID
	rule.ID = "rr-" + util.RandomHex(6)
	now := time.Now().UTC()
	rule.CreatedAt = now
	rule.UpdatedAt = now
	rule.Enabled = true
	if rule.Priority <= 0 {
		rule.Priority = 10
	}

	// Читаем текущие правила
	rules, err := a.configManager.ReadRoutingRules()
	if err != nil {
		writeError(w, http.StatusInternalServerError, "ошибка чтения правил")
		return
	}

	// Добавляем новое правило
	rules = append(rules, rule)

	// Валидируем (validate-before-save)
	if err := a.engine.ValidateRules(rules); err != nil {
		writeError(w, http.StatusBadRequest, err.Error())
		return
	}

	// Сохраняем на диск
	if err := a.configManager.SaveRoutingRules(rules); err != nil {
		writeError(w, http.StatusInternalServerError, "ошибка сохранения правила")
		return
	}

	// Загружаем в память
	a.engine.SetRules(rules)

	writeJSON(w, http.StatusCreated, rule)
}

// handleUpdateRoutingRule — PUT /api/routing/rules/{id} — обновление правила.
func (a *API) handleUpdateRoutingRule(w http.ResponseWriter, r *http.Request) {
	id := r.PathValue("id")
	if id == "" {
		writeError(w, http.StatusBadRequest, "id обязателен")
		return
	}

	var updates models.RouteRule
	if err := decodeJSON(r, &updates); err != nil {
		writeError(w, http.StatusBadRequest, "некорректный JSON")
		return
	}

	rules, err := a.configManager.ReadRoutingRules()
	if err != nil {
		writeError(w, http.StatusInternalServerError, "ошибка чтения правил")
		return
	}

	idx := -1
	for i, rule := range rules {
		if rule.ID == id {
			idx = i
			break
		}
	}
	if idx < 0 {
		writeError(w, http.StatusNotFound, "правило не найдено")
		return
	}

	// Частичное обновление: применяем только непустые поля
	target := &rules[idx]
	if updates.Name != "" {
		target.Name = updates.Name
	}
	if updates.Priority > 0 {
		target.Priority = updates.Priority
	}
	if updates.MatchCallerID != "" {
		target.MatchCallerID = updates.MatchCallerID
	}
	if updates.MatchDestination != "" {
		target.MatchDestination = updates.MatchDestination
	}
	if updates.MatchIngressTrunk != "" {
		target.MatchIngressTrunk = updates.MatchIngressTrunk
	}
	if updates.MatchDaysOfWeek != "" {
		target.MatchDaysOfWeek = updates.MatchDaysOfWeek
	}
	if updates.MatchTimeStart != "" {
		target.MatchTimeStart = updates.MatchTimeStart
	}
	if updates.MatchTimeEnd != "" {
		target.MatchTimeEnd = updates.MatchTimeEnd
	}
	if updates.ActionType != "" {
		target.ActionType = updates.ActionType
		target.ActionNodeID = updates.ActionNodeID
		target.ActionPoolIDs = updates.ActionPoolIDs
	}
	if updates.Description != "" {
		target.Description = updates.Description
	}
	// Позволяем очистить match-поля через пустую строку в JSON (omitempty не подходит)
	// Для этого используем указатели, но для упрощения: если поле не передано — оставляем старое
	// Если передано явно (даже пустое) — нужно смотреть raw JSON, здесь упрощённо
	target.UpdatedAt = time.Now().UTC()

	// Валидируем
	if err := a.engine.ValidateRules(rules); err != nil {
		writeError(w, http.StatusBadRequest, err.Error())
		return
	}

	// Сохраняем
	if err := a.configManager.SaveRoutingRules(rules); err != nil {
		writeError(w, http.StatusInternalServerError, "ошибка сохранения правила")
		return
	}

	// Загружаем в память
	a.engine.SetRules(rules)

	writeJSON(w, http.StatusOK, *target)
}

// handleDeleteRoutingRule — DELETE /api/routing/rules/{id} — удаление правила.
func (a *API) handleDeleteRoutingRule(w http.ResponseWriter, r *http.Request) {
	id := r.PathValue("id")
	if id == "" {
		writeError(w, http.StatusBadRequest, "id обязателен")
		return
	}

	rules, err := a.configManager.ReadRoutingRules()
	if err != nil {
		writeError(w, http.StatusInternalServerError, "ошибка чтения правил")
		return
	}

	found := false
	filtered := make([]models.RouteRule, 0, len(rules))
	for _, rule := range rules {
		if rule.ID == id {
			found = true
			continue
		}
		filtered = append(filtered, rule)
	}

	if !found {
		writeError(w, http.StatusNotFound, "правило не найдено")
		return
	}

	if err := a.configManager.SaveRoutingRules(filtered); err != nil {
		writeError(w, http.StatusInternalServerError, "ошибка сохранения правил")
		return
	}

	// Загружаем в память (валидация не нужна — удаление не может нарушить валидность)
	a.engine.SetRules(filtered)

	writeJSON(w, http.StatusNoContent, nil)
}

// handleToggleRoutingRule — PUT /api/routing/rules/{id}/toggle — вкл/выкл правила.
func (a *API) handleToggleRoutingRule(w http.ResponseWriter, r *http.Request) {
	id := r.PathValue("id")
	if id == "" {
		writeError(w, http.StatusBadRequest, "id обязателен")
		return
	}

	var req struct {
		Enabled bool `json:"enabled"`
	}
	if err := decodeJSON(r, &req); err != nil {
		writeError(w, http.StatusBadRequest, "некорректный JSON")
		return
	}

	rules, err := a.configManager.ReadRoutingRules()
	if err != nil {
		writeError(w, http.StatusInternalServerError, "ошибка чтения правил")
		return
	}

	idx := -1
	for i, rule := range rules {
		if rule.ID == id {
			idx = i
			break
		}
	}
	if idx < 0 {
		writeError(w, http.StatusNotFound, "правило не найдено")
		return
	}

	rules[idx].Enabled = req.Enabled
	rules[idx].UpdatedAt = time.Now().UTC()

	if err := a.configManager.SaveRoutingRules(rules); err != nil {
		writeError(w, http.StatusInternalServerError, "ошибка сохранения правила")
		return
	}

	a.engine.SetRules(rules)

	writeJSON(w, http.StatusOK, rules[idx])
}

// handleReorderRoutingRules — PUT /api/routing/rules/reorder — переупорядочивание правил.
// Принимает массив ID в новом порядке. Переназначает Priority = index * 10.
func (a *API) handleReorderRoutingRules(w http.ResponseWriter, r *http.Request) {
	var order []string
	if err := decodeJSON(r, &order); err != nil {
		writeError(w, http.StatusBadRequest, "некорректный JSON: ожидается массив id")
		return
	}

	if len(order) == 0 {
		writeError(w, http.StatusBadRequest, "пустой порядок")
		return
	}

	rules, err := a.configManager.ReadRoutingRules()
	if err != nil {
		writeError(w, http.StatusInternalServerError, "ошибка чтения правил")
		return
	}

	// Строим map для быстрого поиска
	ruleMap := make(map[string]*models.RouteRule, len(rules))
	for i := range rules {
		ruleMap[rules[i].ID] = &rules[i]
	}

	// Проверяем что все ID из order присутствуют
	for _, id := range order {
		if _, ok := ruleMap[id]; !ok {
			writeError(w, http.StatusBadRequest, fmt.Sprintf("правило с id '%s' не найдено", id))
			return
		}
	}

	// Переназначаем приоритеты
	now := time.Now().UTC()
	for i, id := range order {
		r := ruleMap[id]
		r.Priority = (i + 1) * 10
		r.UpdatedAt = now
	}

	if err := a.configManager.SaveRoutingRules(rules); err != nil {
		writeError(w, http.StatusInternalServerError, "ошибка сохранения порядка")
		return
	}

	a.engine.SetRules(rules)

	writeJSON(w, http.StatusOK, map[string]string{"status": "ok"})
}
