package handlers

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

	"github.com/gin-gonic/gin"

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

// ── Team Provider Handlers ──────────────────

// ListTeamProviders returns API configs scoped to a team.
func (h *TeamHandler) ListTeamProviders(c *gin.Context) {
	teamID := getTeamID(c)
	ctx := c.Request.Context()

	configs, err := h.stores.Providers.ListAllForTeam(ctx, teamID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list team providers"})
		return
	}

	type teamProvider 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"`
		Config       map[string]interface{} `json:"config"`
		IsActive     bool                   `json:"is_active"`
		IsPrivate    bool                   `json:"is_private"`
		CreatedAt    string                 `json:"created_at"`
		UpdatedAt    string                 `json:"updated_at"`
	}

	result := make([]teamProvider, 0, len(configs))
	for _, cfg := range configs {
		var md *string
		if cfg.ModelDefault != "" {
			md = &cfg.ModelDefault
		}
		cfgMap := map[string]interface{}{}
		if cfg.Config != nil {
			cfgMap = cfg.Config
		}
		result = append(result, teamProvider{
			ID:           cfg.ID,
			Name:         cfg.Name,
			Provider:     cfg.Provider,
			Endpoint:     cfg.Endpoint,
			HasKey:       cfg.HasKey(),
			ModelDefault: md,
			Config:       cfgMap,
			IsActive:     cfg.IsActive,
			IsPrivate:    cfg.IsPrivate,
			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":                 result,
		"allow_team_providers": isTeamProvidersAllowed(h.stores, teamID),
	})
}

// CreateTeamProvider creates an API config scoped to a team.
func (h *TeamHandler) CreateTeamProvider(c *gin.Context) {
	teamID := getTeamID(c)

	if !isTeamProvidersAllowed(h.stores, teamID) {
		c.JSON(http.StatusForbidden, gin.H{"error": "team providers are not enabled for this team"})
		return
	}

	var req struct {
		Name         string                 `json:"name" binding:"required,max=100"`
		Provider     string                 `json:"provider" binding:"required"`
		Endpoint     string                 `json:"endpoint" binding:"required"`
		APIKey       string                 `json:"api_key"`
		ModelDefault string                 `json:"model_default,omitempty"`
		Config       map[string]interface{} `json:"config,omitempty"`
		Headers      map[string]string      `json:"headers,omitempty"`
		IsPrivate    bool                   `json:"is_private,omitempty"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	if _, err := providers.Get(req.Provider); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error":               "unsupported provider: " + req.Provider,
			"supported_providers": providers.List(),
		})
		return
	}

	// Encrypt the API key for team scope
	var apiKeyEnc, keyNonce []byte
	if req.APIKey != "" {
		if h.vault != nil {
			var err error
			apiKeyEnc, keyNonce, err = h.vault.EncryptForScope(req.APIKey, "team", "")
			if err != nil {
				c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to encrypt API key"})
				return
			}
		} else {
			apiKeyEnc = []byte(req.APIKey)
		}
	}

	headersMap := models.JSONMap{}
	if req.Headers != nil {
		for k, v := range req.Headers {
			headersMap[k] = v
		}
	}

	cfg := &models.ProviderConfig{
		Scope:        models.ScopeTeam,
		OwnerID:      &teamID,
		Name:         req.Name,
		Provider:     req.Provider,
		Endpoint:     req.Endpoint,
		APIKeyEnc:    apiKeyEnc,
		KeyNonce:     keyNonce,
		KeyScope:     "team",
		ModelDefault: req.ModelDefault,
		Config:       models.JSONMap(req.Config),
		Headers:      headersMap,
		IsActive:     true,
		IsPrivate:    req.IsPrivate,
	}

	if err := h.stores.Providers.Create(c.Request.Context(), cfg); err != nil {
		log.Printf("[WARN] Failed to create team provider: %v", err)
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create provider"})
		return
	}

	c.JSON(http.StatusCreated, gin.H{"id": cfg.ID})
}

// UpdateTeamProvider updates a team-scoped API config.
func (h *TeamHandler) UpdateTeamProvider(c *gin.Context) {
	teamID := getTeamID(c)
	providerID := c.Param("id")
	ctx := c.Request.Context()

	var req struct {
		Name         *string                `json:"name,omitempty"`
		Endpoint     *string                `json:"endpoint,omitempty"`
		APIKey       *string                `json:"api_key,omitempty"`
		ModelDefault *string                `json:"model_default,omitempty"`
		Config       map[string]interface{} `json:"config,omitempty"`
		Headers      map[string]string      `json:"headers,omitempty"`
		IsActive     *bool                  `json:"is_active,omitempty"`
		IsPrivate    *bool                  `json:"is_private,omitempty"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// Verify provider belongs to this team
	existing, err := h.stores.Providers.GetByID(ctx, providerID)
	if err != nil || existing.Scope != models.ScopeTeam || (existing.OwnerID != nil && *existing.OwnerID != teamID) {
		c.JSON(http.StatusNotFound, gin.H{"error": "provider not found in this team"})
		return
	}

	// Build patch
	patch := models.ProviderConfigPatch{}
	fieldCount := 0

	if req.Name != nil {
		patch.Name = req.Name
		fieldCount++
	}
	if req.Endpoint != nil {
		patch.Endpoint = req.Endpoint
		fieldCount++
	}
	if req.APIKey != nil && *req.APIKey != "" {
		if h.vault != nil {
			enc, nonce, err := h.vault.EncryptForScope(*req.APIKey, "team", "")
			if err != nil {
				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)
		}
		fieldCount++
	}
	if req.ModelDefault != nil {
		patch.ModelDefault = req.ModelDefault
		fieldCount++
	}
	if req.IsActive != nil {
		patch.IsActive = req.IsActive
		fieldCount++
	}
	if req.IsPrivate != nil {
		patch.IsPrivate = req.IsPrivate
		fieldCount++
	}
	if req.Config != nil {
		patch.Config = models.JSONMap(req.Config)
		fieldCount++
	}
	if req.Headers != nil {
		headersMap := models.JSONMap{}
		for k, v := range req.Headers {
			headersMap[k] = v
		}
		patch.Headers = headersMap
		fieldCount++
	}

	if fieldCount == 0 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "no fields to update"})
		return
	}

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

	c.JSON(http.StatusOK, gin.H{"id": providerID, "updated": true})
}

