package handlers

import (
	"net/http"
	"strconv"

	"github.com/gin-gonic/gin"

	capspkg "chat-switchboard/capabilities"
	"chat-switchboard/health"
	"chat-switchboard/models"
	"chat-switchboard/providers"
	"chat-switchboard/store"
)

// ── Health Admin Handler ────────────────────

type HealthAdminHandler struct {
	healthStore health.Store
	stores      store.Stores
}

func NewHealthAdminHandler(hs health.Store, stores store.Stores) *HealthAdminHandler {
	return &HealthAdminHandler{healthStore: hs, stores: stores}
}

// GetProviderHealth returns health metrics for a single provider.
// GET /api/v1/admin/providers/:id/health?hours=24
func (h *HealthAdminHandler) GetProviderHealth(c *gin.Context) {
	providerID := c.Param("id")
	hours, _ := strconv.Atoi(c.DefaultQuery("hours", "24"))
	if hours < 1 || hours > 168 {
		hours = 24
	}

	windows, err := h.healthStore.ListWindows(c.Request.Context(), providerID, hours)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to fetch health data"})
		return
	}

	// Derive current status from most recent window
	var summary models.ProviderHealthSummary
	summary.ProviderConfigID = providerID

	if len(windows) > 0 {
		latest := windows[0]
		summary.RequestCount = latest.RequestCount
		summary.ErrorRate = latest.ErrorRate()
		summary.AvgLatencyMs = latest.AvgLatencyMs()
		summary.MaxLatencyMs = latest.MaxLatencyMs
		summary.LastError = latest.LastError
		summary.LastErrorAt = latest.LastErrorAt
		summary.Status = health.DeriveStatus(summary.ErrorRate, summary.RequestCount)
	} else {
		summary.Status = models.StatusUnknown
	}

	SafeJSON(c, http.StatusOK, gin.H{
		"summary": summary,
		"windows": windows,
	})
}

// GetAllProviderHealth returns current health for all providers.
// GET /api/v1/admin/providers/health
func (h *HealthAdminHandler) GetAllProviderHealth(c *gin.Context) {
	windows, err := h.healthStore.ListAllCurrent(c.Request.Context())
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to fetch health data"})
		return
	}

	// Build summaries
	summaries := make([]models.ProviderHealthSummary, 0, len(windows))
	for _, w := range windows {
		s := models.ProviderHealthSummary{
			ProviderConfigID: w.ProviderConfigID,
			Status:           health.DeriveStatus(w.ErrorRate(), w.RequestCount),
			RequestCount:     w.RequestCount,
			ErrorRate:        w.ErrorRate(),
			ErrorCount:       w.ErrorCount,
			RateLimitCount:   w.RateLimitCount,
			TimeoutCount:     w.TimeoutCount,
			AvgLatencyMs:     w.AvgLatencyMs(),
			MaxLatencyMs:     w.MaxLatencyMs,
			LastError:        w.LastError,
			LastErrorAt:      w.LastErrorAt,
		}
		summaries = append(summaries, s)
	}

	SafeJSON(c, http.StatusOK, gin.H{
		"data": summaries,
	})
}

// ── Capability Override Admin Handler ────────

type CapOverrideAdminHandler struct {
	stores store.Stores
}

func NewCapOverrideAdminHandler(stores store.Stores) *CapOverrideAdminHandler {
	return &CapOverrideAdminHandler{stores: stores}
}

// GetModelCapabilities returns resolved capabilities with source annotation.
// GET /api/v1/admin/models/:id/capabilities?provider_config_id=...
func (h *CapOverrideAdminHandler) GetModelCapabilities(c *gin.Context) {
	modelID := c.Param("id")
	providerConfigID := c.Query("provider_config_id")

	// Get catalog capabilities
	var catalogCaps *models.ModelCapabilities
	if providerConfigID != "" {
		if entry, err := h.stores.Catalog.GetByModelID(c.Request.Context(), providerConfigID, modelID); err == nil {
			catalogCaps = &entry.Capabilities
		}
	} else {
		if entry, err := h.stores.Catalog.GetByModelIDAny(c.Request.Context(), modelID); err == nil {
			catalogCaps = &entry.Capabilities
		}
	}

	// Get admin overrides
	var overrides []models.CapabilityOverride
	var err error
	if providerConfigID != "" {
		overrides, err = h.stores.CapOverrides.ListForProviderModel(c.Request.Context(), providerConfigID, modelID)
	} else {
		overrides, err = h.stores.CapOverrides.ListForModel(c.Request.Context(), modelID)
	}
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to fetch overrides"})
		return
	}

	// Resolve through three tiers
	heuristic := capspkg.InferCapabilities(modelID)
	resolved := capspkg.ResolveIntrinsic(modelID, catalogCaps, overrides)

	// Build source annotations
	sources := buildSourceAnnotations(catalogCaps, &heuristic, overrides)

	SafeJSON(c, http.StatusOK, gin.H{
		"model_id":    modelID,
		"resolved":    resolved,
		"catalog":     catalogCaps,
		"heuristic":   heuristic,
		"overrides":   overrides,
		"sources":     sources,
	})
}

