package handlers

import (
	"context"
	"html"
	"net/http"
	"strconv"
	"time"

	"github.com/labstack/echo/v4"
	"github.com/rs/zerolog/log"
	"gorm.io/gorm"

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

// AdminNotificationHandler handles admin notification management endpoints
type AdminNotificationHandler struct {
	db           *gorm.DB
	emailService *services.EmailService
	pushClient   *push.Client
}

// NewAdminNotificationHandler creates a new admin notification handler
func NewAdminNotificationHandler(db *gorm.DB, emailService *services.EmailService, pushClient *push.Client) *AdminNotificationHandler {
	return &AdminNotificationHandler{
		db:           db,
		emailService: emailService,
		pushClient:   pushClient,
	}
}

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

	var notifications []models.Notification
	var total int64

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

	// Apply search
	if filters.Search != "" {
		search := "%" + filters.Search + "%"
		query = query.Where("title ILIKE ? OR body ILIKE ?", search, search)
	}

	// Apply filters
	if filters.UserID != nil {
		query = query.Where("user_id = ?", *filters.UserID)
	}
	if filters.NotificationType != nil {
		query = query.Where("notification_type = ?", *filters.NotificationType)
	}
	if filters.Sent != nil {
		query = query.Where("sent = ?", *filters.Sent)
	}
	if filters.Read != nil {
		query = query.Where("read = ?", *filters.Read)
	}

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

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

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

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

	// Build response
	responses := make([]dto.NotificationResponse, len(notifications))
	for i, notif := range notifications {
		responses[i] = h.toNotificationResponse(&notif)
	}

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

// Get handles GET /api/admin/notifications/:id
func (h *AdminNotificationHandler) 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 notification ID"})
	}

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

	return c.JSON(http.StatusOK, h.toNotificationDetailResponse(&notification))
}

// Delete handles DELETE /api/admin/notifications/:id
func (h *AdminNotificationHandler) 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 notification ID"})
	}

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

	// Hard delete notifications
	if err := h.db.Delete(&notification).Error; err != nil {
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to delete notification"})
	}

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

// Update handles PUT /api/admin/notifications/:id
func (h *AdminNotificationHandler) 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 notification ID"})
	}

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

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

	now := time.Now().UTC()
	if req.Title != nil {
		notification.Title = *req.Title
	}
	if req.Body != nil {
		notification.Body = *req.Body
	}
	if req.Read != nil {
		notification.Read = *req.Read
		if *req.Read && notification.ReadAt == nil {
			notification.ReadAt = &now
		} else if !*req.Read {
			notification.ReadAt = nil
		}
	}

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

	h.db.Preload("User").First(&notification, id)
	return c.JSON(http.StatusOK, h.toNotificationResponse(&notification))
}

// GetStats handles GET /api/admin/notifications/stats
func (h *AdminNotificationHandler) GetStats(c echo.Context) error {
	var total, sent, read, pending int64

	h.db.Model(&models.Notification{}).Count(&total)
	h.db.Model(&models.Notification{}).Where("sent = ?", true).Count(&sent)
	h.db.Model(&models.Notification{}).Where("read = ?", true).Count(&read)
	h.db.Model(&models.Notification{}).Where("sent = ?", false).Count(&pending)

	return c.JSON(http.StatusOK, map[string]interface{}{
		"total":   total,
		"sent":    sent,
		"read":    read,
		"pending": pending,
	})
}

func (h *AdminNotificationHandler) toNotificationResponse(notif *models.Notification) dto.NotificationResponse {
	response := dto.NotificationResponse{
		ID:               notif.ID,
		UserID:           notif.UserID,
		NotificationType: string(notif.NotificationType),
		Title:            notif.Title,
		Body:             notif.Body,
		Sent:             notif.Sent,
		Read:             notif.Read,
		CreatedAt:        notif.CreatedAt.Format("2006-01-02T15:04:05Z"),
	}

	if notif.User.ID != 0 {
		response.UserEmail = notif.User.Email
		response.Username = notif.User.Username
	}
	if notif.SentAt != nil {
		sentAt := notif.SentAt.Format("2006-01-02T15:04:05Z")
		response.SentAt = &sentAt
	}
	if notif.ReadAt != nil {
		readAt := notif.ReadAt.Format("2006-01-02T15:04:05Z")
		response.ReadAt = &readAt
	}

	return response
}

func (h *AdminNotificationHandler) toNotificationDetailResponse(notif *models.Notification) dto.NotificationDetailResponse {
	return dto.NotificationDetailResponse{
		NotificationResponse: h.toNotificationResponse(notif),
		Data:                 notif.Data,
		TaskID:               notif.TaskID,
	}
}

