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"
)

// AdminShareCodeHandler handles admin share code management endpoints
type AdminShareCodeHandler struct {
	db *gorm.DB
}

// NewAdminShareCodeHandler creates a new admin share code handler
func NewAdminShareCodeHandler(db *gorm.DB) *AdminShareCodeHandler {
	return &AdminShareCodeHandler{db: db}
}

// ShareCodeResponse represents a share code in API responses
type ShareCodeResponse struct {
	ID            uint    `json:"id"`
	ResidenceID   uint    `json:"residence_id"`
	ResidenceName string  `json:"residence_name"`
	Code          string  `json:"code"`
	CreatedByID   uint    `json:"created_by_id"`
	CreatedBy     string  `json:"created_by"`
	IsActive      bool    `json:"is_active"`
	ExpiresAt     *string `json:"expires_at"`
	CreatedAt     string  `json:"created_at"`
}

// List handles GET /api/admin/share-codes
func (h *AdminShareCodeHandler) 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.ResidenceShareCode
	var total int64

	query := h.db.Model(&models.ResidenceShareCode{}).
		Preload("Residence").
		Preload("CreatedBy")

	// Apply search (search by code or residence name)
	if filters.Search != "" {
		search := "%" + filters.Search + "%"
		query = query.Joins("JOIN residence_residence ON residence_residence.id = residence_residencesharecode.residence_id").
			Joins("JOIN auth_user ON auth_user.id = residence_residencesharecode.created_by_id").
			Where(
				"residence_residencesharecode.code ILIKE ? OR residence_residence.name ILIKE ? OR auth_user.username 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", "residence_id", "code", "created_by_id",
		"is_active", "expires_at", "created_at",
	}, "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 share codes"})
	}

	// Build response
	responses := make([]ShareCodeResponse, len(codes))
	for i, code := range codes {
		var expiresAt *string
		if code.ExpiresAt != nil {
			formatted := code.ExpiresAt.Format("2006-01-02T15:04:05Z")
			expiresAt = &formatted
		}

		responses[i] = ShareCodeResponse{
			ID:            code.ID,
			ResidenceID:   code.ResidenceID,
			ResidenceName: code.Residence.Name,
			Code:          code.Code,
			CreatedByID:   code.CreatedByID,
			CreatedBy:     code.CreatedBy.Username,
			IsActive:      code.IsActive,
			ExpiresAt:     expiresAt,
			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/share-codes/:id
func (h *AdminShareCodeHandler) 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.ResidenceShareCode
	if err := h.db.Preload("Residence").Preload("CreatedBy").First(&code, id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return c.JSON(http.StatusNotFound, map[string]interface{}{"error": "Share code not found"})
		}
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to fetch share code"})
	}

	var expiresAt *string
	if code.ExpiresAt != nil {
		formatted := code.ExpiresAt.Format("2006-01-02T15:04:05Z")
		expiresAt = &formatted
	}

	response := ShareCodeResponse{
		ID:            code.ID,
		ResidenceID:   code.ResidenceID,
		ResidenceName: code.Residence.Name,
		Code:          code.Code,
		CreatedByID:   code.CreatedByID,
		CreatedBy:     code.CreatedBy.Username,
		IsActive:      code.IsActive,
		ExpiresAt:     expiresAt,
		CreatedAt:     code.CreatedAt.Format("2006-01-02T15:04:05Z"),
	}

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

// Update handles PUT /api/admin/share-codes/:id
func (h *AdminShareCodeHandler) Update(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.ResidenceShareCode
	if err := h.db.First(&code, id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return c.JSON(http.StatusNotFound, map[string]interface{}{"error": "Share code not found"})
		}
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to fetch share code"})
	}

	var req struct {
		IsActive *bool `json:"is_active"`
	}
	if err := c.Bind(&req); err != nil {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Invalid request body"})
	}

	// Only update IsActive when explicitly provided (non-nil).
	// Using *bool prevents a missing field from defaulting to false.
	if req.IsActive != nil {
		code.IsActive = *req.IsActive
	}

	if err := h.db.Save(&code).Error; err != nil {
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to update share code"})
	}

	// Reload with relations
	h.db.Preload("Residence").Preload("CreatedBy").First(&code, id)

	var expiresAt *string
	if code.ExpiresAt != nil {
		formatted := code.ExpiresAt.Format("2006-01-02T15:04:05Z")
		expiresAt = &formatted
	}

	response := ShareCodeResponse{
		ID:            code.ID,
		ResidenceID:   code.ResidenceID,
		ResidenceName: code.Residence.Name,
		Code:          code.Code,
		CreatedByID:   code.CreatedByID,
		CreatedBy:     code.CreatedBy.Username,
		IsActive:      code.IsActive,
		ExpiresAt:     expiresAt,
		CreatedAt:     code.CreatedAt.Format("2006-01-02T15:04:05Z"),
	}

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

// Delete handles DELETE /api/admin/share-codes/:id
func (h *AdminShareCodeHandler) 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.ResidenceShareCode{}, id)
	if result.Error != nil {
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to delete share code"})
	}

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

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

// BulkDelete handles DELETE /api/admin/share-codes/bulk
func (h *AdminShareCodeHandler) 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.ResidenceShareCode{})
	if result.Error != nil {
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to delete share codes"})
	}

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