package handlers

import (
	"errors"
	"log"
	"net/http"

	"github.com/gin-gonic/gin"

	"chat-switchboard/crypto"
	"chat-switchboard/models"
	"chat-switchboard/store"
)

// ProviderConfigHandler handles user-facing provider config endpoints.
type ProviderConfigHandler struct {
	stores store.Stores
	vault  *crypto.KeyResolver
}

func NewProviderConfigHandler(s store.Stores, vault *crypto.KeyResolver) *ProviderConfigHandler {
	return &ProviderConfigHandler{stores: s, vault: vault}
}

// ListConfigs returns configs accessible to the user (global + personal + team).
func (h *ProviderConfigHandler) ListConfigs(c *gin.Context) {
	userID := getUserID(c)

	// User settings → Providers shows only personal (BYOK) configs.
	// Global/team providers are managed by admins and surfaced via the model list.
	cfgs, err := h.stores.Providers.ListForUser(c.Request.Context(), userID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list configs"})
		return
	}

	// Mask API keys
	type safeConfig struct {
		ID           string `json:"id"`
		Name         string `json:"name"`
		Provider     string `json:"provider"`
		Endpoint     string `json:"endpoint"`
		HasKey       bool   `json:"has_key"`
		ModelDefault string `json:"model_default,omitempty"`
		Scope        string `json:"scope"`
		IsActive     bool   `json:"is_active"`
		CreatedAt    string `json:"created_at"`
		UpdatedAt    string `json:"updated_at"`
	}

	out := make([]safeConfig, len(cfgs))
	for i, cfg := range cfgs {
		out[i] = safeConfig{
			ID:           cfg.ID,
			Name:         cfg.Name,
			Provider:     cfg.Provider,
			Endpoint:     cfg.Endpoint,
			HasKey:       cfg.HasKey(),
			ModelDefault: cfg.ModelDefault,
			Scope:        cfg.Scope,
			IsActive:     cfg.IsActive,
			CreatedAt:    cfg.CreatedAt.Format("2006-01-02T15:04:05Z"),
			UpdatedAt:    cfg.UpdatedAt.Format("2006-01-02T15:04:05Z"),
		}
	}

	c.JSON(http.StatusOK, gin.H{"data": out})
}

// GetConfig returns a single config by ID (if user has access).
func (h *ProviderConfigHandler) GetConfig(c *gin.Context) {
	userID := getUserID(c)
	id := c.Param("id")

	if ok, _ := h.stores.Providers.UserCanAccess(c.Request.Context(), userID, id); !ok {
		c.JSON(http.StatusNotFound, gin.H{"error": "config not found"})
		return
	}

	cfg, err := h.stores.Providers.GetByID(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "config not found"})
		return
	}

	c.JSON(http.StatusOK, cfg)
}

