package api

import (
	"errors"
	"net/http"
	"strconv"
	"strings"

	"github.com/ez-api/ez-api/internal/model"
	"github.com/ez-api/ez-api/internal/service"
	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

type AdminHandler struct {
	db             *gorm.DB
	logDB          *gorm.DB
	masterService  *service.MasterService
	syncService    *service.SyncService
	statsService   *service.StatsService
	logPartitioner *service.LogPartitioner
}

func NewAdminHandler(db *gorm.DB, logDB *gorm.DB, masterService *service.MasterService, syncService *service.SyncService, statsService *service.StatsService, partitioner *service.LogPartitioner) *AdminHandler {
	if logDB == nil {
		logDB = db
	}
	return &AdminHandler{db: db, logDB: logDB, masterService: masterService, syncService: syncService, statsService: statsService, logPartitioner: partitioner}
}

func (h *AdminHandler) logDBConn() *gorm.DB {
	if h == nil || h.logDB == nil {
		return h.db
	}
	return h.logDB
}

func (h *AdminHandler) logBaseQuery() *gorm.DB {
	return logBaseQuery(h.logDBConn(), h.logPartitioner)
}

type CreateMasterRequest struct {
	Name         string `json:"name" binding:"required"`
	Group        string `json:"group" binding:"required"`
	MaxChildKeys int    `json:"max_child_keys"`
	GlobalQPS    int    `json:"global_qps"`
}

// CreateMaster godoc
// @Summary      Create a new master tenant
// @Description  Create a new master account (tenant)
// @Tags         admin
// @Accept       json
// @Produce      json
// @Security     AdminAuth
// @Param        master  body      CreateMasterRequest  true  "Master Info"
// @Success      201     {object}  gin.H
// @Failure      400     {object}  gin.H
// @Failure      500     {object}  gin.H
// @Router       /admin/masters [post]
func (h *AdminHandler) CreateMaster(c *gin.Context) {
	var req CreateMasterRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	// Use defaults if not provided
	if req.MaxChildKeys == 0 {
		req.MaxChildKeys = 5
	}
	if req.GlobalQPS < 0 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "global_qps must be >= 0"})
		return
	}

	master, rawMasterKey, err := h.masterService.CreateMaster(req.Name, req.Group, req.MaxChildKeys, req.GlobalQPS)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to create master key", "details": err.Error()})
		return
	}

	if err := h.syncService.SyncMaster(master); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to sync master key", "details": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, gin.H{
		"id":             master.ID,
		"name":           master.Name,
		"group":          master.Group,
		"master_key":     rawMasterKey, // Only show this on creation
		"max_child_keys": master.MaxChildKeys,
		"global_qps":     master.GlobalQPS,
	})
}

type MasterView struct {
	ID               uint                `json:"id"`
	Name             string              `json:"name"`
	Group            string              `json:"group"`
	DefaultNamespace string              `json:"default_namespace"`
	Namespaces       string              `json:"namespaces"`
	Epoch            int64               `json:"epoch"`
	Status           string              `json:"status"`
	MaxChildKeys     int                 `json:"max_child_keys"`
	GlobalQPS        int                 `json:"global_qps"`
	CreatedAt        int64               `json:"created_at"`
	UpdatedAt        int64               `json:"updated_at"`
	Realtime         *MasterRealtimeView `json:"realtime,omitempty"`
}

func toMasterView(m model.Master) MasterView {
	return MasterView{
		ID:               m.ID,
		Name:             m.Name,
		Group:            m.Group,
		DefaultNamespace: strings.TrimSpace(m.DefaultNamespace),
		Namespaces:       strings.TrimSpace(m.Namespaces),
		Epoch:            m.Epoch,
		Status:           m.Status,
		MaxChildKeys:     m.MaxChildKeys,
		GlobalQPS:        m.GlobalQPS,
		CreatedAt:        m.CreatedAt.UTC().Unix(),
		UpdatedAt:        m.UpdatedAt.UTC().Unix(),
	}
}

// ListMasters godoc
// @Summary      List masters
// @Description  List all master tenants
// @Tags         admin
// @Produce      json
// @Security     AdminAuth
// @Param        page    query     int     false  "page (1-based)"
// @Param        limit   query     int     false  "limit (default 50, max 200)"
// @Param        search  query     string  false  "search by name/group"
// @Success      200  {array}   MasterView
// @Failure      500  {object}  gin.H
// @Router       /admin/masters [get]
func (h *AdminHandler) ListMasters(c *gin.Context) {
	var masters []model.Master
	q := h.db.Model(&model.Master{}).Order("id desc")
	query := parseListQuery(c)
	q = applyListSearch(q, query.Search, "name", `"group"`)
	q = applyListPagination(q, query)
	if err := q.Find(&masters).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to list masters", "details": err.Error()})
		return
	}
	out := make([]MasterView, 0, len(masters))
	for _, m := range masters {
		out = append(out, toMasterView(m))
	}
	c.JSON(http.StatusOK, out)
}

