package controllers

import (
	"fmt"

	"fotbal-club/pkg/logger"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

// BatchOperationsController handles bulk operations
type BatchOperationsController struct {
	DB *gorm.DB
}

// NewBatchOperationsController creates a new BatchOperationsController instance
func NewBatchOperationsController(db *gorm.DB) *BatchOperationsController {
	return &BatchOperationsController{DB: db}
}

// BatchDeleteRequest represents a batch delete request
type BatchDeleteRequest struct {
	IDs []uint `json:"ids" binding:"required,min=1"`
}

// BatchUpdateRequest represents a batch update request
type BatchUpdateRequest struct {
	IDs    []uint                 `json:"ids" binding:"required,min=1"`
	Fields map[string]interface{} `json:"fields" binding:"required"`
}

// BatchResult represents the result of a batch operation
type BatchResult struct {
	Success      bool   `json:"success"`
	TotalItems   int    `json:"total_items"`
	SuccessCount int    `json:"success_count"`
	FailureCount int    `json:"failure_count"`
	Errors       []string `json:"errors,omitempty"`
}

// BatchDelete performs a batch delete operation
func (boc *BatchOperationsController) BatchDelete(c *gin.Context, model interface{}, tableName string) {
	var req BatchDeleteRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		Respond.BadRequest(c, "Invalid request: "+err.Error())
		return
	}

	if len(req.IDs) == 0 {
		Respond.BadRequest(c, "No IDs provided")
		return
	}

	// Perform batch delete
	result := boc.DB.Where("id IN ?", req.IDs).Delete(model)
	if result.Error != nil {
		logger.Error("Batch delete failed for %s: %v", tableName, result.Error)
		Respond.InternalError(c, "Failed to perform batch delete")
		return
	}

	batchResult := BatchResult{
		Success:      true,
		TotalItems:   len(req.IDs),
		SuccessCount: int(result.RowsAffected),
		FailureCount: len(req.IDs) - int(result.RowsAffected),
	}

	logger.Info("Batch deleted %d %s records", result.RowsAffected, tableName)
	Respond.Success(c, batchResult, fmt.Sprintf("Successfully deleted %d items", result.RowsAffected))
}

// BatchUpdate performs a batch update operation
func (boc *BatchOperationsController) BatchUpdate(c *gin.Context, model interface{}, tableName string, allowedFields []string) {
	var req BatchUpdateRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		Respond.BadRequest(c, "Invalid request: "+err.Error())
		return
	}

	if len(req.IDs) == 0 {
		Respond.BadRequest(c, "No IDs provided")
		return
	}

	if len(req.Fields) == 0 {
		Respond.BadRequest(c, "No fields to update")
		return
	}

	// Validate fields
	updates := make(map[string]interface{})
	for field, value := range req.Fields {
		allowed := false
		for _, af := range allowedFields {
			if field == af {
				allowed = true
				break
			}
		}
		if !allowed {
			Respond.BadRequest(c, fmt.Sprintf("Field '%s' is not allowed for batch update", field))
			return
		}
		updates[field] = value
	}

	// Perform batch update
	result := boc.DB.Model(model).Where("id IN ?", req.IDs).Updates(updates)
	if result.Error != nil {
		logger.Error("Batch update failed for %s: %v", tableName, result.Error)
		Respond.InternalError(c, "Failed to perform batch update")
		return
	}

	batchResult := BatchResult{
		Success:      true,
		TotalItems:   len(req.IDs),
		SuccessCount: int(result.RowsAffected),
		FailureCount: len(req.IDs) - int(result.RowsAffected),
	}

	logger.Info("Batch updated %d %s records", result.RowsAffected, tableName)
	Respond.Success(c, batchResult, fmt.Sprintf("Successfully updated %d items", result.RowsAffected))
}

// BatchPublish publishes/unpublishes multiple items (for publishable entities)
func (boc *BatchOperationsController) BatchPublish(c *gin.Context, model interface{}, tableName string, publish bool) {
	var req BatchDeleteRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		Respond.BadRequest(c, "Invalid request: "+err.Error())
		return
	}

	if len(req.IDs) == 0 {
		Respond.BadRequest(c, "No IDs provided")
		return
	}

	// Update published status
	result := boc.DB.Model(model).Where("id IN ?", req.IDs).Update("published", publish)
	if result.Error != nil {
		logger.Error("Batch publish failed for %s: %v", tableName, result.Error)
		Respond.InternalError(c, "Failed to perform batch publish")
		return
	}

	action := "published"
	if !publish {
		action = "unpublished"
	}

	batchResult := BatchResult{
		Success:      true,
		TotalItems:   len(req.IDs),
		SuccessCount: int(result.RowsAffected),
		FailureCount: len(req.IDs) - int(result.RowsAffected),
	}

	logger.Info("Batch %s %d %s records", action, result.RowsAffected, tableName)
	Respond.Success(c, batchResult, fmt.Sprintf("Successfully %s %d items", action, result.RowsAffected))
}

