package services

import (
	"context"
	"fmt"
	"time"

	"github.com/google/uuid"
	"go.uber.org/zap"
	"gorm.io/gorm"
	"veza-backend-api/internal/models"
)

// EnhancedNotificationService provides enhanced notification management
// BE-SVC-009: Implement notification service (enhanced version)
type EnhancedNotificationService struct {
	db     *gorm.DB
	logger *zap.Logger
}

// NewEnhancedNotificationService creates a new enhanced notification service
func NewEnhancedNotificationService(db *gorm.DB, logger *zap.Logger) *EnhancedNotificationService {
	if logger == nil {
		logger = zap.NewNop()
	}
	return &EnhancedNotificationService{
		db:     db,
		logger: logger,
	}
}

// NotificationParams represents parameters for querying notifications
type NotificationParams struct {
	UserID     uuid.UUID
	Type       string // Filter by notification type (empty = all types)
	UnreadOnly bool   // Only return unread notifications
	Page       int    // Page number (default: 1)
	Limit      int    // Results per page (default: 20, max: 100)
}

// NotificationListResult represents paginated notification results
type NotificationListResult struct {
	Notifications []models.Notification `json:"notifications"`
	Total         int64                 `json:"total"`
	Page          int                   `json:"page"`
	Limit         int                   `json:"limit"`
	UnreadCount   int64                 `json:"unread_count"`
}

// CreateNotification creates a new notification
func (s *EnhancedNotificationService) CreateNotification(
	ctx context.Context,
	userID uuid.UUID,
	notificationType, title, content, link string,
) (*models.Notification, error) {
	notification := &models.Notification{
		UserID:  userID,
		Type:    notificationType,
		Title:   title,
		Content: content,
		Link:    link,
		Read:    false,
	}

	if err := s.db.WithContext(ctx).Create(notification).Error; err != nil {
		s.logger.Error("Failed to create notification",
			zap.Error(err),
			zap.String("user_id", userID.String()),
			zap.String("type", notificationType),
		)
		return nil, fmt.Errorf("failed to create notification: %w", err)
	}

	s.logger.Info("Notification created",
		zap.String("notification_id", notification.ID.String()),
		zap.String("user_id", userID.String()),
		zap.String("type", notificationType),
	)

	return notification, nil
}

// GetNotifications retrieves notifications with pagination and filtering
func (s *EnhancedNotificationService) GetNotifications(
	ctx context.Context,
	params NotificationParams,
) (*NotificationListResult, error) {
	// Validate and set defaults
	if params.Page < 1 {
		params.Page = 1
	}
	if params.Limit < 1 {
		params.Limit = 20
	}
	if params.Limit > 100 {
		params.Limit = 100
	}

	// Build query
	query := s.db.WithContext(ctx).Model(&models.Notification{}).
		Where("user_id = ?", params.UserID)

	// Filter by type
	if params.Type != "" {
		query = query.Where("type = ?", params.Type)
	}

	// Filter by read status
	if params.UnreadOnly {
		query = query.Where("read = ?", false)
	}

	// Get total count
	var total int64
	if err := query.Count(&total).Error; err != nil {
		return nil, fmt.Errorf("failed to count notifications: %w", err)
	}

	// Get unread count
	var unreadCount int64
	unreadQuery := s.db.WithContext(ctx).Model(&models.Notification{}).
		Where("user_id = ? AND read = ?", params.UserID, false)
	if err := unreadQuery.Count(&unreadCount).Error; err != nil {
		s.logger.Warn("Failed to count unread notifications", zap.Error(err))
	}

	// Apply pagination
	offset := (params.Page - 1) * params.Limit
	var notifications []models.Notification
	if err := query.
		Order("created_at DESC").
		Offset(offset).
		Limit(params.Limit).
		Find(&notifications).Error; err != nil {
		return nil, fmt.Errorf("failed to get notifications: %w", err)
	}

	return &NotificationListResult{
		Notifications: notifications,
		Total:         total,
		Page:          params.Page,
		Limit:         params.Limit,
		UnreadCount:   unreadCount,
	}, nil
}

// GetNotificationByID retrieves a specific notification
func (s *EnhancedNotificationService) GetNotificationByID(
	ctx context.Context,
	userID, notificationID uuid.UUID,
) (*models.Notification, error) {
	var notification models.Notification
	if err := s.db.WithContext(ctx).
		Where("id = ? AND user_id = ?", notificationID, userID).
		First(&notification).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, fmt.Errorf("notification not found")
		}
		return nil, fmt.Errorf("failed to get notification: %w", err)
	}

	return &notification, nil
}