// GetMaster godoc
// @Summary      Get master
// @Description  Get a master tenant by id
// @Tags         admin
// @Produce      json
// @Security     AdminAuth
// @Param        id   path      int  true  "Master ID"
// @Success      200  {object}  MasterView
// @Failure      400  {object}  gin.H
// @Failure      404  {object}  gin.H
// @Failure      500  {object}  gin.H
// @Router       /admin/masters/{id} [get]
func (h *AdminHandler) GetMaster(c *gin.Context) {
	idRaw := strings.TrimSpace(c.Param("id"))
	idU64, err := strconv.ParseUint(idRaw, 10, 64)
	if err != nil || idU64 == 0 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid master id"})
		return
	}
	var m model.Master
	if err := h.db.First(&m, uint(idU64)).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "master not found"})
		return
	}
	view := toMasterView(m)
	if h.statsService != nil {
		if stats, err := h.statsService.GetMasterRealtimeSnapshot(c.Request.Context(), m.ID); err == nil {
			if stats.QPSLimit == 0 && m.GlobalQPS > 0 {
				stats.QPSLimit = int64(m.GlobalQPS)
			}
			view.Realtime = toMasterRealtimeView(stats)
		}
	}
	c.JSON(http.StatusOK, view)
}

type UpdateMasterRequest struct {
	Name            *string `json:"name,omitempty"`
	Group           *string `json:"group,omitempty"`
	MaxChildKeys    *int    `json:"max_child_keys,omitempty"`
	GlobalQPS       *int    `json:"global_qps,omitempty"`
	PropagateToKeys bool    `json:"propagate_to_keys,omitempty"`
}

// UpdateMaster godoc
// @Summary      Update master
// @Description  Update master fields; optionally propagate group to existing keys
// @Tags         admin
// @Accept       json
// @Produce      json
// @Security     AdminAuth
// @Param        id       path      int                true  "Master ID"
// @Param        request  body      UpdateMasterRequest true  "Update payload"
// @Success      200      {object}  MasterView
// @Failure      400      {object}  gin.H
// @Failure      404      {object}  gin.H
// @Failure      500      {object}  gin.H
// @Router       /admin/masters/{id} [put]
func (h *AdminHandler) UpdateMaster(c *gin.Context) {
	idRaw := strings.TrimSpace(c.Param("id"))
	idU64, err := strconv.ParseUint(idRaw, 10, 64)
	if err != nil || idU64 == 0 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid master id"})
		return
	}
	var req UpdateMasterRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	var m model.Master
	if err := h.db.First(&m, uint(idU64)).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "master not found"})
		return
	}

	update := make(map[string]any)
	if req.Name != nil {
		update["name"] = strings.TrimSpace(*req.Name)
	}
	if req.Group != nil {
		g := strings.TrimSpace(*req.Group)
		if g != "" {
			update["group"] = g
		}
	}
	if req.MaxChildKeys != nil && *req.MaxChildKeys > 0 {
		update["max_child_keys"] = *req.MaxChildKeys
	}
	if req.GlobalQPS != nil {
		if *req.GlobalQPS < 0 {
			c.JSON(http.StatusBadRequest, gin.H{"error": "global_qps must be >= 0"})
			return
		}
		update["global_qps"] = *req.GlobalQPS
	}
	if len(update) == 0 && !req.PropagateToKeys {
		c.JSON(http.StatusBadRequest, gin.H{"error": "no fields to update"})
		return
	}

	if len(update) > 0 {
		if err := h.db.Model(&m).Updates(update).Error; err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update master", "details": err.Error()})
			return
		}
	}
	if err := h.db.First(&m, uint(idU64)).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to reload master", "details": err.Error()})
		return
	}

	if req.PropagateToKeys && req.Group != nil {
		g := strings.TrimSpace(*req.Group)
		if g != "" {
			if err := h.db.Model(&model.Key{}).Where("master_id = ?", m.ID).Updates(map[string]any{"group": g}).Error; err != nil {
				c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to propagate group to keys", "details": err.Error()})
				return
			}
		}
	}

	// Sync master metadata and (if propagated) keys.
	if err := h.syncService.SyncMaster(&m); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to sync master", "details": err.Error()})
		return
	}
	if req.PropagateToKeys {
		var keys []model.Key
		if err := h.db.Where("master_id = ?", m.ID).Find(&keys).Error; err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to reload keys for sync", "details": err.Error()})
			return
		}
		for i := range keys {
			if err := h.syncService.SyncKey(&keys[i]); err != nil {
				c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to sync key", "details": err.Error()})
				return
			}
		}
	}

	c.JSON(http.StatusOK, toMasterView(m))
}

type ManageMasterRequest struct {
	Action string `json:"action" binding:"required"` // freeze/unfreeze
}