// BatchArchive archives/unarchives multiple items (for archivable entities)
func (boc *BatchOperationsController) BatchArchive(c *gin.Context, model interface{}, tableName string, archive bool) {
	var req BatchDeleteRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		Respond.BadRequest(c, "Invalid request: "+err.Error())
		return
	}

	if len(req.IDs) == 0 {
		Respond.BadRequest(c, "No IDs provided")
		return
	}

	// Update archived status
	result := boc.DB.Model(model).Where("id IN ?", req.IDs).Update("archived", archive)
	if result.Error != nil {
		logger.Error("Batch archive failed for %s: %v", tableName, result.Error)
		Respond.InternalError(c, "Failed to perform batch archive")
		return
	}

	action := "archived"
	if !archive {
		action = "unarchived"
	}

	batchResult := BatchResult{
		Success:      true,
		TotalItems:   len(req.IDs),
		SuccessCount: int(result.RowsAffected),
		FailureCount: len(req.IDs) - int(result.RowsAffected),
	}

	logger.Info("Batch %s %d %s records", action, result.RowsAffected, tableName)
	Respond.Success(c, batchResult, fmt.Sprintf("Successfully %s %d items", action, result.RowsAffected))
}

// BatchReorder reorders items based on provided order
func (boc *BatchOperationsController) BatchReorder(c *gin.Context, model interface{}, tableName string) {
	var req struct {
		Orders []struct {
			ID    uint `json:"id" binding:"required"`
			Order int  `json:"order" binding:"required"`
		} `json:"orders" binding:"required,min=1"`
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		Respond.BadRequest(c, "Invalid request: "+err.Error())
		return
	}

	// Update order for each item
	successCount := 0
	errors := []string{}

	for _, item := range req.Orders {
		result := boc.DB.Model(model).Where("id = ?", item.ID).Update("display_order", item.Order)
		if result.Error != nil {
			errors = append(errors, fmt.Sprintf("Failed to update order for ID %d: %v", item.ID, result.Error))
			continue
		}
		successCount++
	}

	batchResult := BatchResult{
		Success:      len(errors) == 0,
		TotalItems:   len(req.Orders),
		SuccessCount: successCount,
		FailureCount: len(errors),
		Errors:       errors,
	}

	if len(errors) > 0 {
		logger.Warn("Batch reorder partially failed for %s: %d successes, %d failures", tableName, successCount, len(errors))
		Respond.Custom(c, 207, false, batchResult, "", "Batch reorder completed with errors") // 207 Multi-Status
		return
	}

	logger.Info("Batch reordered %d %s records", successCount, tableName)
	Respond.Success(c, batchResult, fmt.Sprintf("Successfully reordered %d items", successCount))
}

// BatchDuplicate duplicates multiple items
func (boc *BatchOperationsController) BatchDuplicate(c *gin.Context, tableName string, duplicateFunc func(id uint) error) {
	var req BatchDeleteRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		Respond.BadRequest(c, "Invalid request: "+err.Error())
		return
	}

	if len(req.IDs) == 0 {
		Respond.BadRequest(c, "No IDs provided")
		return
	}

	// Duplicate each item
	successCount := 0
	errors := []string{}

	for _, id := range req.IDs {
		if err := duplicateFunc(id); err != nil {
			errors = append(errors, fmt.Sprintf("Failed to duplicate ID %d: %v", id, err))
			continue
		}
		successCount++
	}

	batchResult := BatchResult{
		Success:      len(errors) == 0,
		TotalItems:   len(req.IDs),
		SuccessCount: successCount,
		FailureCount: len(errors),
		Errors:       errors,
	}

	if len(errors) > 0 {
		logger.Warn("Batch duplicate partially failed for %s: %d successes, %d failures", tableName, successCount, len(errors))
		Respond.Custom(c, 207, false, batchResult, "", "Batch duplicate completed with errors")
		return
	}

	logger.Info("Batch duplicated %d %s records", successCount, tableName)
	Respond.Success(c, batchResult, fmt.Sprintf("Successfully duplicated %d items", successCount))
}

// Global BatchOperationsController instance (needs to be initialized with DB)
var BatchOps *BatchOperationsController

// InitBatchOperations initializes the global batch operations controller
func InitBatchOperations(db *gorm.DB) {
	BatchOps = NewBatchOperationsController(db)
}
