package api

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

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

type BatchActionRequest struct {
	Action string `json:"action"`
	IDs    []uint `json:"ids"`
	Status string `json:"status,omitempty"`
}

type BatchResult struct {
	ID    uint   `json:"id"`
	Error string `json:"error,omitempty"`
}

type BatchResponse struct {
	Action  string        `json:"action"`
	Success []uint        `json:"success"`
	Failed  []BatchResult `json:"failed"`
}

func normalizeBatchAction(raw string) string {
	raw = strings.ToLower(strings.TrimSpace(raw))
	if raw == "" {
		return "delete"
	}
	return raw
}

func normalizeStatus(raw string) string {
	return strings.ToLower(strings.TrimSpace(raw))
}

func isAllowedStatus(raw string, allowed ...string) bool {
	raw = normalizeStatus(raw)
	for _, cand := range allowed {
		if raw == cand {
			return true
		}
	}
	return false
}

// BatchMasters godoc
// @Summary      Batch masters
// @Description  Batch delete or status update for masters
// @Tags         admin
// @Accept       json
// @Produce      json
// @Security     AdminAuth
// @Param        request  body      BatchActionRequest  true  "Batch payload"
// @Success      200      {object}  BatchResponse
// @Failure      400      {object}  gin.H
// @Failure      500      {object}  gin.H
// @Router       /admin/masters/batch [post]
func (h *AdminHandler) BatchMasters(c *gin.Context) {
	var req BatchActionRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"})
		return
	}
	action := normalizeBatchAction(req.Action)
	if action != "delete" && action != "status" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "unsupported action"})
		return
	}
	if len(req.IDs) == 0 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "ids required"})
		return
	}

	resp := BatchResponse{Action: action}
	status := normalizeStatus(req.Status)
	if action == "status" && !isAllowedStatus(status, "active", "suspended") {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid status"})
		return
	}
	for _, id := range req.IDs {
		var err error
		switch action {
		case "delete":
			err = h.revokeMasterByID(id)
		case "status":
			err = h.setMasterStatusByID(id, status)
		}
		if err != nil {
			if isRecordNotFound(err) {
				resp.Failed = append(resp.Failed, BatchResult{ID: id, Error: "not found"})
				continue
			}
			resp.Failed = append(resp.Failed, BatchResult{ID: id, Error: err.Error()})
			continue
		}
		resp.Success = append(resp.Success, id)
	}
	c.JSON(http.StatusOK, resp)
}

// BatchAPIKeys godoc
// @Summary      Batch api keys
// @Description  Batch delete or status update for api keys
// @Tags         admin
// @Accept       json
// @Produce      json
// @Security     AdminAuth
// @Param        request  body      BatchActionRequest  true  "Batch payload"
// @Success      200      {object}  BatchResponse
// @Failure      400      {object}  gin.H
// @Failure      500      {object}  gin.H
// @Router       /admin/api-keys/batch [post]
func (h *Handler) BatchAPIKeys(c *gin.Context) {
	var req BatchActionRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"})
		return
	}
	action := normalizeBatchAction(req.Action)
	if action != "delete" && action != "status" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "unsupported action"})
		return
	}
	if len(req.IDs) == 0 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "ids required"})
		return
	}

	resp := BatchResponse{Action: action}
	status := normalizeStatus(req.Status)
	if action == "status" && !isAllowedStatus(status, "active", "auto_disabled", "manual_disabled") {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid status"})
		return
	}
	needsBindingSync := false
	for _, id := range req.IDs {
		var key model.APIKey
		if err := h.db.First(&key, id).Error; err != nil {
			if errors.Is(err, gorm.ErrRecordNotFound) {
				resp.Failed = append(resp.Failed, BatchResult{ID: id, Error: "not found"})
				continue
			}
			resp.Failed = append(resp.Failed, BatchResult{ID: id, Error: err.Error()})
			continue
		}
		switch action {
		case "delete":
			if err := h.db.Delete(&key).Error; err != nil {
				resp.Failed = append(resp.Failed, BatchResult{ID: id, Error: err.Error()})
				continue
			}
			resp.Success = append(resp.Success, id)
			needsBindingSync = true
		case "status":
			update := map[string]any{"status": status}
			if status == "active" {
				update["ban_reason"] = ""
				update["ban_until"] = nil
			}
			if err := h.db.Model(&key).Updates(update).Error; err != nil {
				resp.Failed = append(resp.Failed, BatchResult{ID: id, Error: err.Error()})
				continue
			}
			if err := h.db.First(&key, id).Error; err != nil {
				resp.Failed = append(resp.Failed, BatchResult{ID: id, Error: err.Error()})
				continue
			}
			resp.Success = append(resp.Success, id)
			needsBindingSync = true
		}
	}
	if needsBindingSync {
		if err := h.sync.SyncProviders(h.db); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to sync providers", "details": err.Error()})
			return
		}
		if err := h.sync.SyncBindings(h.db); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to sync bindings", "details": err.Error()})
			return
		}
	}
	c.JSON(http.StatusOK, resp)
}

