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

// AdminNotificationPrefsHandler handles notification preference management
type AdminNotificationPrefsHandler struct {
	db *gorm.DB
}

// NewAdminNotificationPrefsHandler creates a new handler
func NewAdminNotificationPrefsHandler(db *gorm.DB) *AdminNotificationPrefsHandler {
	return &AdminNotificationPrefsHandler{db: db}
}

// NotificationPrefResponse represents a notification preference in the admin API
type NotificationPrefResponse struct {
	ID               uint   `json:"id"`
	UserID           uint   `json:"user_id"`
	Username         string `json:"username"`
	Email            string `json:"email"`
	TaskDueSoon      bool   `json:"task_due_soon"`
	TaskOverdue      bool   `json:"task_overdue"`
	TaskCompleted    bool   `json:"task_completed"`
	TaskAssigned     bool   `json:"task_assigned"`
	ResidenceShared  bool   `json:"residence_shared"`
	WarrantyExpiring bool   `json:"warranty_expiring"`
	DailyDigest      bool   `json:"daily_digest"`

	// Email preferences
	EmailTaskCompleted bool `json:"email_task_completed"`

	// Custom notification times (UTC hour 0-23, null means use system default)
	TaskDueSoonHour      *int `json:"task_due_soon_hour"`
	TaskOverdueHour      *int `json:"task_overdue_hour"`
	WarrantyExpiringHour *int `json:"warranty_expiring_hour"`
	DailyDigestHour      *int `json:"daily_digest_hour"`

	// User timezone (IANA name, auto-captured from X-Timezone header)
	Timezone *string `json:"timezone"`

	CreatedAt string `json:"created_at"`
	UpdatedAt string `json:"updated_at"`
}

// List handles GET /api/admin/notification-prefs
func (h *AdminNotificationPrefsHandler) 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 prefs []models.NotificationPreference
	var total int64

	query := h.db.Model(&models.NotificationPreference{})

	// Apply search on user data via subquery
	if filters.Search != "" {
		search := "%" + filters.Search + "%"
		query = query.Where("user_id IN (?)",
			h.db.Model(&models.User{}).Select("id").Where(
				"username ILIKE ? OR email ILIKE ?", 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", "updated_at",
	}, "created_at")
	query = query.Order(sortBy + " " + filters.GetSortDir())

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

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

	// Get user info for each preference
	userIDs := make([]uint, len(prefs))
	for i, p := range prefs {
		userIDs[i] = p.UserID
	}

	var users []models.User
	userMap := make(map[uint]models.User)
	if len(userIDs) > 0 {
		h.db.Where("id IN ?", userIDs).Find(&users)
		for _, u := range users {
			userMap[u.ID] = u
		}
	}

	// Build response
	responses := make([]NotificationPrefResponse, len(prefs))
	for i, pref := range prefs {
		user := userMap[pref.UserID]
		responses[i] = NotificationPrefResponse{
			ID:                   pref.ID,
			UserID:               pref.UserID,
			Username:             user.Username,
			Email:                user.Email,
			TaskDueSoon:          pref.TaskDueSoon,
			TaskOverdue:          pref.TaskOverdue,
			TaskCompleted:        pref.TaskCompleted,
			TaskAssigned:         pref.TaskAssigned,
			ResidenceShared:      pref.ResidenceShared,
			WarrantyExpiring:     pref.WarrantyExpiring,
			DailyDigest:          pref.DailyDigest,
			EmailTaskCompleted:   pref.EmailTaskCompleted,
			TaskDueSoonHour:      pref.TaskDueSoonHour,
			TaskOverdueHour:      pref.TaskOverdueHour,
			WarrantyExpiringHour: pref.WarrantyExpiringHour,
			DailyDigestHour:      pref.DailyDigestHour,
			Timezone:             pref.Timezone,
			CreatedAt:            pref.CreatedAt.Format("2006-01-02T15:04:05Z"),
			UpdatedAt:            pref.UpdatedAt.Format("2006-01-02T15:04:05Z"),
		}
	}

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

// Get handles GET /api/admin/notification-prefs/:id
func (h *AdminNotificationPrefsHandler) 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 pref models.NotificationPreference
	if err := h.db.First(&pref, id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return c.JSON(http.StatusNotFound, map[string]interface{}{"error": "Notification preference not found"})
		}
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to fetch notification preference"})
	}

	var user models.User
	h.db.First(&user, pref.UserID)

	return c.JSON(http.StatusOK, NotificationPrefResponse{
		ID:                   pref.ID,
		UserID:               pref.UserID,
		Username:             user.Username,
		Email:                user.Email,
		TaskDueSoon:          pref.TaskDueSoon,
		TaskOverdue:          pref.TaskOverdue,
		TaskCompleted:        pref.TaskCompleted,
		TaskAssigned:         pref.TaskAssigned,
		ResidenceShared:      pref.ResidenceShared,
		WarrantyExpiring:     pref.WarrantyExpiring,
		DailyDigest:          pref.DailyDigest,
		EmailTaskCompleted:   pref.EmailTaskCompleted,
		TaskDueSoonHour:      pref.TaskDueSoonHour,
		TaskOverdueHour:      pref.TaskOverdueHour,
		WarrantyExpiringHour: pref.WarrantyExpiringHour,
		DailyDigestHour:      pref.DailyDigestHour,
		Timezone:             pref.Timezone,
		CreatedAt:            pref.CreatedAt.Format("2006-01-02T15:04:05Z"),
		UpdatedAt:            pref.UpdatedAt.Format("2006-01-02T15:04:05Z"),
	})
}

