package api

import (
	"strings"

	"github.com/gofiber/fiber/v2"

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

type skillResponse struct {
	Skill skill.Skill `json:"skill"`
}

type skillsResponse struct {
	Items  []skill.Skill `json:"items"`
	Count  int           `json:"count"`
	Limit  int           `json:"limit"`
	Offset int           `json:"offset"`
}

type skillValidateResponse struct {
	Valid  bool               `json:"valid"`
	Errors []skill.FieldError `json:"errors"` // geçerliyse boş dizi (null değil)
}

type skillStatusRequest struct {
	Status skill.Status `json:"status"`
}

// skillsAvailable Skill Registry yapılandırılmamışsa tüm /api/skills uçlarını 503 yapar.
func (s *Server) skillsAvailable(c *fiber.Ctx) error {
	if s.deps.Skills == nil {
		return fail(c, fiber.StatusServiceUnavailable, protocol.ErrCodeInternal, "skill sistemi yapılandırılmamış")
	}
	return c.Next()
}

// listSkills GET /api/skills?status=&tag=&q=&limit=&offset= — ada göre sıralı.
func (s *Server) listSkills(c *fiber.Ctx) error {
	limit, offset, err := s.parsePaging(c)
	if err != nil {
		return err
	}
	status := skill.Status(strings.TrimSpace(c.Query("status")))
	if status != "" && !status.Valid() {
		return fail(c, fiber.StatusBadRequest, protocol.ErrCodeBadRequest, "status draft, active veya disabled olmalı")
	}
	f := skill.ListFilter{
		Status: status,
		Tag:    strings.ToLower(strings.TrimSpace(c.Query("tag"))), // etiketler küçük harf saklanır
		Query:  c.Query("q"),
	}
	items, err := s.deps.Skills.List(c.UserContext(), f, limit, offset)
	if err != nil {
		return failDomain(c, err)
	}
	return c.JSON(skillsResponse{Items: items, Count: len(items), Limit: limit, Offset: offset})
}

// getSkill GET /api/skills/:id
func (s *Server) getSkill(c *fiber.Ctx) error {
	sk, err := s.deps.Skills.Get(c.UserContext(), c.Params("id"))
	if err != nil {
		return failDomain(c, err)
	}
	return c.JSON(skillResponse{Skill: *sk})
}

// createSkill POST /api/skills — kimlik, zaman damgaları ve embedding sunucuya aittir (gövdedekiler yok sayılır).
func (s *Server) createSkill(c *fiber.Ctx) error {
	in, err := parseSkillBody(c)
	if err != nil {
		return err
	}
	return s.writeSkill(c, "skill.create", fiber.StatusCreated, func() (*skill.Skill, error) {
		return s.deps.Skills.Register(c.UserContext(), in)
	})
}

// updateSkill PUT /api/skills/:id — skill'i gövdedeki alanlarla TAMAMEN değiştirir.
func (s *Server) updateSkill(c *fiber.Ctx) error {
	in, err := parseSkillBody(c)
	if err != nil {
		return err
	}
	id := c.Params("id")
	return s.writeSkill(c, "skill.update", fiber.StatusOK, func() (*skill.Skill, error) {
		return s.deps.Skills.Update(c.UserContext(), id, in)
	})
}

// setSkillStatus PUT /api/skills/:id/status {"status":"active"} — kurallar yeniden denetlenir.
func (s *Server) setSkillStatus(c *fiber.Ctx) error {
	var in skillStatusRequest
	if err := c.BodyParser(&in); err != nil {
		return fail(c, fiber.StatusBadRequest, protocol.ErrCodeBadRequest, "geçersiz istek gövdesi")
	}
	// Boş durum servis katmanında sessizce "draft"a dönerdi; burada açıkça reddedilir.
	if strings.TrimSpace(string(in.Status)) == "" {
		return fail(c, fiber.StatusBadRequest, protocol.ErrCodeBadRequest, "status gerekli")
	}
	id := c.Params("id")
	return s.writeSkill(c, "skill.status", fiber.StatusOK, func() (*skill.Skill, error) {
		return s.deps.Skills.SetStatus(c.UserContext(), id, in.Status)
	})
}

// duplicateSkill POST /api/skills/:id/duplicate — "<ad>_copy" adıyla taslak kopya.
func (s *Server) duplicateSkill(c *fiber.Ctx) error {
	id := c.Params("id")
	return s.writeSkill(c, "skill.duplicate", fiber.StatusCreated, func() (*skill.Skill, error) {
		return s.deps.Skills.Duplicate(c.UserContext(), id)
	})
}

// deleteSkill DELETE /api/skills/:id
func (s *Server) deleteSkill(c *fiber.Ctx) error {
	id, userID := c.Params("id"), s.currentUser(c)
	if err := s.deps.Skills.Delete(c.UserContext(), id); err != nil {
		s.auditFailure(userID, "skill.delete", id+": "+err.Error())
		return failDomain(c, err)
	}
	s.auditSuccess(userID, "skill.delete", id)
	return c.SendStatus(fiber.StatusNoContent)
}

// validateSkill POST /api/skills/validate[?id=<skillId>] — kaydetmeden doğrular (arayüzün canlı doğrulaması).
// Geçersizlik bir HATA değil sonuçtur: her zaman 200 döner. id verilirse o skill'in kendi adı çakışma sayılmaz.
func (s *Server) validateSkill(c *fiber.Ctx) error {
	in, err := parseSkillBody(c)
	if err != nil {
		return err
	}
	errs, err := s.deps.Skills.Validate(c.UserContext(), strings.TrimSpace(c.Query("id")), in)
	if err != nil {
		return failDomain(c, err)
	}
	return c.JSON(skillValidateResponse{Valid: len(errs) == 0, Errors: errs})
}

// writeSkill yazma işlemini çalıştırır, denetim kaydı alır ve skill'i yanıtlar.
func (s *Server) writeSkill(c *fiber.Ctx, action string, status int, op func() (*skill.Skill, error)) error {
	userID := s.currentUser(c)
	sk, err := op()
	if err != nil {
		s.auditFailure(userID, action, err.Error())
		return failDomain(c, err)
	}
	s.auditSuccess(userID, action, "name="+sk.Name+" id="+sk.ID)
	return c.Status(status).JSON(skillResponse{Skill: *sk})
}

// parseSkillBody gövdeyi Skill'e çevirir. Hata *fiber.Error'dur (handler `return err` ile bırakır).
func parseSkillBody(c *fiber.Ctx) (skill.Skill, error) {
	var in skill.Skill
	if err := c.BodyParser(&in); err != nil {
		return skill.Skill{}, fiber.NewError(fiber.StatusBadRequest, "geçersiz istek gövdesi")
	}
	return in, nil
}