// BatchModels godoc
// @Summary      Batch models
// @Description  Batch delete for models
// @Tags         admin
// @Accept       json
// @Produce      json
// @Security     AdminAuth
// @Param        request  body      BatchActionRequest  true  "Batch payload"
// @Success      200      {object}  BatchResponse
// @Failure      400      {object}  gin.H
// @Failure      500      {object}  gin.H
// @Router       /admin/models/batch [post]
func (h *Handler) BatchModels(c *gin.Context) {
	var req BatchActionRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"})
		return
	}
	action := normalizeBatchAction(req.Action)
	if action != "delete" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "unsupported action"})
		return
	}
	if len(req.IDs) == 0 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "ids required"})
		return
	}

	resp := BatchResponse{Action: action}
	for _, id := range req.IDs {
		var m model.Model
		if err := h.db.First(&m, id).Error; err != nil {
			if errors.Is(err, gorm.ErrRecordNotFound) {
				resp.Failed = append(resp.Failed, BatchResult{ID: id, Error: "not found"})
				continue
			}
			resp.Failed = append(resp.Failed, BatchResult{ID: id, Error: err.Error()})
			continue
		}
		if err := h.db.Delete(&m).Error; err != nil {
			resp.Failed = append(resp.Failed, BatchResult{ID: id, Error: err.Error()})
			continue
		}
		if err := h.sync.SyncModelDelete(&m); err != nil {
			resp.Failed = append(resp.Failed, BatchResult{ID: id, Error: err.Error()})
			continue
		}
		resp.Success = append(resp.Success, id)
	}
	c.JSON(http.StatusOK, resp)
}

// BatchBindings godoc
// @Summary      Batch bindings
// @Description  Batch delete or status update for bindings
// @Tags         admin
// @Accept       json
// @Produce      json
// @Security     AdminAuth
// @Param        request  body      BatchActionRequest  true  "Batch payload"
// @Success      200      {object}  BatchResponse
// @Failure      400      {object}  gin.H
// @Failure      500      {object}  gin.H
// @Router       /admin/bindings/batch [post]
func (h *Handler) BatchBindings(c *gin.Context) {
	var req BatchActionRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "invalid request body"})
		return
	}
	action := normalizeBatchAction(req.Action)
	if action != "delete" && action != "status" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "unsupported action"})
		return
	}
	if len(req.IDs) == 0 {
		c.JSON(http.StatusBadRequest, gin.H{"error": "ids required"})
		return
	}

	resp := BatchResponse{Action: action}
	needsSync := false
	status := normalizeStatus(req.Status)
	if action == "status" && status == "" {
		c.JSON(http.StatusBadRequest, gin.H{"error": "status required"})
		return
	}
	for _, id := range req.IDs {
		var b model.Binding
		if err := h.db.First(&b, id).Error; err != nil {
			if errors.Is(err, gorm.ErrRecordNotFound) {
				resp.Failed = append(resp.Failed, BatchResult{ID: id, Error: "not found"})
				continue
			}
			resp.Failed = append(resp.Failed, BatchResult{ID: id, Error: err.Error()})
			continue
		}
		switch action {
		case "delete":
			if err := h.db.Delete(&b).Error; err != nil {
				resp.Failed = append(resp.Failed, BatchResult{ID: id, Error: err.Error()})
				continue
			}
			resp.Success = append(resp.Success, id)
			needsSync = true
		case "status":
			if err := h.db.Model(&b).Update("status", status).Error; err != nil {
				resp.Failed = append(resp.Failed, BatchResult{ID: id, Error: err.Error()})
				continue
			}
			resp.Success = append(resp.Success, id)
			needsSync = true
		}
	}
	if needsSync {
		if err := h.sync.SyncBindings(h.db); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to sync bindings", "details": err.Error()})
			return
		}
	}
	c.JSON(http.StatusOK, resp)
}
