package api

import (
	"net/http"
	"strings"

	"github.com/ez-api/ez-api/internal/dto"
	"github.com/ez-api/ez-api/internal/model"
	"github.com/gin-gonic/gin"
)

// CreateAPIKey godoc
// @Summary      Create an API key
// @Description  Create an API key for a provider group
// @Tags         admin
// @Accept       json
// @Produce      json
// @Security     AdminAuth
// @Param        key  body      dto.APIKeyDTO  true  "API key payload"
// @Success      201  {object}  model.APIKey
// @Failure      400  {object}  gin.H
// @Failure      500  {object}  gin.H
// @Router       /admin/api-keys [post]
func (h *Handler) CreateAPIKey(c *gin.Context) {
	var req dto.APIKeyDTO
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}
	if req.GroupID == 0 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "group_id required"})
		return
	}
	var group model.ProviderGroup
	if err := h.db.First(&group, req.GroupID).Error; err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "provider group not found"})
		return
	}

	apiKey := strings.TrimSpace(req.APIKey)

	status := strings.TrimSpace(req.Status)
	if status == "" {
		status = "active"
	}
	autoBan := true
	if req.AutoBan != nil {
		autoBan = *req.AutoBan
	}

	key := model.APIKey{
		GroupID:     req.GroupID,
		APIKey:      apiKey,
		AccessToken: strings.TrimSpace(req.AccessToken),
		RefreshToken: strings.TrimSpace(req.RefreshToken),
		AccountID:   strings.TrimSpace(req.AccountID),
		ProjectID:   strings.TrimSpace(req.ProjectID),
		Weight:      normalizeWeight(req.Weight),
		Status:      status,
		AutoBan:     autoBan,
		BanReason:   strings.TrimSpace(req.BanReason),
	}
	if !req.ExpiresAt.IsZero() {
		tu := req.ExpiresAt.UTC()
		key.ExpiresAt = &tu
	}
	if !req.BanUntil.IsZero() {
		tu := req.BanUntil.UTC()
		key.BanUntil = &tu
	}
	if err := h.groupManager.ValidateAPIKey(group, key); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	if err := h.db.Create(&key).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create api key", "details": err.Error()})
		return
	}

	if err := h.sync.SyncProvidersForAPIKey(h.db, key.ID); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to sync providers", "details": err.Error()})
		return
	}
	if err := h.sync.SyncBindingsForAPIKey(h.db, key.ID); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to sync bindings", "details": err.Error()})
		return
	}

	c.JSON(http.StatusCreated, key)
}

// ListAPIKeys godoc
// @Summary      List API keys
// @Description  List API keys with optional filters
// @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        group_id query     int     false  "filter by group_id"
// @Param        status   query     string  false  "filter by status (active, suspended, auto_disabled, manual_disabled)"
// @Success      200      {array}   model.APIKey
// @Failure      500      {object}  gin.H
// @Router       /admin/api-keys [get]
func (h *Handler) ListAPIKeys(c *gin.Context) {
	var keys []model.APIKey
	q := h.db.Model(&model.APIKey{}).Order("id desc")
	if groupID := strings.TrimSpace(c.Query("group_id")); groupID != "" {
		q = q.Where("group_id = ?", groupID)
	}
	if status := strings.TrimSpace(c.Query("status")); status != "" {
		q = q.Where("status = ?", status)
	}
	query := parseListQuery(c)
	q = applyListPagination(q, query)
	if err := q.Find(&keys).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to list api keys", "details": err.Error()})
		return
	}
	c.JSON(http.StatusOK, keys)
}

// GetAPIKey godoc
// @Summary      Get API key
// @Description  Get an API key by id
// @Tags         admin
// @Produce      json
// @Security     AdminAuth
// @Param        id   path      int  true  "APIKey ID"
// @Success      200  {object}  model.APIKey
// @Failure      400  {object}  gin.H
// @Failure      404  {object}  gin.H
// @Failure      500  {object}  gin.H
// @Router       /admin/api-keys/{id} [get]
func (h *Handler) GetAPIKey(c *gin.Context) {
	id, ok := parseUintParam(c, "id")
	if !ok {
		return
	}
	var key model.APIKey
	if err := h.db.First(&key, id).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "api key not found"})
		return
	}
	c.JSON(http.StatusOK, key)
}

