<?php
/**
 * Notification Service
 * 
 * This service provides methods to create and manage notifications
 */

namespace Services\Notification;

use Services\Core\Database;
use Services\Core\WebSocket\WebSocketClient;

class NotificationService {
    private $db;
    private $webSocketClient;

    public function __construct() {
        require_once __DIR__ . '/../../config/database.php';
        require_once __DIR__ . '/../core/WebSocketClient.php';
        
        $this->db = new Database();
        $this->webSocketClient = new WebSocketClient();
    }

    /**
     * Create a new notification
     *
     * @param array $data Notification data
     * @return array Result with success status and notification ID
     */
    public function createNotification($data) {
        try {
            $conn = $this->db->getConnection();
            
            // Start transaction
            $conn->beginTransaction();
            
            // Insert notification
            $query = "INSERT INTO notifications 
                      (title, message, type, priority, target_audience, created_by, created_at) 
                      VALUES (:title, :message, :type, :priority, :target_audience, :created_by, NOW())";
            
            $stmt = $conn->prepare($query);
            $stmt->bindParam(':title', $data['title']);
            $stmt->bindParam(':message', $data['message']);
            $stmt->bindParam(':type', $data['type']);
            $stmt->bindParam(':priority', $data['priority']);
            $stmt->bindParam(':target_audience', $data['target_audience']);
            $stmt->bindParam(':created_by', $data['created_by']);
            $stmt->execute();
            
            $notificationId = $conn->lastInsertId();
            
            // Add recipients based on target audience
            $this->addRecipients($notificationId, $data['target_audience'], $data['specific_recipients'] ?? null);
            
            // Send real-time notification via WebSocket
            $this->sendRealTimeNotification($notificationId);
            
            // Commit transaction
            $conn->commit();
            
            return [
                'success' => true,
                'notification_id' => $notificationId,
                'message' => 'Notification created successfully'
            ];
        } catch (\Exception $e) {
            // Rollback transaction on error
            if (isset($conn)) {
                $conn->rollBack();
            }
            
            return [
                'success' => false,
                'message' => 'Error creating notification: ' . $e->getMessage()
            ];
        }
    }
    
    /**
     * Add recipients to a notification based on target audience
     *
     * @param int $notificationId Notification ID
     * @param string $targetAudience Target audience ('all', 'admins', 'members', 'specific')
     * @param array|null $specificRecipients Array of specific recipient IDs (only used if target_audience is 'specific')
     * @return bool Success status
     */
    private function addRecipients($notificationId, $targetAudience, $specificRecipients = null) {
        try {
            $conn = $this->db->getConnection();
            
            // Prepare base query
            $baseQuery = "INSERT INTO notification_recipients 
                         (notification_id, recipient_id, recipient_type, is_read, created_at) 
                         VALUES (:notification_id, :recipient_id, :recipient_type, 0, NOW())";
            
            $stmt = $conn->prepare($baseQuery);
            
            // Different logic based on target audience
            switch ($targetAudience) {
                case 'all':
                    // Add all admins
                    $this->addAllAdminsAsRecipients($notificationId);
                    
                    // Add all members
                    $this->addAllMembersAsRecipients($notificationId);
                    break;
                    
                case 'admins':
                    // Add all admins
                    $this->addAllAdminsAsRecipients($notificationId);
                    break;
                    
                case 'members':
                    // Add all members
                    $this->addAllMembersAsRecipients($notificationId);
                    break;
                    
                case 'specific':
                    // Add specific recipients
                    if (!empty($specificRecipients)) {
                        foreach ($specificRecipients as $recipient) {
                            // Parse recipient ID and type (format: "type_id", e.g., "admin_1", "member_5")
                            $parts = explode('_', $recipient);
                            if (count($parts) === 2) {
                                $recipientType = $parts[0];
                                $recipientId = $parts[1];
                                
                                // Add recipient
                                $stmt->bindParam(':notification_id', $notificationId);
                                $stmt->bindParam(':recipient_id', $recipientId);
                                $stmt->bindParam(':recipient_type', $recipientType);
                                $stmt->execute();
                            }
                        }
                    }
                    break;
            }
            
            return true;
        } catch (\Exception $e) {
            error_log('Error adding recipients: ' . $e->getMessage());
            return false;
        }
    }
    
