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

// AdminDeviceHandler handles admin device management endpoints
type AdminDeviceHandler struct {
	db *gorm.DB
}

// NewAdminDeviceHandler creates a new admin device handler
func NewAdminDeviceHandler(db *gorm.DB) *AdminDeviceHandler {
	return &AdminDeviceHandler{db: db}
}

// APNSDeviceResponse represents an iOS device in API responses
type APNSDeviceResponse struct {
	ID             uint    `json:"id"`
	Name           string  `json:"name"`
	Active         bool    `json:"active"`
	UserID         *uint   `json:"user_id"`
	Username       *string `json:"username"`
	DeviceID       string  `json:"device_id"`
	RegistrationID string  `json:"registration_id"`
	DateCreated    string  `json:"date_created"`
}

// GCMDeviceResponse represents an Android device in API responses
type GCMDeviceResponse struct {
	ID               uint    `json:"id"`
	Name             string  `json:"name"`
	Active           bool    `json:"active"`
	UserID           *uint   `json:"user_id"`
	Username         *string `json:"username"`
	DeviceID         string  `json:"device_id"`
	RegistrationID   string  `json:"registration_id"`
	CloudMessageType string  `json:"cloud_message_type"`
	DateCreated      string  `json:"date_created"`
}

// ListAPNS handles GET /api/admin/devices/apns
func (h *AdminDeviceHandler) ListAPNS(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 devices []models.APNSDevice
	var total int64

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

	// Apply search
	if filters.Search != "" {
		search := "%" + filters.Search + "%"
		query = query.Joins("LEFT JOIN auth_user ON auth_user.id = push_notifications_apnsdevice.user_id").
			Where(
				"push_notifications_apnsdevice.name ILIKE ? OR push_notifications_apnsdevice.device_id ILIKE ? OR auth_user.username ILIKE ?",
				search, search, search,
			)
	}

	query.Count(&total)

	// Apply sorting (allowlist prevents SQL injection via sort_by parameter)
	sortBy := filters.GetSafeSortBy([]string{
		"id", "name", "active", "user_id", "device_id", "date_created",
	}, "date_created")
	query = query.Order(sortBy + " " + filters.GetSortDir())
	query = query.Offset(filters.GetOffset()).Limit(filters.GetPerPage())

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

	responses := make([]APNSDeviceResponse, len(devices))
	for i, device := range devices {
		var username *string
		if device.User != nil {
			username = &device.User.Username
		}
		responses[i] = APNSDeviceResponse{
			ID:             device.ID,
			Name:           device.Name,
			Active:         device.Active,
			UserID:         device.UserID,
			Username:       username,
			DeviceID:       device.DeviceID,
			RegistrationID: device.RegistrationID, // Full device token
			DateCreated:    device.DateCreated.Format("2006-01-02T15:04:05Z"),
		}
	}

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

// ListGCM handles GET /api/admin/devices/gcm
func (h *AdminDeviceHandler) ListGCM(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 devices []models.GCMDevice
	var total int64

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

	if filters.Search != "" {
		search := "%" + filters.Search + "%"
		query = query.Joins("LEFT JOIN auth_user ON auth_user.id = push_notifications_gcmdevice.user_id").
			Where(
				"push_notifications_gcmdevice.name ILIKE ? OR push_notifications_gcmdevice.device_id ILIKE ? OR auth_user.username ILIKE ?",
				search, search, search,
			)
	}

	query.Count(&total)

	// Apply sorting (allowlist prevents SQL injection via sort_by parameter)
	sortBy := filters.GetSafeSortBy([]string{
		"id", "name", "active", "user_id", "device_id", "cloud_message_type", "date_created",
	}, "date_created")
	query = query.Order(sortBy + " " + filters.GetSortDir())
	query = query.Offset(filters.GetOffset()).Limit(filters.GetPerPage())

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

	responses := make([]GCMDeviceResponse, len(devices))
	for i, device := range devices {
		var username *string
		if device.User != nil {
			username = &device.User.Username
		}
		responses[i] = GCMDeviceResponse{
			ID:               device.ID,
			Name:             device.Name,
			Active:           device.Active,
			UserID:           device.UserID,
			Username:         username,
			DeviceID:         device.DeviceID,
			RegistrationID:   device.RegistrationID, // Full device token
			CloudMessageType: device.CloudMessageType,
			DateCreated:      device.DateCreated.Format("2006-01-02T15:04:05Z"),
		}
	}

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

// UpdateAPNS handles PUT /api/admin/devices/apns/:id
func (h *AdminDeviceHandler) UpdateAPNS(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 device models.APNSDevice
	if err := h.db.First(&device, id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return c.JSON(http.StatusNotFound, map[string]interface{}{"error": "Device not found"})
		}
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to fetch device"})
	}

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

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

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

// UpdateGCM handles PUT /api/admin/devices/gcm/:id
func (h *AdminDeviceHandler) UpdateGCM(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 device models.GCMDevice
	if err := h.db.First(&device, id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return c.JSON(http.StatusNotFound, map[string]interface{}{"error": "Device not found"})
		}
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to fetch device"})
	}

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

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

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

// DeleteAPNS handles DELETE /api/admin/devices/apns/:id
func (h *AdminDeviceHandler) DeleteAPNS(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.APNSDevice{}, id)
	if result.Error != nil {
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to delete device"})
	}

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

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

// DeleteGCM handles DELETE /api/admin/devices/gcm/:id
func (h *AdminDeviceHandler) DeleteGCM(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.GCMDevice{}, id)
	if result.Error != nil {
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to delete device"})
	}

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

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

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

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

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

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

// GetStats handles GET /api/admin/devices/stats
func (h *AdminDeviceHandler) GetStats(c echo.Context) error {
	var apnsTotal, apnsActive, gcmTotal, gcmActive int64

	h.db.Model(&models.APNSDevice{}).Count(&apnsTotal)
	h.db.Model(&models.APNSDevice{}).Where("active = ?", true).Count(&apnsActive)
	h.db.Model(&models.GCMDevice{}).Count(&gcmTotal)
	h.db.Model(&models.GCMDevice{}).Where("active = ?", true).Count(&gcmActive)

	return c.JSON(http.StatusOK, map[string]interface{}{
		"apns": map[string]interface{}{
			"total":  apnsTotal,
			"active": apnsActive,
		},
		"gcm": map[string]interface{}{
			"total":  gcmTotal,
			"active": gcmActive,
		},
		"total": apnsTotal + gcmTotal,
	})
}