// UpdateAPIKey godoc
// @Summary      Update API key
// @Description  Update an API key
// @Tags         admin
// @Accept       json
// @Produce      json
// @Security     AdminAuth
// @Param        id   path      int            true  "APIKey ID"
// @Param        key  body      dto.APIKeyDTO  true  "API key payload"
// @Success      200  {object}  model.APIKey
// @Failure      400  {object}  gin.H
// @Failure      404  {object}  gin.H
// @Failure      500  {object}  gin.H
// @Router       /admin/api-keys/{id} [put]
func (h *Handler) UpdateAPIKey(c *gin.Context) {
	id, ok := parseUintParam(c, "id")
	if !ok {
		return
	}
	var key model.APIKey
	if err := h.db.First(&key, id).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "api key not found"})
		return
	}
	var req dto.APIKeyDTO
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
		return
	}

	update := map[string]any{}
	groupID := key.GroupID
	if req.GroupID != 0 {
		groupID = req.GroupID
	}
	var group model.ProviderGroup
	if err := h.db.First(&group, groupID).Error; err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "provider group not found"})
		return
	}
	if req.GroupID != 0 {
		update["group_id"] = req.GroupID
	}
	if strings.TrimSpace(req.APIKey) != "" {
		update["api_key"] = strings.TrimSpace(req.APIKey)
	}
	if strings.TrimSpace(req.AccessToken) != "" {
		update["access_token"] = strings.TrimSpace(req.AccessToken)
	}
	if strings.TrimSpace(req.RefreshToken) != "" {
		update["refresh_token"] = strings.TrimSpace(req.RefreshToken)
	}
	if !req.ExpiresAt.IsZero() {
		tu := req.ExpiresAt.UTC()
		update["expires_at"] = &tu
	}
	if strings.TrimSpace(req.AccountID) != "" {
		update["account_id"] = strings.TrimSpace(req.AccountID)
	}
	if strings.TrimSpace(req.ProjectID) != "" {
		update["project_id"] = strings.TrimSpace(req.ProjectID)
	}
	if req.Weight > 0 {
		update["weight"] = normalizeWeight(req.Weight)
	}
	if strings.TrimSpace(req.Status) != "" {
		update["status"] = strings.TrimSpace(req.Status)
	}
	if req.AutoBan != nil {
		update["auto_ban"] = *req.AutoBan
	}
	if req.BanReason != "" || strings.TrimSpace(req.Status) == "active" {
		update["ban_reason"] = strings.TrimSpace(req.BanReason)
	}
	if !req.BanUntil.IsZero() {
		tu := req.BanUntil.UTC()
		update["ban_until"] = &tu
	}
	if req.BanUntil.IsZero() && strings.TrimSpace(req.Status) == "active" {
		update["ban_until"] = nil
	}
	if req.GroupID != 0 || strings.TrimSpace(req.APIKey) != "" || strings.TrimSpace(req.AccessToken) != "" || strings.TrimSpace(req.RefreshToken) != "" {
		nextKey := key
		if v, ok := update["api_key"].(string); ok {
			nextKey.APIKey = v
		}
		if v, ok := update["access_token"].(string); ok {
			nextKey.AccessToken = v
		}
		if v, ok := update["refresh_token"].(string); ok {
			nextKey.RefreshToken = v
		}
		if req.GroupID != 0 {
			nextKey.GroupID = req.GroupID
		}
		if err := h.groupManager.ValidateAPIKey(group, nextKey); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}
	}

	if err := h.db.Model(&key).Updates(update).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update api key", "details": err.Error()})
		return
	}
	if err := h.db.First(&key, id).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to reload api key", "details": err.Error()})
		return
	}

	if err := h.sync.SyncProvidersForAPIKey(h.db, key.ID); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to sync providers", "details": err.Error()})
		return
	}
	if err := h.sync.SyncBindingsForAPIKey(h.db, key.ID); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to sync bindings", "details": err.Error()})
		return
	}

	c.JSON(http.StatusOK, key)
}

// DeleteAPIKey godoc
// @Summary      Delete API key
// @Description  Delete an API key
// @Tags         admin
// @Produce      json
// @Security     AdminAuth
// @Param        id   path      int  true  "APIKey ID"
// @Success      200  {object}  gin.H
// @Failure      400  {object}  gin.H
// @Failure      404  {object}  gin.H
// @Failure      500  {object}  gin.H
// @Router       /admin/api-keys/{id} [delete]
func (h *Handler) DeleteAPIKey(c *gin.Context) {
	id, ok := parseUintParam(c, "id")
	if !ok {
		return
	}
	var key model.APIKey
	if err := h.db.First(&key, id).Error; err != nil {
		c.JSON(http.StatusNotFound, gin.H{"error": "api key not found"})
		return
	}
	if err := h.db.Delete(&key).Error; err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete api key", "details": err.Error()})
		return
	}

	if err := h.sync.SyncProvidersForAPIKey(h.db, key.ID); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to sync providers", "details": err.Error()})
		return
	}
	if err := h.sync.SyncBindingsForAPIKey(h.db, key.ID); err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to sync bindings", "details": err.Error()})
		return
	}

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