    /**
     * Add all admins as recipients
     *
     * @param int $notificationId Notification ID
     * @return bool Success status
     */
    private function addAllAdminsAsRecipients($notificationId) {
        try {
            $conn = $this->db->getConnection();
            
            // Get all admin IDs
            $query = "SELECT id FROM admin_users";
            $stmt = $conn->prepare($query);
            $stmt->execute();
            $admins = $stmt->fetchAll(\PDO::FETCH_ASSOC);
            
            // Prepare recipient insert query
            $insertQuery = "INSERT INTO notification_recipients 
                           (notification_id, recipient_id, recipient_type, is_read, created_at) 
                           VALUES (:notification_id, :recipient_id, 'admin', 0, NOW())";
            
            $insertStmt = $conn->prepare($insertQuery);
            
            // Add each admin as recipient
            foreach ($admins as $admin) {
                $insertStmt->bindParam(':notification_id', $notificationId);
                $insertStmt->bindParam(':recipient_id', $admin['id']);
                $insertStmt->execute();
            }
            
            return true;
        } catch (\Exception $e) {
            error_log('Error adding admin recipients: ' . $e->getMessage());
            return false;
        }
    }
    
    /**
     * Add all members as recipients
     *
     * @param int $notificationId Notification ID
     * @return bool Success status
     */
    private function addAllMembersAsRecipients($notificationId) {
        try {
            $conn = $this->db->getConnection();
            
            // Get all member IDs
            $query = "SELECT id FROM members";
            $stmt = $conn->prepare($query);
            $stmt->execute();
            $members = $stmt->fetchAll(\PDO::FETCH_ASSOC);
            
            // Prepare recipient insert query
            $insertQuery = "INSERT INTO notification_recipients 
                           (notification_id, recipient_id, recipient_type, is_read, created_at) 
                           VALUES (:notification_id, :recipient_id, 'member', 0, NOW())";
            
            $insertStmt = $conn->prepare($insertQuery);
            
            // Add each member as recipient
            foreach ($members as $member) {
                $insertStmt->bindParam(':notification_id', $notificationId);
                $insertStmt->bindParam(':recipient_id', $member['id']);
                $insertStmt->execute();
            }
            
            return true;
        } catch (\Exception $e) {
            error_log('Error adding member recipients: ' . $e->getMessage());
            return false;
        }
    }
    
    /**
     * Send real-time notification via WebSocket
     *
     * @param int $notificationId Notification ID
     * @return bool Success status
     */
    private function sendRealTimeNotification($notificationId) {
        try {
            $conn = $this->db->getConnection();
            
            // Get notification details
            $query = "SELECT n.*, 
                            a.username as created_by_name
                     FROM notifications n
                     LEFT JOIN admin_users a ON n.created_by = a.id
                     WHERE n.id = :notification_id";
            
            $stmt = $conn->prepare($query);
            $stmt->bindParam(':notification_id', $notificationId);
            $stmt->execute();
            $notification = $stmt->fetch(\PDO::FETCH_ASSOC);
            
            if (!$notification) {
                return false;
            }
            
            // Get recipients
            $recipientQuery = "SELECT recipient_id, recipient_type 
                              FROM notification_recipients 
                              WHERE notification_id = :notification_id";
            
            $recipientStmt = $conn->prepare($recipientQuery);
            $recipientStmt->bindParam(':notification_id', $notificationId);
            $recipientStmt->execute();
            $recipients = $recipientStmt->fetchAll(\PDO::FETCH_ASSOC);
            
            // Prepare notification data for WebSocket
            $notificationData = [
                'type' => 'notification',
                'data' => $notification,
                'recipients' => $recipients
            ];
            
            // Send via WebSocket client
            $this->webSocketClient->send(json_encode($notificationData));
            
            return true;
        } catch (\Exception $e) {
            error_log('Error sending real-time notification: ' . $e->getMessage());
            return false;
        }
    }
    
