package api

import (
	"sort"
	"strings"

	"github.com/gofiber/fiber/v2"

	"fiko-ai/internal/prompt"
	"fiko-ai/internal/protocol"
	"fiko-ai/internal/settings"
)

// promptPartResponse, arayüzde gösterilen tek bir istem parçasıdır.
//
// "default" alanı KODDA yaşar ve değiştirilemez; "text" ise yöneticinin anlık
// metnidir (boşsa varsayılan kullanılır). Arayüz "Varsayılana dön" işlemini
// yalnızca text'i boşaltarak yapar — yani sunucu tarafında değişen tek şey ayar.
type promptPartResponse struct {
	Name        string `json:"name"`
	Title       string `json:"title"`
	Description string `json:"description"`
	Safe        bool   `json:"safe"`
	Editable    bool   `json:"editable"`
	// DefaultEnabled parçanın kod içindeki açık/kapalı halidir.
	DefaultEnabled bool `json:"defaultEnabled"`
	// Default kod varsayılan metnidir.
	Default string `json:"default"`
	// Text yöneticinin kaydettiği metin (yoksa boş = varsayılan geçerli).
	Text string `json:"text"`
	// Enabled, o anda bu bölümün isteme eklenip eklenmediği (anlık görünüm).
	Enabled bool `json:"enabled"`
	// Modified, text'in varsayılmandan farklı olup olmadığı (arayüz ipucu).
	Modified bool `json:"modified"`
}

type promptsResponse struct {
	Parts []promptPartResponse `json:"parts"`
	// Disabled, şu an KAPALI olan güvenlik parçalarının adları (uyarı için).
	DisabledSafeParts []string `json:"disabledSafeParts"`
}

// buildPromptResponse, kod kataloğu + kayıtlı ayarlar birleşiminden yanıt üretir.
func buildPromptResponse(v settings.Values) promptsResponse {
	out := promptsResponse{Parts: make([]promptPartResponse, 0, len(prompt.EditableParts))}
	for i := range prompt.EditableParts {
		p := prompt.EditableParts[i]
		text := v.PromptOverrides[p.Name]
		enabled := true
		if v.PromptDisabled != nil && v.PromptDisabled[p.Name] {
			enabled = false
		}
		r := promptPartResponse{
			Name: p.Name, Title: p.Title, Description: p.Description,
			Safe: p.Safe, Editable: p.Editable,
			DefaultEnabled: p.DefaultEnabled, Default: p.Default,
			Text: text, Enabled: enabled, Modified: text != "",
		}
		// "Değiştirilmiş" gerçekten farklı mı? Metin boşsa varsayılan geçerlidir.
		if text != "" && text != p.Default {
			r.Modified = true
		} else if text == p.Default && text != "" {
			r.Modified = false
		}
		if !enabled && p.Safe {
			out.DisabledSafeParts = append(out.DisabledSafeParts, p.Name)
		}
		out.Parts = append(out.Parts, r)
	}
	return out
}

// getPrompts GET /api/admin/prompts — sistem istemi parçaları ve kayıtlı düzenlemeler.
// YALNIZCA yönetici (grup zaten adminOnly ile korumalı).
func (s *Server) getPrompts(c *fiber.Ctx) error {
	if s.deps.Settings == nil {
		return fail(c, fiber.StatusServiceUnavailable, protocol.ErrCodeInternal, "ayarlar yapılandırılmamış")
	}
	return c.JSON(buildPromptResponse(s.deps.Settings.Get()))
}

// putPrompts PUT /api/admin/prompts — parçaları düzenle/kapat/varsayılana döndür.
//
// GÜVENLİK: gövde DOĞRUDAN isteme gider. Bu yüzden sunucu her adı katalogla
// sınırlar (prompt.CleanOverrides/CleanDisabled): katalogda olmayan bir anahtar
// yutulur, düzenlenemez parçaya yazma isteği yok sayılır. Aksi halde istemci
// modele istediği metni sokabilirdi.
func (s *Server) putPrompts(c *fiber.Ctx) error {
	if s.deps.Settings == nil {
		return fail(c, fiber.StatusServiceUnavailable, protocol.ErrCodeInternal, "ayarlar yapılandırılmamış")
	}
	var in struct {
		Text     map[string]string `json:"text"`
		Disabled map[string]bool   `json:"disabled"`
	}
	if err := c.BodyParser(&in); err != nil {
		return fail(c, fiber.StatusBadRequest, protocol.ErrCodeBadRequest, "geçersiz istek gövdesi")
	}
	// Alan yoksa (null) değişiklik yapma; alan VARSA tamamen değiştir (sıfırlanabilir).
	patch := settings.Patch{}
	before := s.deps.Settings.Get()
	if in.Text != nil {
		patch.PromptOverrides = in.Text
	}
	if in.Disabled != nil {
		patch.PromptDisabled = in.Disabled
	}
	if patch.PromptOverrides == nil && patch.PromptDisabled == nil {
		return fail(c, fiber.StatusBadRequest, protocol.ErrCodeBadRequest, "text ya da disabled alanı gönderilmeli")
	}

	userID := s.currentUser(c)
	updated, err := s.deps.Settings.Update(patch)
	if err != nil {
		s.auditFailure(userID, "prompts.update", err.Error())
		return fail(c, fiber.StatusBadRequest, protocol.ErrCodeBadRequest, err.Error())
	}
	s.auditSuccess(userID, "prompts.update", promptsAuditDetail(before, updated))
	return c.JSON(buildPromptResponse(updated))
}

// promptsAuditDetail, değişen parçaları ADIYLA bildirir (metin denetim kaydına yazılmaz:
// istem içeriği gizli olabilir ve kaydı şişirir).
func promptsAuditDetail(before, after settings.Values) string {
	changed := make([]string, 0, 8)
	for name, txt := range after.PromptOverrides {
		if before.PromptOverrides[name] != txt {
			changed = append(changed, name)
		}
	}
	for name, off := range after.PromptDisabled {
		if off != before.PromptDisabled[name] {
			if off {
				changed = append(changed, name+"=kapalı")
			} else {
				changed = append(changed, name+"=açık")
			}
		}
	}
	if len(changed) == 0 {
		return "değişiklik yok"
	}
	// Harita sırası kararsızdır; denetim kaydının tutarlı olması için sırala.
	sort.Strings(changed)
	return strings.Join(changed, ",")
}
