package handlers

import (
	"database/sql"
	"net/http"

	"github.com/gin-gonic/gin"

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

// ── Channel Models Handler ──────────────────
// Manages multi-model assignments per channel (v0.20.0 Phase 2).
// Routes:
//   GET    /channels/:id/models           — list
//   POST   /channels/:id/models           — add
//   PATCH  /channels/:id/models/:modelId  — update (display_name, is_default, system_prompt)
//   DELETE /channels/:id/models/:modelId  — remove

const maxModelsPerChannel = 5

type ChannelModelHandler struct {
	stores store.Stores
}

func NewChannelModelHandler(stores store.Stores) *ChannelModelHandler {
	return &ChannelModelHandler{stores: stores}
}

// ── List ─────────────────────────────────────

func (h *ChannelModelHandler) List(c *gin.Context) {
	channelID := c.Param("id")
	userID := getUserID(c)

	if !userOwnsChannel(c, channelID, userID) {
		return
	}

	roster, err := h.stores.Channels.GetModels(c.Request.Context(), channelID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list models"})
		return
	}
	if roster == nil {
		roster = []models.ChannelModel{}
	}
	c.JSON(http.StatusOK, gin.H{"data": roster})
}

// ── Add ──────────────────────────────────────

type addChannelModelRequest struct {
	ModelID          string `json:"model_id" binding:"required"`
	ProviderConfigID string `json:"provider_config_id"`
	DisplayName      string `json:"display_name" binding:"required"`
	SystemPrompt     string `json:"system_prompt"`
	IsDefault        bool   `json:"is_default"`
}

func (h *ChannelModelHandler) Add(c *gin.Context) {
	channelID := c.Param("id")
	userID := getUserID(c)

	if !userOwnsChannel(c, channelID, userID) {
		return
	}

	var req addChannelModelRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// Check max models limit
	existing, err := h.stores.Channels.GetModels(c.Request.Context(), channelID)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to check existing models"})
		return
	}
	if len(existing) >= maxModelsPerChannel {
		c.JSON(http.StatusConflict, gin.H{"error": "maximum models per channel reached"})
		return
	}

	// If this is the first model or marked as default, ensure only one default
	isDefault := req.IsDefault || len(existing) == 0

	cm := &models.ChannelModel{
		ChannelID:        channelID,
		ModelID:          req.ModelID,
		ProviderConfigID: req.ProviderConfigID,
		DisplayName:      req.DisplayName,
		SystemPrompt:     req.SystemPrompt,
		IsDefault:        isDefault,
	}

	// SetModel handles INSERT ON CONFLICT (upsert by channel_id + model_id)
	if err := h.stores.Channels.SetModel(c.Request.Context(), cm); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to add model"})
		return
	}

	// If this is the new default, clear default on others
	if isDefault {
		h.clearOtherDefaults(c, channelID, req.ModelID)
	}

	// Return the updated roster
	roster, _ := h.stores.Channels.GetModels(c.Request.Context(), channelID)
	if roster == nil { roster = []models.ChannelModel{} }
	c.JSON(http.StatusCreated, gin.H{"data": roster})
}

// ── Update ───────────────────────────────────

type updateChannelModelRequest struct {
	DisplayName  *string `json:"display_name"`
	SystemPrompt *string `json:"system_prompt"`
	IsDefault    *bool   `json:"is_default"`
}

func (h *ChannelModelHandler) Update(c *gin.Context) {
	channelID := c.Param("id")
	modelRecordID := c.Param("modelId")
	userID := getUserID(c)

	if !userOwnsChannel(c, channelID, userID) {
		return
	}

	var req updateChannelModelRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// Verify model belongs to this channel
	cm, err := h.stores.Channels.GetModelByID(c.Request.Context(), modelRecordID)
	if err == sql.ErrNoRows {
		c.JSON(http.StatusNotFound, gin.H{"error": "model not found"})
		return
	}
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to look up model"})
		return
	}
	if cm.ChannelID != channelID {
		c.JSON(http.StatusForbidden, gin.H{"error": "model does not belong to this channel"})
		return
	}

	fields := make(map[string]interface{})
	if req.DisplayName != nil {
		fields["display_name"] = *req.DisplayName
	}
	if req.SystemPrompt != nil {
		fields["system_prompt"] = *req.SystemPrompt
	}
	if req.IsDefault != nil {
		fields["is_default"] = *req.IsDefault
		if *req.IsDefault {
			h.clearOtherDefaults(c, channelID, cm.ModelID)
		}
	}

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

	if err := h.stores.Channels.UpdateModel(c.Request.Context(), modelRecordID, fields); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to update model"})
		return
	}

	roster, _ := h.stores.Channels.GetModels(c.Request.Context(), channelID)
	if roster == nil { roster = []models.ChannelModel{} }
	c.JSON(http.StatusOK, gin.H{"data": roster})
}

// ── Delete ───────────────────────────────────

func (h *ChannelModelHandler) Delete(c *gin.Context) {
	channelID := c.Param("id")
	modelRecordID := c.Param("modelId")
	userID := getUserID(c)

	if !userOwnsChannel(c, channelID, userID) {
		return
	}

	// Verify model belongs to this channel
	cm, err := h.stores.Channels.GetModelByID(c.Request.Context(), modelRecordID)
	if err == sql.ErrNoRows {
		c.JSON(http.StatusNotFound, gin.H{"error": "model not found"})
		return
	}
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to look up model"})
		return
	}
	if cm.ChannelID != channelID {
		c.JSON(http.StatusForbidden, gin.H{"error": "model does not belong to this channel"})
		return
	}

	if err := h.stores.Channels.DeleteModel(c.Request.Context(), modelRecordID); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to delete model"})
		return
	}

	// If deleted model was default, promote the first remaining model
	if cm.IsDefault {
		remaining, _ := h.stores.Channels.GetModels(c.Request.Context(), channelID)
		if len(remaining) > 0 {
			h.stores.Channels.UpdateModel(c.Request.Context(), remaining[0].ID,
				map[string]interface{}{"is_default": true})
		}
	}

	roster, _ := h.stores.Channels.GetModels(c.Request.Context(), channelID)
	if roster == nil { roster = []models.ChannelModel{} }
	c.JSON(http.StatusOK, gin.H{"data": roster})
}

// ── Helpers ──────────────────────────────────

// clearOtherDefaults sets is_default=false on all models in the channel
// except the one with the given modelID.
func (h *ChannelModelHandler) clearOtherDefaults(c *gin.Context, channelID, keepModelID string) {
	roster, _ := h.stores.Channels.GetModels(c.Request.Context(), channelID)
	for _, m := range roster {
		if m.ModelID != keepModelID && m.IsDefault {
			h.stores.Channels.UpdateModel(c.Request.Context(), m.ID,
				map[string]interface{}{"is_default": false})
		}
	}
}