// UpdateNotificationPrefRequest represents an update request
type UpdateNotificationPrefRequest struct {
	TaskDueSoon      *bool `json:"task_due_soon"`
	TaskOverdue      *bool `json:"task_overdue"`
	TaskCompleted    *bool `json:"task_completed"`
	TaskAssigned     *bool `json:"task_assigned"`
	ResidenceShared  *bool `json:"residence_shared"`
	WarrantyExpiring *bool `json:"warranty_expiring"`
	DailyDigest      *bool `json:"daily_digest"`

	// Email preferences
	EmailTaskCompleted *bool `json:"email_task_completed"`

	// Custom notification times (UTC hour 0-23)
	TaskDueSoonHour      *int `json:"task_due_soon_hour"`
	TaskOverdueHour      *int `json:"task_overdue_hour"`
	WarrantyExpiringHour *int `json:"warranty_expiring_hour"`
	DailyDigestHour      *int `json:"daily_digest_hour"`
}

// Update handles PUT /api/admin/notification-prefs/:id
func (h *AdminNotificationPrefsHandler) 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 pref models.NotificationPreference
	if err := h.db.First(&pref, id).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return c.JSON(http.StatusNotFound, map[string]interface{}{"error": "Notification preference not found"})
		}
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to fetch notification preference"})
	}

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

	// Apply updates
	if req.TaskDueSoon != nil {
		pref.TaskDueSoon = *req.TaskDueSoon
	}
	if req.TaskOverdue != nil {
		pref.TaskOverdue = *req.TaskOverdue
	}
	if req.TaskCompleted != nil {
		pref.TaskCompleted = *req.TaskCompleted
	}
	if req.TaskAssigned != nil {
		pref.TaskAssigned = *req.TaskAssigned
	}
	if req.ResidenceShared != nil {
		pref.ResidenceShared = *req.ResidenceShared
	}
	if req.WarrantyExpiring != nil {
		pref.WarrantyExpiring = *req.WarrantyExpiring
	}
	if req.DailyDigest != nil {
		pref.DailyDigest = *req.DailyDigest
	}
	if req.EmailTaskCompleted != nil {
		pref.EmailTaskCompleted = *req.EmailTaskCompleted
	}

	// Apply notification time updates
	if req.TaskDueSoonHour != nil {
		pref.TaskDueSoonHour = req.TaskDueSoonHour
	}
	if req.TaskOverdueHour != nil {
		pref.TaskOverdueHour = req.TaskOverdueHour
	}
	if req.WarrantyExpiringHour != nil {
		pref.WarrantyExpiringHour = req.WarrantyExpiringHour
	}
	if req.DailyDigestHour != nil {
		pref.DailyDigestHour = req.DailyDigestHour
	}

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

	var user models.User
	h.db.First(&user, pref.UserID)

	return c.JSON(http.StatusOK, NotificationPrefResponse{
		ID:                   pref.ID,
		UserID:               pref.UserID,
		Username:             user.Username,
		Email:                user.Email,
		TaskDueSoon:          pref.TaskDueSoon,
		TaskOverdue:          pref.TaskOverdue,
		TaskCompleted:        pref.TaskCompleted,
		TaskAssigned:         pref.TaskAssigned,
		ResidenceShared:      pref.ResidenceShared,
		WarrantyExpiring:     pref.WarrantyExpiring,
		DailyDigest:          pref.DailyDigest,
		EmailTaskCompleted:   pref.EmailTaskCompleted,
		TaskDueSoonHour:      pref.TaskDueSoonHour,
		TaskOverdueHour:      pref.TaskOverdueHour,
		WarrantyExpiringHour: pref.WarrantyExpiringHour,
		DailyDigestHour:      pref.DailyDigestHour,
		Timezone:             pref.Timezone,
		CreatedAt:            pref.CreatedAt.Format("2006-01-02T15:04:05Z"),
		UpdatedAt:            pref.UpdatedAt.Format("2006-01-02T15:04:05Z"),
	})
}