    /**
     * Get notifications for a specific user
     *
     * @param int $userId User ID
     * @param string $userType User type ('admin' or 'member')
     * @param array $filters Optional filters for notifications
     * @param int $limit Limit the number of results
     * @param int $offset Offset for pagination
     * @return array Notifications with count
     */
    public function getNotifications($userId, $userType, $filters = [], $limit = 10, $offset = 0) {
        try {
            $conn = $this->db->getConnection();
            
            // Build the query with joins
            $query = "SELECT n.*, 
                            nr.is_read,
                            a.username as created_by_name
                     FROM notifications n
                     INNER JOIN notification_recipients nr ON n.id = nr.notification_id
                     LEFT JOIN admin_users a ON n.created_by = a.id
                     WHERE nr.recipient_id = :recipient_id 
                     AND nr.recipient_type = :recipient_type";
            
            $countQuery = "SELECT COUNT(*) as total
                          FROM notifications n
                          INNER JOIN notification_recipients nr ON n.id = nr.notification_id
                          WHERE nr.recipient_id = :recipient_id 
                          AND nr.recipient_type = :recipient_type";
            
            $params = [
                ':recipient_id' => $userId,
                ':recipient_type' => $userType
            ];
            
            // Add filters if provided
            if (!empty($filters)) {
                if (isset($filters['type']) && !empty($filters['type'])) {
                    $query .= " AND n.type = :type";
                    $countQuery .= " AND n.type = :type";
                    $params[':type'] = $filters['type'];
                }
                
                if (isset($filters['priority']) && !empty($filters['priority'])) {
                    $query .= " AND n.priority = :priority";
                    $countQuery .= " AND n.priority = :priority";
                    $params[':priority'] = $filters['priority'];
                }
                
                if (isset($filters['is_read']) && $filters['is_read'] !== null) {
                    $query .= " AND nr.is_read = :is_read";
                    $countQuery .= " AND nr.is_read = :is_read";
                    $params[':is_read'] = $filters['is_read'] ? 1 : 0;
                }
            }
            
            // Add order and limit
            $query .= " ORDER BY n.created_at DESC LIMIT :limit OFFSET :offset";
            
            // Prepare and execute count query
            $countStmt = $conn->prepare($countQuery);
            foreach ($params as $param => $value) {
                $countStmt->bindValue($param, $value);
            }
            $countStmt->execute();
            $totalCount = $countStmt->fetch(\PDO::FETCH_ASSOC)['total'];
            
            // Prepare and execute main query
            $stmt = $conn->prepare($query);
            foreach ($params as $param => $value) {
                $stmt->bindValue($param, $value);
            }
            $stmt->bindValue(':limit', $limit, \PDO::PARAM_INT);
            $stmt->bindValue(':offset', $offset, \PDO::PARAM_INT);
            $stmt->execute();
            $notifications = $stmt->fetchAll(\PDO::FETCH_ASSOC);
            
            return [
                'success' => true,
                'data' => [
                    'notifications' => $notifications,
                    'total' => $totalCount
                ]
            ];
        } catch (\Exception $e) {
            return [
                'success' => false,
                'message' => 'Error retrieving notifications: ' . $e->getMessage()
            ];
        }
    }
    
    /**
     * Mark a notification as read
     *
     * @param int $notificationId Notification ID
     * @param int $userId User ID
     * @param string $userType User type ('admin' or 'member')
     * @return array Result with success status
     */
    public function markAsRead($notificationId, $userId, $userType) {
        try {
            $conn = $this->db->getConnection();
            
            $query = "UPDATE notification_recipients
                     SET is_read = 1
                     WHERE notification_id = :notification_id
                     AND recipient_id = :recipient_id
                     AND recipient_type = :recipient_type";
            
            $stmt = $conn->prepare($query);
            $stmt->bindParam(':notification_id', $notificationId);
            $stmt->bindParam(':recipient_id', $userId);
            $stmt->bindParam(':recipient_type', $userType);
            $stmt->execute();
            
            return [
                'success' => true,
                'message' => 'Notification marked as read'
            ];
        } catch (\Exception $e) {
            return [
                'success' => false,
                'message' => 'Error marking notification as read: ' . $e->getMessage()
            ];
        }
    }
    
