package handlers

import (
	"net/http"
	"time"

	"github.com/gin-gonic/gin"

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

// UsageHandler manages usage tracking and pricing endpoints.
type UsageHandler struct {
	stores store.Stores
}

// NewUsageHandler creates a usage handler.
func NewUsageHandler(s store.Stores) *UsageHandler {
	return &UsageHandler{stores: s}
}

// ── Admin: Aggregated Usage ────────────────
// GET /admin/usage?since=...&until=...&group_by=model|user|day|provider

func (h *UsageHandler) AdminUsage(c *gin.Context) {
	opts := h.parseQueryOptions(c)
	opts.ExcludeBYOK = true // Admin views exclude personal BYOK

	results, err := h.stores.Usage.QueryByModel(c.Request.Context(), opts)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to query usage"})
		return
	}

	totals, err := h.stores.Usage.GetTotals(c.Request.Context(), opts)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get totals"})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"totals":  totals,
		"results": results,
	})
}

// ── Admin: Per-User Usage ──────────────────
// GET /admin/usage/users/:id

func (h *UsageHandler) AdminUserUsage(c *gin.Context) {
	userID := c.Param("id")
	opts := h.parseQueryOptions(c)
	opts.ExcludeBYOK = true

	results, err := h.stores.Usage.QueryByUser(c.Request.Context(), userID, opts)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to query user usage"})
		return
	}

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

// ── Admin: Per-Team Usage ──────────────────
// GET /admin/usage/teams/:id

func (h *UsageHandler) AdminTeamUsage(c *gin.Context) {
	teamID := c.Param("id")
	opts := h.parseQueryOptions(c)
	opts.ExcludeBYOK = true

	results, err := h.stores.Usage.QueryByTeam(c.Request.Context(), teamID, opts)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to query team usage"})
		return
	}

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

// ── User: Personal Usage ───────────────────
// GET /usage

func (h *UsageHandler) PersonalUsage(c *gin.Context) {
	userID := getUserID(c)
	opts := h.parseQueryOptions(c)

	totals, err := h.stores.Usage.GetPersonalTotals(c.Request.Context(), userID, opts)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get usage"})
		return
	}

	results, err := h.stores.Usage.QueryByUserPersonal(c.Request.Context(), userID, opts)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to query usage"})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"totals":  totals,
		"results": results,
	})
}

// ── Team Admin: Team Provider Usage ────────
// GET /teams/:teamId/usage
//
// Shows usage against providers owned by this team. Only team admins
// (enforced by RequireTeamAdmin middleware) can see this view.

func (h *UsageHandler) TeamUsage(c *gin.Context) {
	teamID := c.Param("teamId")
	opts := h.parseQueryOptions(c)

	totals, err := h.stores.Usage.GetTeamProviderTotals(c.Request.Context(), teamID, opts)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to get team usage"})
		return
	}

	results, err := h.stores.Usage.QueryByTeamProviders(c.Request.Context(), teamID, opts)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to query team usage"})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"totals":  totals,
		"results": results,
	})
}

// ── Admin: List Pricing ────────────────────
// GET /admin/pricing

func (h *UsageHandler) ListPricing(c *gin.Context) {
	entries, err := h.stores.Pricing.List(c.Request.Context())
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to list pricing"})
		return
	}
	if entries == nil {
		entries = []models.PricingEntry{}
	}

	c.JSON(http.StatusOK, entries)
}

// ── Admin: Upsert Pricing ──────────────────
// PUT /admin/pricing

func (h *UsageHandler) UpsertPricing(c *gin.Context) {
	var req models.PricingEntry
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// Validate provider is admin-managed (global or team), not personal BYOK
	pc, err := h.stores.Providers.GetByID(c.Request.Context(), req.ProviderConfigID)
	if err != nil || pc == nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "provider config not found"})
		return
	}
	if pc.Scope == "personal" {
		c.JSON(http.StatusForbidden, gin.H{"error": "cannot set pricing for personal BYOK providers"})
		return
	}

	userID := getUserID(c)
	req.Source = "manual"
	req.UpdatedBy = &userID

	if err := h.stores.Pricing.Upsert(c.Request.Context(), &req); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to save pricing"})
		return
	}

	c.JSON(http.StatusOK, gin.H{"message": "pricing saved"})
}

// ── Admin: Delete Pricing ──────────────────
// DELETE /admin/pricing/:provider/:model

func (h *UsageHandler) DeletePricing(c *gin.Context) {
	providerConfigID := c.Param("provider")
	modelID := c.Param("model")

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

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

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

func (h *UsageHandler) parseQueryOptions(c *gin.Context) store.UsageQueryOptions {
	opts := store.UsageQueryOptions{
		GroupBy: c.DefaultQuery("group_by", "model"),
		Limit:   100,
	}

	if since := c.Query("since"); since != "" {
		if t, err := time.Parse(time.RFC3339, since); err == nil {
			opts.Since = &t
		}
	}
	if until := c.Query("until"); until != "" {
		if t, err := time.Parse(time.RFC3339, until); err == nil {
			opts.Until = &t
		}
	}

	// Convenience shorthand: ?period=7d|30d|90d
	if period := c.Query("period"); period != "" && opts.Since == nil {
		var dur time.Duration
		switch period {
		case "7d":
			dur = 7 * 24 * time.Hour
		case "30d":
			dur = 30 * 24 * time.Hour
		case "90d":
			dur = 90 * 24 * time.Hour
		}
		if dur > 0 {
			t := time.Now().Add(-dur)
			opts.Since = &t
		}
	}

	return opts
}
