package api

import (
	"errors"
	"strconv"
	"strings"

	"github.com/gofiber/fiber/v2"

	"fiko-ai/internal/llm"
	"fiko-ai/internal/protocol"
	"fiko-ai/internal/skill"
)

type skillSearchRequest struct {
	Query    string           `json:"query"`
	Mode     skill.SearchMode `json:"mode"`     // semantic | keyword | hybrid (boş = hybrid)
	Limit    int              `json:"limit"`    // boş = 5, en fazla 50
	MinScore float64          `json:"minScore"` // 0..1
}

type skillSearchResponse struct {
	Items []skill.SearchResult `json:"items"` // skora göre azalan; yalnızca active skill'ler
	Count int                  `json:"count"`
	Mode  skill.SearchMode     `json:"mode"`
}

// searchSkills POST /api/skills/search — aktif skill'ler arasında semantik/anahtar kelime/hibrit arama.
// Agent'a tüm skill'ler yerine yalnızca ilgili olanları vermek için kullanılır (PROJE.MD "SKILL REGISTRY").
// Okuma işlemidir; denetim kaydı alınmaz.
func (s *Server) searchSkills(c *fiber.Ctx) error {
	var in skillSearchRequest
	if err := c.BodyParser(&in); err != nil {
		return fail(c, fiber.StatusBadRequest, protocol.ErrCodeBadRequest, "geçersiz istek gövdesi")
	}
	mode := in.Mode
	if mode == "" {
		mode = skill.ModeHybrid
	}
	items, err := s.deps.Skills.Search(c.UserContext(), skill.SearchOptions{
		Query: in.Query, Mode: in.Mode, Limit: in.Limit, MinScore: in.MinScore,
	})
	if err != nil {
		return failEmbedding(c, err)
	}
	return c.JSON(skillSearchResponse{Items: items, Count: len(items), Mode: mode})
}

// reindexSkills POST /api/skills/reindex[?force=true] — embedding'i eksik skill'leri doldurur.
// force=true tümünü yeniden üretir (embedding modeli değiştiğinde). Yazma işlemidir; denetim kaydı alınır.
func (s *Server) reindexSkills(c *fiber.Ctx) error {
	force := false
	if raw := strings.TrimSpace(c.Query("force")); raw != "" {
		v, err := strconv.ParseBool(raw)
		if err != nil {
			return fail(c, fiber.StatusBadRequest, protocol.ErrCodeBadRequest, "force true veya false olmalı")
		}
		force = v
	}
	userID := s.currentUser(c)
	rep, err := s.deps.Skills.Reindex(c.UserContext(), force)
	if err != nil {
		s.auditFailure(userID, "skill.reindex", err.Error())
		return failEmbedding(c, err)
	}
	s.auditSuccess(userID, "skill.reindex", "force="+strconv.FormatBool(force)+
		" total="+strconv.Itoa(rep.Total)+" embedded="+strconv.Itoa(rep.Embedded)+
		" failed="+strconv.Itoa(rep.Failed))
	return c.JSON(rep)
}

// failEmbedding embedding'e özgü hataları çevirir; kalanı failDomain'e bırakır.
func failEmbedding(c *fiber.Ctx, err error) error {
	var pe *llm.ProviderError
	switch {
	case errors.Is(err, skill.ErrNoEmbedder), errors.Is(err, llm.ErrNoEmbedModel):
		return fail(c, fiber.StatusServiceUnavailable, protocol.ErrCodeLLM,
			"embedding sağlayıcısı yapılandırılmamış (OLLAMA_EMBED_MODEL ayarla)")
	case errors.As(err, &pe):
		return failLLM(c, err)
	default:
		return failDomain(c, err)
	}
}