    /**
     * Get notification settings for a user
     *
     * @param int $userId User ID
     * @param string $userType User type ('admin' or 'member')
     * @return array User notification settings
     */
    public function getSettings($userId, $userType) {
        try {
            $conn = $this->db->getConnection();
            
            $query = "SELECT * FROM notification_settings
                     WHERE user_id = :user_id AND user_type = :user_type";
            
            $stmt = $conn->prepare($query);
            $stmt->bindParam(':user_id', $userId);
            $stmt->bindParam(':user_type', $userType);
            $stmt->execute();
            
            $settings = $stmt->fetch(\PDO::FETCH_ASSOC);
            
            // If no settings found, create default settings
            if (!$settings) {
                $settings = $this->createDefaultSettings($userId, $userType);
            }
            
            return [
                'success' => true,
                'data' => $settings
            ];
        } catch (\Exception $e) {
            return [
                'success' => false,
                'message' => 'Error retrieving notification settings: ' . $e->getMessage()
            ];
        }
    }
    
    /**
     * Create default notification settings for a user
     *
     * @param int $userId User ID
     * @param string $userType User type ('admin' or 'member')
     * @return array Default settings
     */
    private function createDefaultSettings($userId, $userType) {
        try {
            $conn = $this->db->getConnection();
            
            // Default notification types
            $defaultNotificationTypes = json_encode([
                'info', 'success', 'warning', 'error', 
                'announcement', 'event', 'payment', 'membership'
            ]);
            
            $query = "INSERT INTO notification_settings
                     (user_id, user_type, browser_notifications, sound_notifications, 
                      email_notifications, notification_types, created_at)
                     VALUES (:user_id, :user_type, 1, 1, 0, :notification_types, NOW())";
            
            $stmt = $conn->prepare($query);
            $stmt->bindParam(':user_id', $userId);
            $stmt->bindParam(':user_type', $userType);
            $stmt->bindParam(':notification_types', $defaultNotificationTypes);
            $stmt->execute();
            
            return [
                'id' => $conn->lastInsertId(),
                'user_id' => $userId,
                'user_type' => $userType,
                'browser_notifications' => 1,
                'sound_notifications' => 1,
                'email_notifications' => 0,
                'notification_types' => $defaultNotificationTypes
            ];
        } catch (\Exception $e) {
            error_log('Error creating default settings: ' . $e->getMessage());
            
            // Return fallback default settings
            return [
                'id' => null,
                'user_id' => $userId,
                'user_type' => $userType,
                'browser_notifications' => 1,
                'sound_notifications' => 1,
                'email_notifications' => 0,
                'notification_types' => json_encode([
                    'info', 'success', 'warning', 'error', 
                    'announcement', 'event', 'payment', 'membership'
                ])
            ];
        }
    }
    