// DeleteTeamProvider removes a team-scoped API config.
func (h *TeamHandler) DeleteTeamProvider(c *gin.Context) {
	teamID := getTeamID(c)
	providerID := c.Param("id")

	n, err := h.stores.Providers.DeleteByIDAndTeam(c.Request.Context(), providerID, teamID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to delete provider"})
		return
	}
	if n == 0 {
		c.JSON(http.StatusNotFound, gin.H{"error": "provider not found in this team"})
		return
	}

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

// ListTeamProviderModels lists models available from a team provider (live query).
func (h *TeamHandler) ListTeamProviderModels(c *gin.Context) {
	teamID := getTeamID(c)
	providerID := c.Param("id")
	ctx := c.Request.Context()

	cfg, err := h.stores.Providers.GetByID(ctx, providerID)
	if err != nil || cfg.Scope != models.ScopeTeam || !cfg.IsActive {
		c.JSON(http.StatusNotFound, gin.H{"error": "provider not found"})
		return
	}
	if cfg.OwnerID == nil || *cfg.OwnerID != teamID {
		c.JSON(http.StatusNotFound, gin.H{"error": "provider not found"})
		return
	}

	provider, err := providers.Get(cfg.Provider)
	if err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "unsupported provider"})
		return
	}

	key := ""
	if cfg.HasKey() {
		if h.vault != nil {
			var err error
			key, err = h.vault.Decrypt(cfg.APIKeyEnc, cfg.KeyNonce, cfg.KeyScope, "")
			if err != nil {
				c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to decrypt API key"})
				return
			}
		} else {
			key = string(cfg.APIKeyEnc)
		}
	}

	var customHeaders map[string]string
	if cfg.Headers != nil {
		b, _ := json.Marshal(cfg.Headers)
		_ = json.Unmarshal(b, &customHeaders)
	}

	modelList, err := provider.ListModels(ctx, providers.ProviderConfig{
		Endpoint:      cfg.Endpoint,
		APIKey:        key,
		CustomHeaders: customHeaders,
	})
	if err != nil {
		c.JSON(http.StatusBadGateway, gin.H{"error": "failed to fetch models: " + err.Error()})
		return
	}

	type modelInfo struct {
		ID           string                   `json:"id"`
		Type         string                   `json:"type"`
		Capabilities models.ModelCapabilities `json:"capabilities"`
	}

	out := make([]modelInfo, 0, len(modelList))
	for _, m := range modelList {
		caps := capspkg.ResolveIntrinsic(m.ID, &m.Capabilities, nil)
		caps.MaxOutputTokens = capspkg.ResolveMaxOutput(m.ID, caps)
		out = append(out, modelInfo{ID: m.ID, Type: m.Type, Capabilities: caps})
	}

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

// parseJSONBConfig parses a JSONB text string into a map.
func parseJSONBConfig(raw string) map[string]interface{} {
	if raw == "" || raw == "{}" || raw == "null" {
		return map[string]interface{}{}
	}
	var m map[string]interface{}
	if err := json.Unmarshal([]byte(raw), &m); err != nil {
		return map[string]interface{}{}
	}
	return m
}

// isTeamProvidersAllowed checks if team providers are enabled.
func isTeamProvidersAllowed(stores store.Stores, teamID string) bool {
	if stores.GlobalConfig == nil {
		return false
	}

	ctx := context.Background()

	// Check global setting
	globalVal, err := stores.GlobalConfig.GetString(ctx, "allow_team_providers")
	if err == nil && globalVal == "false" {
		return false
	}

	// Check team-level setting
	team, err := stores.Teams.GetByID(ctx, teamID)
	if err != nil {
		return true // fail open
	}

	if team.Settings != nil {
		if v, ok := team.Settings["allow_team_providers"]; ok {
			if bVal, ok := v.(bool); ok {
				return bVal
			}
		}
	}

	return true
}