// SetModelCapability sets an admin override for a specific capability.
// PUT /api/v1/admin/models/:id/capabilities
func (h *CapOverrideAdminHandler) SetModelCapability(c *gin.Context) {
	modelID := c.Param("id")
	userID := c.GetString("user_id")

	var req struct {
		ProviderConfigID *string `json:"provider_config_id"`
		Field            string  `json:"field" binding:"required"`
		Value            string  `json:"value" binding:"required"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// Validate field name
	validFields := map[string]bool{
		"streaming": true, "tool_calling": true, "vision": true,
		"thinking": true, "reasoning": true, "code_optimized": true,
		"web_search": true, "max_context": true, "max_output_tokens": true,
	}
	if !validFields[req.Field] {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid capability field: " + req.Field})
		return
	}

	// Validate value
	switch req.Field {
	case "max_context", "max_output_tokens":
		if _, err := strconv.Atoi(req.Value); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "value must be a number for " + req.Field})
			return
		}
	default:
		if req.Value != "true" && req.Value != "false" {
			c.JSON(http.StatusBadRequest, gin.H{"error": "value must be 'true' or 'false' for " + req.Field})
			return
		}
	}

	override := &models.CapabilityOverride{
		ProviderConfigID: req.ProviderConfigID,
		ModelID:          modelID,
		Field:            req.Field,
		Value:            req.Value,
		SetBy:            &userID,
	}

	if err := h.stores.CapOverrides.Set(c.Request.Context(), override); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to set override"})
		return
	}

	c.JSON(http.StatusOK, gin.H{"status": "ok"})
}

// DeleteModelCapability removes a specific override.
// DELETE /api/v1/admin/models/:id/capabilities/:overrideId
func (h *CapOverrideAdminHandler) DeleteModelCapability(c *gin.Context) {
	overrideID := c.Param("overrideId")

	if err := h.stores.CapOverrides.Delete(c.Request.Context(), overrideID); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to delete override"})
		return
	}

	c.JSON(http.StatusOK, gin.H{"status": "ok"})
}

// ListAllOverrides returns all capability overrides (admin view).
// GET /api/v1/admin/capability-overrides
func (h *CapOverrideAdminHandler) ListAllOverrides(c *gin.Context) {
	overrides, err := h.stores.CapOverrides.ListAll(c.Request.Context())
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to fetch overrides"})
		return
	}

	SafeJSON(c, http.StatusOK, gin.H{
		"data":  overrides,
		"total": len(overrides),
	})
}

// ── Source Annotation Helpers ────────────────

type capSource struct {
	Field  string `json:"field"`
	Source string `json:"source"` // "catalog", "heuristic", "override"
}

func buildSourceAnnotations(catalog *models.ModelCapabilities, heuristic *models.ModelCapabilities, overrides []models.CapabilityOverride) []capSource {
	// Build override set for quick lookup
	overrideSet := make(map[string]bool, len(overrides))
	for _, o := range overrides {
		overrideSet[o.Field] = true
	}

	fields := []struct {
		name       string
		hasCatalog bool
		hasHeur    bool
	}{
		{"streaming", catalog != nil && catalog.Streaming, heuristic.Streaming},
		{"tool_calling", catalog != nil && catalog.ToolCalling, heuristic.ToolCalling},
		{"vision", catalog != nil && catalog.Vision, heuristic.Vision},
		{"thinking", catalog != nil && catalog.Thinking, heuristic.Thinking},
		{"reasoning", catalog != nil && catalog.Reasoning, heuristic.Reasoning},
		{"code_optimized", catalog != nil && catalog.CodeOptimized, heuristic.CodeOptimized},
		{"web_search", catalog != nil && catalog.WebSearch, heuristic.WebSearch},
		{"max_context", catalog != nil && catalog.MaxContext > 0, heuristic.MaxContext > 0},
		{"max_output_tokens", catalog != nil && catalog.MaxOutputTokens > 0, heuristic.MaxOutputTokens > 0},
	}

	var sources []capSource
	for _, f := range fields {
		src := "default"
		if overrideSet[f.name] {
			src = "override"
		} else if f.hasCatalog {
			src = "catalog"
		} else if f.hasHeur {
			src = "heuristic"
		}
		sources = append(sources, capSource{Field: f.name, Source: src})
	}
	return sources
}

// ── Provider Types Endpoint ─────────────────

// GetProviderTypes returns metadata and profile schemas for all registered
// provider types. Used by the admin UI to render provider creation forms
// and show available settings.
func GetProviderTypes(c *gin.Context) {
	types := providers.ListTypes()
	c.JSON(http.StatusOK, gin.H{"data": types})
}