// MarkAsRead marks a notification as read
func (s *EnhancedNotificationService) MarkAsRead(
	ctx context.Context,
	userID, notificationID uuid.UUID,
) error {
	now := time.Now()
	result := s.db.WithContext(ctx).Model(&models.Notification{}).
		Where("id = ? AND user_id = ?", notificationID, userID).
		Updates(map[string]interface{}{
			"read":    true,
			"read_at": now,
		})

	if result.Error != nil {
		return fmt.Errorf("failed to mark notification as read: %w", result.Error)
	}

	if result.RowsAffected == 0 {
		return fmt.Errorf("notification not found or already read")
	}

	s.logger.Info("Notification marked as read",
		zap.String("notification_id", notificationID.String()),
		zap.String("user_id", userID.String()),
	)

	return nil
}

// MarkAllAsRead marks all notifications as read for a user
func (s *EnhancedNotificationService) MarkAllAsRead(
	ctx context.Context,
	userID uuid.UUID,
) (int64, error) {
	now := time.Now()
	result := s.db.WithContext(ctx).Model(&models.Notification{}).
		Where("user_id = ? AND read = ?", userID, false).
		Updates(map[string]interface{}{
			"read":    true,
			"read_at": now,
		})

	if result.Error != nil {
		return 0, fmt.Errorf("failed to mark all notifications as read: %w", result.Error)
	}

	count := result.RowsAffected
	s.logger.Info("All notifications marked as read",
		zap.String("user_id", userID.String()),
		zap.Int64("count", count),
	)

	return count, nil
}

// DeleteNotification deletes a notification
func (s *EnhancedNotificationService) DeleteNotification(
	ctx context.Context,
	userID, notificationID uuid.UUID,
) error {
	result := s.db.WithContext(ctx).
		Where("id = ? AND user_id = ?", notificationID, userID).
		Delete(&models.Notification{})

	if result.Error != nil {
		return fmt.Errorf("failed to delete notification: %w", result.Error)
	}

	if result.RowsAffected == 0 {
		return fmt.Errorf("notification not found")
	}

	s.logger.Info("Notification deleted",
		zap.String("notification_id", notificationID.String()),
		zap.String("user_id", userID.String()),
	)

	return nil
}

// DeleteAllRead deletes all read notifications for a user
func (s *EnhancedNotificationService) DeleteAllRead(
	ctx context.Context,
	userID uuid.UUID,
) (int64, error) {
	result := s.db.WithContext(ctx).
		Where("user_id = ? AND read = ?", userID, true).
		Delete(&models.Notification{})

	if result.Error != nil {
		return 0, fmt.Errorf("failed to delete read notifications: %w", result.Error)
	}

	count := result.RowsAffected
	s.logger.Info("All read notifications deleted",
		zap.String("user_id", userID.String()),
		zap.Int64("count", count),
	)

	return count, nil
}

// GetUnreadCount returns the count of unread notifications
func (s *EnhancedNotificationService) GetUnreadCount(
	ctx context.Context,
	userID uuid.UUID,
) (int64, error) {
	var count int64
	if err := s.db.WithContext(ctx).Model(&models.Notification{}).
		Where("user_id = ? AND read = ?", userID, false).
		Count(&count).Error; err != nil {
		return 0, fmt.Errorf("failed to get unread count: %w", err)
	}

	return count, nil
}

// GetNotificationTypes returns distinct notification types for a user
func (s *EnhancedNotificationService) GetNotificationTypes(
	ctx context.Context,
	userID uuid.UUID,
) ([]string, error) {
	var types []string
	if err := s.db.WithContext(ctx).Model(&models.Notification{}).
		Where("user_id = ?", userID).
		Distinct("type").
		Pluck("type", &types).Error; err != nil {
		return nil, fmt.Errorf("failed to get notification types: %w", err)
	}

	return types, nil
}

// CreateNotificationBatch creates multiple notifications at once
func (s *EnhancedNotificationService) CreateNotificationBatch(
	ctx context.Context,
	notifications []models.Notification,
) error {
	if len(notifications) == 0 {
		return nil
	}

	// Set IDs for notifications that don't have one
	for i := range notifications {
		if notifications[i].ID == uuid.Nil {
			notifications[i].ID = uuid.New()
		}
		notifications[i].Read = false
	}

	if err := s.db.WithContext(ctx).Create(&notifications).Error; err != nil {
		s.logger.Error("Failed to create notification batch",
			zap.Error(err),
			zap.Int("count", len(notifications)),
		)
		return fmt.Errorf("failed to create notification batch: %w", err)
	}

	s.logger.Info("Notification batch created",
		zap.Int("count", len(notifications)),
	)

	return nil
}