// CreateConfig creates a personal provider config (BYOK).
// After creation, automatically fetches models from the provider API and enables them.
func (h *ProviderConfigHandler) CreateConfig(c *gin.Context) {
	userID := getUserID(c)

	// Check policy
	allowed, _ := h.stores.Policies.GetBool(c.Request.Context(), "allow_user_byok")
	if !allowed {
		c.JSON(http.StatusForbidden, gin.H{"error": "personal API keys not allowed"})
		return
	}

	var req struct {
		Name         string `json:"name" binding:"required"`
		Provider     string `json:"provider" binding:"required"`
		Endpoint     string `json:"endpoint" binding:"required"`
		APIKey       string `json:"api_key"`
		ModelDefault string `json:"model_default,omitempty"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	cfg := &models.ProviderConfig{
		Name:         req.Name,
		Provider:     req.Provider,
		Endpoint:     req.Endpoint,
		ModelDefault: req.ModelDefault,
		Scope:        models.ScopePersonal,
		KeyScope:     models.ScopePersonal,
		OwnerID:      &userID,
		IsActive:     true,
	}

	// Encrypt the API key with user's UEK (personal scope)
	if req.APIKey != "" {
		if h.vault != nil {
			enc, nonce, err := h.vault.EncryptForScope(req.APIKey, "personal", userID)
			if err != nil {
				if err == crypto.ErrVaultLocked {
					c.JSON(http.StatusUnauthorized, gin.H{"error": "vault is locked — please log in again"})
					return
				}
				c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to encrypt API key"})
				return
			}
			cfg.APIKeyEnc = enc
			cfg.KeyNonce = nonce
		} else {
			cfg.APIKeyEnc = []byte(req.APIKey)
		}
	}

	if err := h.stores.Providers.Create(c.Request.Context(), cfg); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create config"})
		return
	}

	// Auto-fetch models from the provider API and enable them.
	// The user added this key to USE it — don't make them hunt for a fetch button.
	resp := gin.H{
		"id":       cfg.ID,
		"name":     cfg.Name,
		"provider": cfg.Provider,
		"endpoint": cfg.Endpoint,
		"scope":    cfg.Scope,
	}

	result, err := syncAndEnableProviderModels(c.Request.Context(), h.stores, cfg, req.APIKey)
	if err != nil {
		// Provider created successfully but model fetch failed.
		// Return 201 (provider exists) with a warning so the frontend can show it.
		resp["warning"] = "Provider created but model fetch failed: " + err.Error()
		resp["models_fetched"] = 0
		log.Printf("warn: BYOK auto-fetch for %s (%s) failed: %v", cfg.ID, cfg.Provider, err)
	} else {
		resp["models_fetched"] = result.Total
	}

	c.JSON(http.StatusCreated, resp)
}

// UpdateConfig updates a personal provider config.
func (h *ProviderConfigHandler) UpdateConfig(c *gin.Context) {
	userID := getUserID(c)
	id := c.Param("id")

	existing, err := h.stores.Providers.GetByID(c.Request.Context(), id)
	if err != nil || existing.Scope != models.ScopePersonal || existing.OwnerID == nil || *existing.OwnerID != userID {
		c.JSON(http.StatusForbidden, gin.H{"error": "can only update your own configs"})
		return
	}

	// Bind standard fields
	var req struct {
		models.ProviderConfigPatch
		APIKey *string `json:"api_key,omitempty"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	patch := req.ProviderConfigPatch
	// Transfer api_key → encrypted fields
	if req.APIKey != nil && *req.APIKey != "" {
		if h.vault != nil {
			enc, nonce, err := h.vault.EncryptForScope(*req.APIKey, "personal", userID)
			if err != nil {
				if err == crypto.ErrVaultLocked {
					c.JSON(http.StatusUnauthorized, gin.H{"error": "vault is locked — please log in again"})
					return
				}
				c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to encrypt API key"})
				return
			}
			patch.APIKeyEnc = enc
			patch.KeyNonce = nonce
		} else {
			patch.APIKeyEnc = []byte(*req.APIKey)
		}
	}

	if err := h.stores.Providers.Update(c.Request.Context(), id, patch); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to update config"})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "config updated"})
}

// DeleteConfig deletes a personal provider config.
func (h *ProviderConfigHandler) DeleteConfig(c *gin.Context) {
	userID := getUserID(c)
	id := c.Param("id")

	existing, err := h.stores.Providers.GetByID(c.Request.Context(), id)
	if err != nil || existing.Scope != models.ScopePersonal || existing.OwnerID == nil || *existing.OwnerID != userID {
		c.JSON(http.StatusForbidden, gin.H{"error": "can only delete your own configs"})
		return
	}

	h.stores.Catalog.DeleteForProvider(c.Request.Context(), id)
	if err := h.stores.Providers.Delete(c.Request.Context(), id); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to delete config"})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "config deleted"})
}

// ListModels returns models for a specific provider config.
func (h *ProviderConfigHandler) ListModels(c *gin.Context) {
	id := c.Param("id")

	entries, err := h.stores.Catalog.ListForProvider(c.Request.Context(), id)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list models"})
		return
	}

	c.JSON(http.StatusOK, gin.H{"data": entries})
}

// FetchModels fetches models from the provider API and auto-enables them.
// This is the user-facing equivalent of admin POST /models/fetch.
// Allows refreshing models for existing BYOK providers.
func (h *ProviderConfigHandler) FetchModels(c *gin.Context) {
	userID := getUserID(c)
	id := c.Param("id")

	cfg, err := h.stores.Providers.GetByID(c.Request.Context(), id)
	if err != nil || cfg.Scope != models.ScopePersonal || cfg.OwnerID == nil || *cfg.OwnerID != userID {
		c.JSON(http.StatusNotFound, gin.H{"error": "provider not found"})
		return
	}

	// Decrypt the API key for the provider API call
	apiKey := ""
	if len(cfg.APIKeyEnc) > 0 {
		if h.vault != nil {
			apiKey, err = h.vault.Decrypt(cfg.APIKeyEnc, cfg.KeyNonce, cfg.KeyScope, userID)
			if err != nil {
				c.JSON(http.StatusUnauthorized, gin.H{"error": "vault is locked — please log in again"})
				return
			}
		} else {
			apiKey = string(cfg.APIKeyEnc)
		}
	}

	result, err := syncAndEnableProviderModels(c.Request.Context(), h.stores, cfg, apiKey)
	if err != nil {
		if errors.Is(err, ErrUpstreamTimeout) {
			c.JSON(http.StatusGatewayTimeout, gin.H{"error": err.Error()})
			return
		}
		c.JSON(http.StatusBadGateway, gin.H{"error": "failed to fetch models: " + err.Error()})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"message": "models synced",
		"added":   result.Added,
		"updated": result.Updated,
		"total":   result.Total,
	})
}
