package handlers

import (
	"context"
	"encoding/json"
	"log"
	"net/http"

	"github.com/gin-gonic/gin"

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

// ModelHandler provides the unified models endpoint.
type ModelHandler struct {
	stores      store.Stores
	healthStore HealthStatusQuerier // provider health for status enrichment (v0.22.3, nil = disabled)
}

func NewModelHandler(s store.Stores) *ModelHandler {
	return &ModelHandler{stores: s}
}

// SetHealthStore attaches the health store for model status enrichment (v0.22.3).
func (h *ModelHandler) SetHealthStore(hs HealthStatusQuerier) {
	h.healthStore = hs
}

// ListEnabledModels returns all models the user can access (catalog + personas),
// with user preferences (hidden, sort order) applied.
func (h *ModelHandler) ListEnabledModels(c *gin.Context) {
	userID := getUserID(c)

	userModels, err := capspkg.ModelsForUser(c.Request.Context(), h.stores, userID)
	if err != nil {
		log.Printf("error: ModelsForUser(%s): %v", userID, err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to resolve models"})
		return
	}

	// Enrich with provider health status (v0.22.3)
	if h.healthStore != nil {
		healthMap := h.buildHealthMap(c.Request.Context())
		for i := range userModels {
			if s, ok := healthMap[userModels[i].ProviderConfigID]; ok {
				userModels[i].ProviderStatus = s
			}
		}
	}

	// Include admin default model so frontend can use it in resolution chain
	defaultModel, _ := h.stores.Policies.Get(c.Request.Context(), "default_model")

	// Model allowlist filtering (v0.24.2) — non-admin users with group-level
	// model restrictions only see permitted models. Persona entries whose
	// underlying model is disallowed are also filtered.
	role, _ := c.Get("role")
	if role != "admin" {
		allowlist, err := auth.ResolveModelAllowlist(c.Request.Context(), h.stores, userID)
		if err == nil && allowlist != nil {
			filtered := make([]models.UserModel, 0, len(userModels))
			for _, m := range userModels {
				if allowlist[m.ModelID] {
					filtered = append(filtered, m)
				}
			}
			userModels = filtered
		}
	}

	c.JSON(http.StatusOK, gin.H{"data": userModels, "default_model": defaultModel})
}

// buildHealthMap returns a map of configID → ProviderStatus from current health windows.
func (h *ModelHandler) buildHealthMap(ctx context.Context) map[string]models.ProviderStatus {
	m := make(map[string]models.ProviderStatus)
	windows, err := h.healthStore.ListAllCurrent(ctx)
	if err != nil {
		return m
	}
	for _, w := range windows {
		m[w.ProviderConfigID] = health.DeriveStatus(w.ErrorRate(), w.RequestCount)
	}
	return m
}

// ResolveModelCaps is the canonical capability resolver for any model.
// v0.29.0: accepts CatalogStore instead of using database.DB directly.
func ResolveModelCaps(catalog store.CatalogStore, modelID, configID string) models.ModelCapabilities {
	// 1. Exact match: model_id + provider_config_id
	if configID != "" {
		caps, ok := capsFromCatalog(catalog, modelID, configID)
		if ok {
			caps.MaxOutputTokens = capspkg.ResolveMaxOutput(modelID, caps)
			return caps
		}
	}

	// 2. Any provider: same model_id, any config
	caps, ok := capsFromCatalog(catalog, modelID, "")
	if ok {
		caps.MaxOutputTokens = capspkg.ResolveMaxOutput(modelID, caps)
		return caps
	}

	// 3. Heuristic inference
	caps = capspkg.InferCapabilities(modelID)
	caps.MaxOutputTokens = capspkg.ResolveMaxOutput(modelID, caps)
	return caps
}

// capsFromCatalog looks up capabilities from the model_catalog table.
func capsFromCatalog(catalog store.CatalogStore, modelID, configID string) (models.ModelCapabilities, bool) {
	if catalog == nil {
		return models.ModelCapabilities{}, false
	}

	var capsJSON []byte
	var err error
	if configID != "" {
		capsJSON, err = catalog.GetCapabilities(context.Background(), modelID, configID)
	} else {
		capsJSON, err = catalog.GetCapabilitiesAny(context.Background(), modelID)
	}
	if err != nil || len(capsJSON) == 0 {
		return models.ModelCapabilities{}, false
	}

	var caps models.ModelCapabilities
	if json.Unmarshal(capsJSON, &caps) != nil || !caps.HasProviderData() {
		return models.ModelCapabilities{}, false
	}

	// Merge with known data to fill gaps
	resolved := capspkg.ResolveIntrinsic(modelID, &caps, nil)
	return resolved, true
}

