package handlers

import (
	"net/http"
	"strconv"

	"github.com/labstack/echo/v4"
	"gorm.io/gorm"

	"github.com/treytartt/honeydue-api/internal/admin/dto"
	"github.com/treytartt/honeydue-api/internal/models"
)

// AdminPasswordResetCodeHandler handles admin password reset code management endpoints
type AdminPasswordResetCodeHandler struct {
	db *gorm.DB
}

// NewAdminPasswordResetCodeHandler creates a new admin password reset code handler
func NewAdminPasswordResetCodeHandler(db *gorm.DB) *AdminPasswordResetCodeHandler {
	return &AdminPasswordResetCodeHandler{db: db}
}

// PasswordResetCodeResponse represents a password reset code in API responses
type PasswordResetCodeResponse struct {
	ID          uint   `json:"id"`
	UserID      uint   `json:"user_id"`
	Username    string `json:"username"`
	Email       string `json:"email"`
	ResetToken  string `json:"reset_token"`
	ExpiresAt   string `json:"expires_at"`
	Used        bool   `json:"used"`
	Attempts    int    `json:"attempts"`
	MaxAttempts int    `json:"max_attempts"`
	CreatedAt   string `json:"created_at"`
}

// List handles GET /api/admin/password-reset-codes
func (h *AdminPasswordResetCodeHandler) List(c echo.Context) error {
	var filters dto.PaginationParams
	if err := c.Bind(&filters); err != nil {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Invalid request body"})
	}

	var codes []models.PasswordResetCode
	var total int64

	query := h.db.Model(&models.PasswordResetCode{}).Preload("User")

	// Apply search (search by user info or token)
	if filters.Search != "" {
		search := "%" + filters.Search + "%"
		query = query.Joins("JOIN auth_user ON auth_user.id = user_passwordresetcode.user_id").
			Where(
				"auth_user.username ILIKE ? OR auth_user.email ILIKE ? OR user_passwordresetcode.reset_token ILIKE ?",
				search, search, search,
			)
	}

	// Get total count
	query.Count(&total)

	// Apply sorting (allowlist prevents SQL injection via sort_by parameter)
	sortBy := filters.GetSafeSortBy([]string{
		"id", "user_id", "created_at", "expires_at", "used",
	}, "created_at")
	query = query.Order(sortBy + " " + filters.GetSortDir())

	// Apply pagination
	query = query.Offset(filters.GetOffset()).Limit(filters.GetPerPage())

	if err := query.Find(&codes).Error; err != nil {
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to fetch password reset codes"})
	}

	// Build response
	responses := make([]PasswordResetCodeResponse, len(codes))
	for i, code := range codes {
		responses[i] = PasswordResetCodeResponse{
			ID:          code.ID,
			UserID:      code.UserID,
			Username:    code.User.Username,
			Email:       code.User.Email,
			ResetToken:  code.ResetToken[:8] + "..." + code.ResetToken[len(code.ResetToken)-4:], // Truncate for display
			ExpiresAt:   code.ExpiresAt.Format("2006-01-02T15:04:05Z"),
			Used:        code.Used,
			Attempts:    code.Attempts,
			MaxAttempts: code.MaxAttempts,
			CreatedAt:   code.CreatedAt.Format("2006-01-02T15:04:05Z"),
		}
	}

	return c.JSON(http.StatusOK, dto.NewPaginatedResponse(responses, total, filters.GetPage(), filters.GetPerPage()))
}

// Get handles GET /api/admin/password-reset-codes/:id
func (h *AdminPasswordResetCodeHandler) Get(c echo.Context) error {
	id, err := strconv.ParseUint(c.Param("id"), 10, 32)
	if err != nil {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Invalid ID"})
	}

	var code models.PasswordResetCode
	if err := h.db.Preload("User").First(&code, id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return c.JSON(http.StatusNotFound, map[string]interface{}{"error": "Password reset code not found"})
		}
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to fetch password reset code"})
	}

	response := PasswordResetCodeResponse{
		ID:          code.ID,
		UserID:      code.UserID,
		Username:    code.User.Username,
		Email:       code.User.Email,
		ResetToken:  code.ResetToken[:8] + "..." + code.ResetToken[len(code.ResetToken)-4:],
		ExpiresAt:   code.ExpiresAt.Format("2006-01-02T15:04:05Z"),
		Used:        code.Used,
		Attempts:    code.Attempts,
		MaxAttempts: code.MaxAttempts,
		CreatedAt:   code.CreatedAt.Format("2006-01-02T15:04:05Z"),
	}

	return c.JSON(http.StatusOK, response)
}

// Delete handles DELETE /api/admin/password-reset-codes/:id
func (h *AdminPasswordResetCodeHandler) Delete(c echo.Context) error {
	id, err := strconv.ParseUint(c.Param("id"), 10, 32)
	if err != nil {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Invalid ID"})
	}

	result := h.db.Delete(&models.PasswordResetCode{}, id)
	if result.Error != nil {
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to delete password reset code"})
	}

	if result.RowsAffected == 0 {
		return c.JSON(http.StatusNotFound, map[string]interface{}{"error": "Password reset code not found"})
	}

	return c.JSON(http.StatusOK, map[string]interface{}{"message": "Password reset code deleted successfully"})
}

// BulkDelete handles DELETE /api/admin/password-reset-codes/bulk
func (h *AdminPasswordResetCodeHandler) BulkDelete(c echo.Context) error {
	var req dto.BulkDeleteRequest
	if err := c.Bind(&req); err != nil {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Invalid request body"})
	}

	result := h.db.Where("id IN ?", req.IDs).Delete(&models.PasswordResetCode{})
	if result.Error != nil {
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to delete password reset codes"})
	}

	return c.JSON(http.StatusOK, map[string]interface{}{"message": "Password reset codes deleted successfully", "count": result.RowsAffected})
}