    /**
     * Update notification settings for a user
     *
     * @param int $userId User ID
     * @param string $userType User type ('admin' or 'member')
     * @param array $settings New settings
     * @return array Result with success status
     */
    public function updateSettings($userId, $userType, $settings) {
        try {
            $conn = $this->db->getConnection();
            
            // Check if settings exist
            $checkQuery = "SELECT id FROM notification_settings
                          WHERE user_id = :user_id AND user_type = :user_type";
            
            $checkStmt = $conn->prepare($checkQuery);
            $checkStmt->bindParam(':user_id', $userId);
            $checkStmt->bindParam(':user_type', $userType);
            $checkStmt->execute();
            
            $existingSettings = $checkStmt->fetch(\PDO::FETCH_ASSOC);
            
            // Prepare notification types
            $notificationTypes = json_encode($settings['notification_types'] ?? []);
            
            if ($existingSettings) {
                // Update existing settings
                $query = "UPDATE notification_settings
                         SET browser_notifications = :browser_notifications,
                             sound_notifications = :sound_notifications,
                             email_notifications = :email_notifications,
                             notification_types = :notification_types,
                             updated_at = NOW()
                         WHERE user_id = :user_id AND user_type = :user_type";
            } else {
                // Create new settings
                $query = "INSERT INTO notification_settings
                         (user_id, user_type, browser_notifications, sound_notifications, 
                          email_notifications, notification_types, created_at)
                         VALUES (:user_id, :user_type, :browser_notifications, :sound_notifications, 
                                 :email_notifications, :notification_types, NOW())";
            }
            
            $stmt = $conn->prepare($query);
            $stmt->bindParam(':user_id', $userId);
            $stmt->bindParam(':user_type', $userType);
            $stmt->bindParam(':browser_notifications', $settings['browser_notifications']);
            $stmt->bindParam(':sound_notifications', $settings['sound_notifications']);
            $stmt->bindParam(':email_notifications', $settings['email_notifications']);
            $stmt->bindParam(':notification_types', $notificationTypes);
            $stmt->execute();
            
            return [
                'success' => true,
                'message' => 'Notification settings updated successfully'
            ];
        } catch (\Exception $e) {
            return [
                'success' => false,
                'message' => 'Error updating notification settings: ' . $e->getMessage()
            ];
        }
    }
    
    /**
     * Delete a notification
     *
     * @param int $notificationId Notification ID
     * @param int $userId User ID (for permission check)
     * @return array Result with success status
     */
    public function deleteNotification($notificationId, $userId) {
        try {
            $conn = $this->db->getConnection();
            
            // Start transaction
            $conn->beginTransaction();
            
            // Check if the user is allowed to delete this notification
            $checkQuery = "SELECT created_by FROM notifications WHERE id = :notification_id";
            $checkStmt = $conn->prepare($checkQuery);
            $checkStmt->bindParam(':notification_id', $notificationId);
            $checkStmt->execute();
            
            $notification = $checkStmt->fetch(\PDO::FETCH_ASSOC);
            
            // Only allow the creator or an admin to delete
            if (!$notification || ($notification['created_by'] != $userId && !$this->isUserAdmin($userId))) {
                return [
                    'success' => false,
                    'message' => 'You do not have permission to delete this notification'
                ];
            }
            
            // Delete recipients first (foreign key constraint)
            $deleteRecipientsQuery = "DELETE FROM notification_recipients 
                                     WHERE notification_id = :notification_id";
            
            $deleteRecipientsStmt = $conn->prepare($deleteRecipientsQuery);
            $deleteRecipientsStmt->bindParam(':notification_id', $notificationId);
            $deleteRecipientsStmt->execute();
            
            // Delete notification
            $deleteNotificationQuery = "DELETE FROM notifications WHERE id = :notification_id";
            $deleteNotificationStmt = $conn->prepare($deleteNotificationQuery);
            $deleteNotificationStmt->bindParam(':notification_id', $notificationId);
            $deleteNotificationStmt->execute();
            
            // Commit transaction
            $conn->commit();
            
            return [
                'success' => true,
                'message' => 'Notification deleted successfully'
            ];
        } catch (\Exception $e) {
            // Rollback transaction on error
            if (isset($conn)) {
                $conn->rollBack();
            }
            
            return [
                'success' => false,
                'message' => 'Error deleting notification: ' . $e->getMessage()
            ];
        }
    }
    
    /**
     * Check if a user is an admin
     *
     * @param int $userId User ID
     * @return bool True if user is admin, false otherwise
     */
    private function isUserAdmin($userId) {
        try {
            $conn = $this->db->getConnection();
            
            $query = "SELECT id FROM admin_users WHERE id = :user_id";
            $stmt = $conn->prepare($query);
            $stmt->bindParam(':user_id', $userId);
            $stmt->execute();
            
            return $stmt->rowCount() > 0;
        } catch (\Exception $e) {
            error_log('Error checking if user is admin: ' . $e->getMessage());
            return false;
        }
    }
}