// Delete handles DELETE /api/admin/notification-prefs/:id
func (h *AdminNotificationPrefsHandler) 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.NotificationPreference{}, id)
	if result.Error != nil {
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to delete notification preference"})
	}
	if result.RowsAffected == 0 {
		return c.JSON(http.StatusNotFound, map[string]interface{}{"error": "Notification preference not found"})
	}

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

// GetByUser handles GET /api/admin/notification-prefs/user/:user_id
func (h *AdminNotificationPrefsHandler) GetByUser(c echo.Context) error {
	userID, err := strconv.ParseUint(c.Param("user_id"), 10, 32)
	if err != nil {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Invalid user ID"})
	}

	var pref models.NotificationPreference
	if err := h.db.Where("user_id = ?", userID).First(&pref).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return c.JSON(http.StatusNotFound, map[string]interface{}{"error": "Notification preference not found for this user"})
		}
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to fetch notification preference"})
	}

	var user models.User
	h.db.First(&user, pref.UserID)

	return c.JSON(http.StatusOK, NotificationPrefResponse{
		ID:                   pref.ID,
		UserID:               pref.UserID,
		Username:             user.Username,
		Email:                user.Email,
		TaskDueSoon:          pref.TaskDueSoon,
		TaskOverdue:          pref.TaskOverdue,
		TaskCompleted:        pref.TaskCompleted,
		TaskAssigned:         pref.TaskAssigned,
		ResidenceShared:      pref.ResidenceShared,
		WarrantyExpiring:     pref.WarrantyExpiring,
		DailyDigest:          pref.DailyDigest,
		EmailTaskCompleted:   pref.EmailTaskCompleted,
		TaskDueSoonHour:      pref.TaskDueSoonHour,
		TaskOverdueHour:      pref.TaskOverdueHour,
		WarrantyExpiringHour: pref.WarrantyExpiringHour,
		DailyDigestHour:      pref.DailyDigestHour,
		Timezone:             pref.Timezone,
		CreatedAt:            pref.CreatedAt.Format("2006-01-02T15:04:05Z"),
		UpdatedAt:            pref.UpdatedAt.Format("2006-01-02T15:04:05Z"),
	})
}