// ManageMaster godoc
// @Summary      Manage master status
// @Description  Freeze or unfreeze a master tenant
// @Tags         admin
// @Accept       json
// @Produce      json
// @Security     AdminAuth
// @Param        id       path      int                true  "Master ID"
// @Param        request  body      ManageMasterRequest true  "Action"
// @Success      200      {object}  MasterView
// @Failure      400      {object}  gin.H
// @Failure      404      {object}  gin.H
// @Failure      500      {object}  gin.H
// @Router       /admin/masters/{id}/manage [post]
func (h *AdminHandler) ManageMaster(c *gin.Context) {
	idRaw := strings.TrimSpace(c.Param("id"))
	idU64, err := strconv.ParseUint(idRaw, 10, 64)
	if err != nil || idU64 == 0 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid master id"})
		return
	}
	var req ManageMasterRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	action := strings.ToLower(strings.TrimSpace(req.Action))
	var status string
	switch action {
	case "freeze":
		status = "suspended"
	case "unfreeze":
		status = "active"
	default:
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid action"})
		return
	}

	var m model.Master
	if err := h.db.First(&m, uint(idU64)).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "master not found"})
		return
	}

	if err := h.db.Model(&m).Update("status", status).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update master status", "details": err.Error()})
		return
	}
	if err := h.db.First(&m, uint(idU64)).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to reload master", "details": err.Error()})
		return
	}
	if err := h.syncService.SyncMaster(&m); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to sync master", "details": err.Error()})
		return
	}
	c.JSON(http.StatusOK, toMasterView(m))
}

// DeleteMaster godoc
// @Summary      Delete (revoke) master
// @Description  Suspends a master and revokes all existing keys by bumping epoch and syncing to Redis
// @Tags         admin
// @Produce      json
// @Security     AdminAuth
// @Param        id   path      int  true  "Master ID"
// @Success      200  {object}  gin.H
// @Failure      400  {object}  gin.H
// @Failure      404  {object}  gin.H
// @Failure      500  {object}  gin.H
// @Router       /admin/masters/{id} [delete]
func (h *AdminHandler) DeleteMaster(c *gin.Context) {
	idRaw := strings.TrimSpace(c.Param("id"))
	idU64, err := strconv.ParseUint(idRaw, 10, 64)
	if err != nil || idU64 == 0 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid master id"})
		return
	}

	if err := h.revokeMasterByID(uint(idU64)); err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			c.JSON(http.StatusNotFound, gin.H{"error": "master not found"})
			return
		}
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to revoke master", "details": err.Error()})
		return
	}

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

// IssueChildKeyForMaster godoc
// @Summary      Issue a child key on behalf of a master
// @Description  Issue a new access token (child key) for a specified master. The key still belongs to the master; issuer is recorded as admin for audit.
// @Tags         admin
// @Accept       json
// @Produce      json
// @Security     AdminAuth
// @Param        id         path      int                  true  "Master ID"
// @Param        request    body      IssueChildKeyRequest  true  "Key Request"
// @Success      201        {object}  gin.H
// @Failure      400        {object}  gin.H
// @Failure      403        {object}  gin.H
// @Failure      404        {object}  gin.H
// @Failure      500        {object}  gin.H
// @Router       /admin/masters/{id}/keys [post]
func (h *AdminHandler) IssueChildKeyForMaster(c *gin.Context) {
	idRaw := strings.TrimSpace(c.Param("id"))
	if idRaw == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "master id required"})
		return
	}
	idU64, err := strconv.ParseUint(idRaw, 10, 64)
	if err != nil || idU64 == 0 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid master id"})
		return
	}
	masterID := uint(idU64)

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

	modelLimits := strings.TrimSpace(req.ModelLimits)
	modelLimitsEnabled := false
	if req.ModelLimitsEnabled != nil {
		modelLimitsEnabled = *req.ModelLimitsEnabled
	} else if modelLimits != "" {
		modelLimitsEnabled = true
	}

	key, rawChildKey, err := h.masterService.IssueChildKeyAsAdmin(masterID, service.IssueKeyOptions{
		Group:              strings.TrimSpace(req.Group),
		Scopes:             strings.TrimSpace(req.Scopes),
		ModelLimits:        modelLimits,
		ModelLimitsEnabled: modelLimitsEnabled,
		ExpiresAt:          req.ExpiresAt,
		AllowIPs:           strings.TrimSpace(req.AllowIPs),
		DenyIPs:            strings.TrimSpace(req.DenyIPs),
	})
	if err != nil {
		switch {
		case errors.Is(err, service.ErrMasterNotFound):
			c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
		case errors.Is(err, service.ErrMasterNotActive):
			c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
		case errors.Is(err, service.ErrModelLimitForbidden):
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		case errors.Is(err, service.ErrChildKeyGroupForbidden):
			c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
		case errors.Is(err, service.ErrChildKeyLimitReached):
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		default:
			c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to issue child key", "details": err.Error()})
		}
		return
	}

	if err := h.syncService.SyncKey(key); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to sync child key", "details": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, gin.H{
		"id":         key.ID,
		"key_secret": rawChildKey,
		"group":      key.Group,
		"scopes":     key.Scopes,
		"issued_by":  key.IssuedBy,
	})
}