// SendTestNotification handles POST /api/admin/notifications/send-test
func (h *AdminNotificationHandler) SendTestNotification(c echo.Context) error {
	var req dto.SendTestNotificationRequest
	if err := c.Bind(&req); err != nil {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Invalid request body"})
	}

	// Verify user exists
	var user models.User
	if err := h.db.First(&user, req.UserID).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return c.JSON(http.StatusNotFound, map[string]interface{}{"error": "User not found"})
		}
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to fetch user"})
	}

	// Get user's device tokens
	var iosDevices []models.APNSDevice
	var androidDevices []models.GCMDevice

	h.db.Where("user_id = ? AND active = ?", req.UserID, true).Find(&iosDevices)
	h.db.Where("user_id = ? AND active = ?", req.UserID, true).Find(&androidDevices)

	if len(iosDevices) == 0 && len(androidDevices) == 0 {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "User has no registered devices"})
	}

	// Create notification record
	now := time.Now().UTC()
	notification := models.Notification{
		UserID:           req.UserID,
		NotificationType: models.NotificationType("test"),
		Title:            req.Title,
		Body:             req.Body,
		Data:             `{"test": true}`,
		Sent:             false,
	}

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

	// Collect tokens
	var iosTokens, androidTokens []string
	for _, d := range iosDevices {
		iosTokens = append(iosTokens, d.RegistrationID)
	}
	for _, d := range androidDevices {
		androidTokens = append(androidTokens, d.RegistrationID)
	}

	// Send push notification
	if h.pushClient != nil {
		ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
		defer cancel()

		pushData := map[string]string{
			"notification_id": strconv.FormatUint(uint64(notification.ID), 10),
			"test":            "true",
		}

		err := h.pushClient.SendToAll(ctx, iosTokens, androidTokens, req.Title, req.Body, pushData)
		if err != nil {
			// Log the real error for debugging
			log.Error().Err(err).Uint("notification_id", notification.ID).Msg("Failed to send push notification")
			return c.JSON(http.StatusInternalServerError, map[string]interface{}{
				"error": "Failed to send push notification",
			})
		}
	} else {
		return c.JSON(http.StatusServiceUnavailable, map[string]interface{}{"error": "Push notification service not configured"})
	}

	// Mark as sent
	h.db.Model(&notification).Updates(map[string]interface{}{
		"sent":    true,
		"sent_at": now,
	})

	return c.JSON(http.StatusOK, map[string]interface{}{
		"message":         "Test notification sent successfully",
		"notification_id": notification.ID,
		"devices": map[string]interface{}{
			"ios":     len(iosTokens),
			"android": len(androidTokens),
		},
	})
}

// SendTestEmail handles POST /api/admin/emails/send-test
func (h *AdminNotificationHandler) SendTestEmail(c echo.Context) error {
	var req dto.SendTestEmailRequest
	if err := c.Bind(&req); err != nil {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "Invalid request body"})
	}

	// Verify user exists
	var user models.User
	if err := h.db.First(&user, req.UserID).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return c.JSON(http.StatusNotFound, map[string]interface{}{"error": "User not found"})
		}
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to fetch user"})
	}

	if user.Email == "" {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "User has no email address"})
	}

	// Send email
	if h.emailService == nil {
		return c.JSON(http.StatusServiceUnavailable, map[string]interface{}{"error": "Email service not configured"})
	}

	// HTML-escape user-supplied values to prevent XSS via email content
	escapedSubject := html.EscapeString(req.Subject)
	escapedBody := html.EscapeString(req.Body)

	// Create HTML body with basic styling
	htmlBody := `<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>` + escapedSubject + `</title>
</head>
<body style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto; padding: 20px;">
    <h2 style="color: #333;">` + escapedSubject + `</h2>
    <div style="color: #666; line-height: 1.6;">` + escapedBody + `</div>
    <hr style="border: none; border-top: 1px solid #eee; margin: 30px 0;">
    <p style="color: #999; font-size: 12px;">This is a test email sent from honeyDue Admin Panel.</p>
</body>
</html>`

	err := h.emailService.SendEmail(user.Email, req.Subject, htmlBody, req.Body)
	if err != nil {
		log.Error().Err(err).Str("email", user.Email).Msg("Failed to send test email")
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{
			"error": "Failed to send email",
		})
	}

	return c.JSON(http.StatusOK, map[string]interface{}{
		"message": "Test email sent successfully",
		"to":      user.Email,
	})
}

// SendPostVerificationEmail handles POST /api/admin/emails/send-post-verification
func (h *AdminNotificationHandler) SendPostVerificationEmail(c echo.Context) error {
	var req struct {
		UserID uint `json:"user_id" validate:"required"`
	}
	if err := c.Bind(&req); err != nil {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "user_id is required"})
	}
	if err := c.Validate(&req); err != nil {
		return err
	}

	// Verify user exists
	var user models.User
	if err := h.db.First(&user, req.UserID).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return c.JSON(http.StatusNotFound, map[string]interface{}{"error": "User not found"})
		}
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": "Failed to fetch user"})
	}

	if user.Email == "" {
		return c.JSON(http.StatusBadRequest, map[string]interface{}{"error": "User has no email address"})
	}

	// Send email
	if h.emailService == nil {
		return c.JSON(http.StatusServiceUnavailable, map[string]interface{}{"error": "Email service not configured"})
	}

	err := h.emailService.SendPostVerificationEmail(user.Email, user.FirstName)
	if err != nil {
		log.Error().Err(err).Str("email", user.Email).Msg("Failed to send post-verification email")
		return c.JSON(http.StatusInternalServerError, map[string]interface{}{
			"error": "Failed to send email",
		})
	}

	return c.JSON(http.StatusOK, map[string]interface{}{
		"message": "Post-verification email sent successfully",
		"to":      user.Email,
	})
}
