<?php
// news.php API
require_once './includes/cors.php';
require_once './includes/session_init.php';
require_once './auth/middleware.php';
require_once './includes/config.php';
require_once './includes/audit_helper.php';

// Set content type first and disable error display
header('Content-Type: application/json; charset=UTF-8');
ini_set('display_errors', 0);
ini_set('log_errors', 1);
error_reporting(E_ALL & ~E_NOTICE);

// Define constants
define('MAX_NEWS_CONTENT_SIZE', 65535);
define('API_RATE_LIMIT_GET', 60);
define('API_RATE_LIMIT_POST', 10);

// Audience types
define('AUDIENCE_ALL', 'all');
define('AUDIENCE_MEMBERS_ONLY', 'members');
define('AUDIENCE_USERS_ONLY', 'users');
define('AUDIENCE_ROLE_BASED', 'role');

function sendJsonResponse($success, $data = null, $error = null, $statusCode = 200) {
    http_response_code($statusCode);
    $response = ['success' => (bool)$success];
    if ($data !== null) $response['data'] = $data;
    if ($error !== null) $response['error'] = $error;
    echo json_encode($response);
    exit;
}

// ✅ Require authenticated session
if (!isset($_SESSION['user_id'])) {
    sendJsonResponse(false, null, 'Unauthorized', 401);
}

$userId = $_SESSION['user_id'];
$method = $_SERVER['REQUEST_METHOD'];

// Check permissions for write operations
$writeMethods = ['POST', 'PUT', 'DELETE', 'PATCH'];
if (in_array($method, $writeMethods) && !hasPermission($userId, 'manage_news', $pdo)) {
    safeAuditLog($pdo, $userId, 'ACCESS_DENIED', 'news_announcements', null, [
        'method' => $method,
        'action' => 'manage_news',
        'reason' => 'Insufficient permissions',
        'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
    ]);
    sendJsonResponse(false, null, 'Access Denied: You are not authorized to manage news', 403);
}

// Helper function to get user's role
function getUserRole($pdo, $userId) {
    try {
        $stmt = $pdo->prepare("
            SELECT u.role_id as id, r.name 
            FROM users u
            LEFT JOIN roles r ON u.role_id = r.id
            WHERE u.id = ?
        ");
        $stmt->execute([$userId]);
        return $stmt->fetch(PDO::FETCH_ASSOC);
    } catch (Exception $e) {
        error_log("Error fetching user role: " . $e->getMessage());
        return null;
    }
}

// Helper function to determine if user is a member
function isMember($pdo, $userId) {
    try {
        $stmt = $pdo->prepare("SELECT member_id FROM users WHERE id = ? AND member_id IS NOT NULL");
        $stmt->execute([$userId]);
        return $stmt->fetchColumn() !== false;
    } catch (Exception $e) {
        error_log("Error checking member status: " . $e->getMessage());
        return false;
    }
}

// Helper function to check if news item is visible to user
function isNewsVisibleToUser($pdo, $userId, $newsItem) {
    // If audience is 'all', always visible
    if ($newsItem['audience_type'] === AUDIENCE_ALL) {
        return true;
    }
    
    // Check if user is a member
    $userIsMember = isMember($pdo, $userId);
    
    // Handle members-only audience
    if ($newsItem['audience_type'] === AUDIENCE_MEMBERS_ONLY) {
        return $userIsMember;
    }
    
    // Handle users-only audience (non-members)
    if ($newsItem['audience_type'] === AUDIENCE_USERS_ONLY) {
        return !$userIsMember;
    }
    
    // Handle role-based audience
    if ($newsItem['audience_type'] === AUDIENCE_ROLE_BASED && !empty($newsItem['target_roles'])) {
        $targetRoles = json_decode($newsItem['target_roles'], true);
        if (empty($targetRoles)) {
            return true; // No specific roles means visible to all
        }
        
        // Get user's single role
        $userRole = getUserRole($pdo, $userId);
        if (!$userRole || !isset($userRole['id'])) {
            return false;
        }
        
        // Check if user's role is in target roles
        return in_array($userRole['id'], $targetRoles);
    }
    
    return false;
}

try {
    // Check database connection
    if (!$pdo) {
        safeAuditLog($pdo, $userId, 'DB_CONNECTION_FAILED', 'news_announcements', null, [
            'method' => $method,
            'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
        ]);
        throw new Exception('Database connection failed');
    }

    switch ($method) {
        // 🟢 GET — fetch news based on user's permissions and audience targeting
        case 'GET':
            // Rate limiting for GET requests
            if (!checkApiRateLimit('news_get_' . $userId, API_RATE_LIMIT_GET)) {
                safeAuditLog($pdo, $userId, 'RATE_LIMIT_EXCEEDED', 'news_announcements', null, [
                    'action' => 'GET',
                    'limit' => API_RATE_LIMIT_GET,
                    'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
                ]);
                sendJsonResponse(false, null, 'Too many requests. Please try again later.', 429);
            }
            
            $isAdmin = hasPermission($userId, 'manage_news', $pdo);
            $userIsMember = isMember($pdo, $userId);
            $userRole = getUserRole($pdo, $userId);
            
            if ($isAdmin && isset($_GET['admin']) && $_GET['admin'] == 'true') {
                // Admin view - get all news with pagination and audience details
                safeAuditLog($pdo, $userId, 'VIEW_ATTEMPT', 'news_announcements', null, [
                    'action' => 'view_admin_news',
                    'view_type' => 'admin',
                    'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
                ]);
                
                $page = isset($_GET['page']) ? max(1, intval($_GET['page'])) : 1;
                $limit = isset($_GET['limit']) ? max(1, intval($_GET['limit'])) : 10;
                $offset = ($page - 1) * $limit;
                
                // Build filters
                $whereClauses = [];
                $params = [];
                $paramTypes = [];
                
                // Status filter
                if (isset($_GET['status'])) {
                    if ($_GET['status'] === 'active') {
                        $whereClauses[] = 'n.is_active = 1';
                    } elseif ($_GET['status'] === 'inactive') {
                        $whereClauses[] = 'n.is_active = 0';
                    } elseif ($_GET['status'] === 'expired') {
                        $whereClauses[] = 'n.end_date < CURDATE() AND n.end_date IS NOT NULL';
                    } elseif ($_GET['status'] === 'upcoming') {
                        $whereClauses[] = 'n.start_date > CURDATE()';
                    }
                }
                
                // Audience type filter
                if (isset($_GET['audience_type']) && in_array($_GET['audience_type'], [AUDIENCE_ALL, AUDIENCE_MEMBERS_ONLY, AUDIENCE_USERS_ONLY, AUDIENCE_ROLE_BASED])) {
                    $whereClauses[] = 'n.audience_type = ?';
                    $params[] = $_GET['audience_type'];
                    $paramTypes[] = PDO::PARAM_STR;
                }
                
                // Role filter (for role-based audience)
                if (!empty($_GET['role_id'])) {
                    $whereClauses[] = '(n.audience_type != ? OR JSON_CONTAINS(n.target_roles, ?))';
                    $params[] = AUDIENCE_ROLE_BASED;
                    $params[] = json_encode([intval($_GET['role_id'])]);
                    $paramTypes[] = PDO::PARAM_STR;
                    $paramTypes[] = PDO::PARAM_STR;
                }
                
                // Priority filter
                if (isset($_GET['priority']) && is_numeric($_GET['priority'])) {
                    $whereClauses[] = 'n.priority = ?';
                    $params[] = intval($_GET['priority']);
                    $paramTypes[] = PDO::PARAM_INT;
                }
                
                // Search filter
                if (!empty($_GET['search'])) {
                    $whereClauses[] = '(n.title LIKE ? OR n.content LIKE ? OR n.excerpt LIKE ?)';
                    $searchTerm = '%' . $_GET['search'] . '%';
                    $params = array_merge($params, [$searchTerm, $searchTerm, $searchTerm]);
                    $paramTypes = array_merge($paramTypes, [PDO::PARAM_STR, PDO::PARAM_STR, PDO::PARAM_STR]);
                }
                
                // Date range filter
                if (!empty($_GET['start_date'])) {
                    $whereClauses[] = 'DATE(n.created_at) >= ?';
                    $params[] = $_GET['start_date'];
                    $paramTypes[] = PDO::PARAM_STR;
                }
                
                if (!empty($_GET['end_date'])) {
                    $whereClauses[] = 'DATE(n.created_at) <= ?';
                    $params[] = $_GET['end_date'];
                    $paramTypes[] = PDO::PARAM_STR;
                }
                
                $whereSQL = empty($whereClauses) ? '' : 'WHERE ' . implode(' AND ', $whereClauses);
                
                // Get total count
                $countSql = "SELECT COUNT(*) as total FROM news_announcements n $whereSQL";
                $countStmt = $pdo->prepare($countSql);
                
                if (!empty($params)) {
                    foreach ($params as $index => $param) {
                        $type = $paramTypes[$index] ?? PDO::PARAM_STR;
                        $countStmt->bindValue($index + 1, $param, $type);
                    }
                }
                
                $countStmt->execute();
                $total = $countStmt->fetch(PDO::FETCH_ASSOC)['total'];
                
                // Get news with pagination
                $sql = "
                    SELECT 
                        n.id, n.title, n.content, n.excerpt, n.icon, 
                        n.is_active, n.start_date, n.end_date, 
                        n.link_url, n.link_text, n.priority,
                        n.audience_type, n.target_roles,
                        n.created_at, n.updated_at,
                        GROUP_CONCAT(DISTINCT r.name) as role_names
                    FROM news_announcements n
                    LEFT JOIN roles r ON JSON_CONTAINS(n.target_roles, CAST(r.id AS JSON))
                    $whereSQL
                    GROUP BY n.id
                    ORDER BY n.created_at DESC
                    LIMIT ? OFFSET ?
                ";
                
                $stmt = $pdo->prepare($sql);
                
                // Bind parameters
                $paramIndex = 1;
                foreach ($params as $index => $param) {
                    $type = $paramTypes[$index] ?? PDO::PARAM_STR;
                    $stmt->bindValue($paramIndex++, $param, $type);
                }
                
                $stmt->bindValue($paramIndex++, $limit, PDO::PARAM_INT);
                $stmt->bindValue($paramIndex, $offset, PDO::PARAM_INT);
                $stmt->execute();
                $news = $stmt->fetchAll(PDO::FETCH_ASSOC);
                
                // Format news items
                $formattedNews = array_map(function($item) {
                    // Determine status
                    $currentDate = date('Y-m-d');
                    $status = 'active';
                    
                    if ($item['is_active'] == 0) {
                        $status = 'inactive';
                    } elseif ($item['end_date'] && $item['end_date'] < $currentDate) {
                        $status = 'expired';
                    } elseif ($item['start_date'] && $item['start_date'] > $currentDate) {
                        $status = 'upcoming';
                    }
                    
                    // Parse target roles
                    $targetRoles = json_decode($item['target_roles'], true) ?: [];
                    
                    return [
                        'id' => $item['id'],
                        'title' => $item['title'],
                        'content' => $item['content'],
                        'excerpt' => $item['excerpt'] ?: substr(strip_tags($item['content']), 0, 100) . '...',
                        'icon' => $item['icon'] ?: 'card-view',
                        'link_url' => $item['link_url'],
                        'link_text' => $item['link_text'] ?: 'Learn more',
                        'priority' => (int)$item['priority'],
                        'is_active' => (bool)$item['is_active'],
                        'status' => $status,
                        'audience_type' => $item['audience_type'] ?? AUDIENCE_ALL,
                        'target_roles' => $targetRoles,
                        'target_role_names' => $item['role_names'] ? explode(',', $item['role_names']) : [],
                        'start_date' => $item['start_date'],
                        'end_date' => $item['end_date'],
                        'created_at' => $item['created_at'],
                        'updated_at' => $item['updated_at'],
                        'created_at_formatted' => date('Y-m-d H:i', strtotime($item['created_at'])),
                        'updated_at_formatted' => $item['updated_at'] ? date('Y-m-d H:i', strtotime($item['updated_at'])) : null
                    ];
                }, $news);

                // Get stats
                $statsSql = "
                    SELECT 
                        COUNT(*) as total_news,
                        COUNT(CASE WHEN n.is_active = 1 THEN 1 END) as active_news,
                        COUNT(CASE WHEN n.is_active = 0 THEN 1 END) as inactive_news,
                        COUNT(CASE WHEN n.end_date < CURDATE() AND n.end_date IS NOT NULL THEN 1 END) as expired_news,
                        COUNT(CASE WHEN n.start_date > CURDATE() THEN 1 END) as upcoming_news,
                        COUNT(CASE WHEN n.audience_type = 'all' THEN 1 END) as audience_all,
                        COUNT(CASE WHEN n.audience_type = 'members' THEN 1 END) as audience_members,
                        COUNT(CASE WHEN n.audience_type = 'users' THEN 1 END) as audience_users,
                        COUNT(CASE WHEN n.audience_type = 'role' THEN 1 END) as audience_role,
                        COUNT(CASE WHEN n.priority = 1 THEN 1 END) as low_priority_count,
                        COUNT(CASE WHEN n.priority = 2 THEN 1 END) as medium_priority_count,
                        COUNT(CASE WHEN n.priority = 3 THEN 1 END) as high_priority_count
                    FROM news_announcements n
                    $whereSQL
                ";
                
                $statsStmt = $pdo->prepare($statsSql);
                
                if (!empty($params)) {
                    foreach ($params as $index => $param) {
                        $type = $paramTypes[$index] ?? PDO::PARAM_STR;
                        $statsStmt->bindValue($index + 1, $param, $type);
                    }
                }
                
                $statsStmt->execute();
                $dbStats = $statsStmt->fetch(PDO::FETCH_ASSOC);
                
                // Format stats
                $stats = [
                    'total_news' => (int)($dbStats['total_news'] ?? 0),
                    'active_news' => (int)($dbStats['active_news'] ?? 0),
                    'inactive_news' => (int)($dbStats['inactive_news'] ?? 0),
                    'expired_news' => (int)($dbStats['expired_news'] ?? 0),
                    'upcoming_news' => (int)($dbStats['upcoming_news'] ?? 0),
                    'audience_all' => (int)($dbStats['audience_all'] ?? 0),
                    'audience_members' => (int)($dbStats['audience_members'] ?? 0),
                    'audience_users' => (int)($dbStats['audience_users'] ?? 0),
                    'audience_role' => (int)($dbStats['audience_role'] ?? 0),
                    'low_priority' => (int)($dbStats['low_priority_count'] ?? 0),
                    'medium_priority' => (int)($dbStats['medium_priority_count'] ?? 0),
                    'high_priority' => (int)($dbStats['high_priority_count'] ?? 0)
                ];
                
                // Get all roles for filtering
                $rolesStmt = $pdo->query("SELECT id, name FROM roles ORDER BY name");
                $roles = $rolesStmt->fetchAll(PDO::FETCH_ASSOC);
                
                $responseData = [
                    'news' => $formattedNews,
                    'stats' => $stats,
                    'total' => (int)$total,
                    'page' => (int)$page,
                    'limit' => (int)$limit,
                    'total_pages' => ceil($total / $limit),
                    'filters' => [
                        'status' => $_GET['status'] ?? null,
                        'audience_type' => $_GET['audience_type'] ?? null,
                        'role_id' => $_GET['role_id'] ?? null,
                        'priority' => $_GET['priority'] ?? null,
                        'search' => $_GET['search'] ?? null,
                        'start_date' => $_GET['start_date'] ?? null,
                        'end_date' => $_GET['end_date'] ?? null
                    ],
                    'available_roles' => $roles,
                    'timestamp' => time()
                ];
                
                safeAuditLog($pdo, $userId, 'VIEW_SUCCESS', 'news_announcements', null, [
                    'action' => 'view_admin_news',
                    'view_type' => 'admin',
                    'total' => $total,
                    'page' => $page,
                    'limit' => $limit,
                    'filters_applied' => !empty($whereClauses),
                    'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
                ]);

                sendJsonResponse(true, $responseData);
            } else {
                // Public view - get news based on user's audience targeting
                safeAuditLog($pdo, $userId, 'VIEW_ATTEMPT', 'news_announcements', null, [
                    'action' => 'view_public_news',
                    'view_type' => 'public',
                    'is_member' => $userIsMember,
                    'role_id' => $userRole['id'] ?? null,
                    'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
                ]);
                
                $limit = isset($_GET['limit']) ? max(1, min(20, intval($_GET['limit']))) : 5;
                
                // Get all active news first
                $stmt = $pdo->prepare("
                    SELECT 
                        id, title, content, excerpt, icon, 
                        is_active, start_date, end_date, 
                        link_url, link_text, priority,
                        audience_type, target_roles,
                        created_at, updated_at
                    FROM news_announcements 
                    WHERE is_active = 1 
                    AND (start_date IS NULL OR start_date <= CURDATE())
                    AND (end_date IS NULL OR end_date >= CURDATE())
                    ORDER BY priority DESC, created_at DESC
                ");
                
                $stmt->execute();
                $allNews = $stmt->fetchAll(PDO::FETCH_ASSOC);
                
                // Filter news based on user's visibility
                $visibleNews = array_filter($allNews, function($item) use ($pdo, $userId) {
                    return isNewsVisibleToUser($pdo, $userId, $item);
                });
                
                // Apply limit after filtering and reindex
                $visibleNews = array_values(array_slice($visibleNews, 0, $limit));
                
                // Format response
                $formattedNews = array_map(function($item) {
                    $targetRoles = json_decode($item['target_roles'], true) ?: [];
                    
                    return [
                        'id' => (int)$item['id'],
                        'title' => $item['title'],
                        'content' => $item['content'],
                        'excerpt' => $item['excerpt'] ?: substr(strip_tags($item['content']), 0, 100) . '...',
                        'icon' => $item['icon'] ?: 'card-view',
                        'link_url' => $item['link_url'],
                        'link_text' => $item['link_text'] ?: 'Learn more',
                        'priority' => (int)$item['priority'],
                        'audience_type' => $item['audience_type'] ?? AUDIENCE_ALL,
                        'target_roles' => $targetRoles,
                        'start_date' => $item['start_date'],
                        'end_date' => $item['end_date'],
                        'created_at' => $item['created_at'],
                        'created_at_formatted' => date('M d, Y', strtotime($item['created_at']))
                    ];
                }, $visibleNews);

                $responseData = [
                    'news' => $formattedNews,
                    'total' => count($formattedNews),
                    'user_context' => [
                        'is_member' => $userIsMember,
                        'role' => $userRole ? [
                            'id' => (int)($userRole['id'] ?? 0),
                            'name' => $userRole['name'] ?? null
                        ] : null
                    ],
                    'timestamp' => time()
                ];
                
                safeAuditLog($pdo, $userId, 'VIEW_SUCCESS', 'news_announcements', null, [
                    'action' => 'view_public_news',
                    'view_type' => 'public',
                    'total_visible' => count($formattedNews),
                    'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
                ]);

                sendJsonResponse(true, $responseData);
            }
            break;

        // 🟡 POST — create new announcement with audience targeting (admin only)
        case 'POST':
            // Rate limiting for POST requests
            if (!checkApiRateLimit('news_post_' . $userId, API_RATE_LIMIT_POST)) {
                safeAuditLog($pdo, $userId, 'RATE_LIMIT_EXCEEDED', 'news_announcements', null, [
                    'action' => 'POST',
                    'limit' => API_RATE_LIMIT_POST,
                    'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
                ]);
                sendJsonResponse(false, null, 'Too many requests. Please try again later.', 429);
            }
            
            $input = json_decode(file_get_contents('php://input'), true);
            
            if (!$input || json_last_error() !== JSON_ERROR_NONE) {
                safeAuditLog($pdo, $userId, 'PROCESS_FAILED', 'news_announcements', null, [
                    'error' => 'Invalid JSON input',
                    'json_error' => json_last_error_msg(),
                    'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
                ]);
                sendJsonResponse(false, null, 'Invalid JSON input: ' . json_last_error_msg(), 400);
            }

            safeAuditLog($pdo, $userId, 'CREATE_ATTEMPT', 'news_announcements', null, [
                'input_data' => sanitizeForLogging($input),
                'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
            ]);

            // Validate required fields including audience targeting
            $errors = validateNewsInput($input);
            if (!empty($errors)) {
                safeAuditLog($pdo, $userId, 'VALIDATION_FAILED', 'news_announcements', null, [
                    'errors' => $errors,
                    'input' => sanitizeForLogging($input),
                    'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
                ]);
                sendJsonResponse(false, null, implode(' ', $errors), 400);
            }

            // Begin transaction
            $pdo->beginTransaction();
            
            try {
                // Prepare target_roles JSON
                $targetRoles = null;
                if (isset($input['target_roles']) && is_array($input['target_roles'])) {
                    $targetRoles = json_encode(array_map('intval', $input['target_roles']));
                } elseif (isset($input['target_roles']) && is_string($input['target_roles'])) {
                    $targetRoles = $input['target_roles'];
                }
                
                $sql = "
                    INSERT INTO news_announcements (
                        title, content, excerpt, icon, 
                        is_active, start_date, end_date, 
                        link_url, link_text, priority,
                        audience_type, target_roles
                    ) VALUES (
                        :title, :content, :excerpt, :icon,
                        :is_active, :start_date, :end_date,
                        :link_url, :link_text, :priority,
                        :audience_type, :target_roles
                    )
                ";
                
                $stmt = $pdo->prepare($sql);
                $result = $stmt->execute([
                    ':title' => trim($input['title']),
                    ':content' => trim($input['content']),
                    ':excerpt' => trim($input['excerpt'] ?? ''),
                    ':icon' => trim($input['icon'] ?? 'card-view'),
                    ':is_active' => isset($input['is_active']) ? (int)$input['is_active'] : 1,
                    ':start_date' => !empty($input['start_date']) ? $input['start_date'] : null,
                    ':end_date' => !empty($input['end_date']) ? $input['end_date'] : null,
                    ':link_url' => trim($input['link_url'] ?? ''),
                    ':link_text' => trim($input['link_text'] ?? 'Learn more'),
                    ':priority' => isset($input['priority']) ? (int)$input['priority'] : 2,
                    ':audience_type' => $input['audience_type'] ?? AUDIENCE_ALL,
                    ':target_roles' => $targetRoles
                ]);

                if (!$result) {
                    $errorInfo = $stmt->errorInfo();
                    throw new Exception('Database error: ' . ($errorInfo[2] ?? 'Unknown error'));
                }

                $newsId = $pdo->lastInsertId();
                
                $pdo->commit();
                
                safeAuditLog($pdo, $userId, 'CREATE_SUCCESS', 'news_announcements', $newsId, [
                    'title' => $input['title'],
                    'priority' => $input['priority'] ?? 2,
                    'audience_type' => $input['audience_type'] ?? AUDIENCE_ALL,
                    'target_roles' => $input['target_roles'] ?? [],
                    'is_active' => $input['is_active'] ?? 1,
                    'start_date' => $input['start_date'] ?? null,
                    'end_date' => $input['end_date'] ?? null,
                    'user_id' => $userId,
                    'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
                ]);

                sendJsonResponse(true, [
                    'id' => (int)$newsId,
                    'timestamp' => time()
                ], 'News announcement created successfully');
                
            } catch (Exception $e) {
                if ($pdo->inTransaction()) {
                    $pdo->rollBack();
                }
                
                safeAuditLog($pdo, $userId, 'TRANSACTION_FAILED', 'news_announcements', null, [
                    'error' => $e->getMessage(),
                    'input_data' => sanitizeForLogging($input),
                    'user_id' => $userId
                ]);
                
                throw $e;
            }
            break;

        // 🔵 PUT — update existing announcement with audience targeting (admin only)
        case 'PUT':
            if (!checkApiRateLimit('news_put_' . $userId, API_RATE_LIMIT_POST)) {
                safeAuditLog($pdo, $userId, 'RATE_LIMIT_EXCEEDED', 'news_announcements', null, [
                    'action' => 'PUT',
                    'limit' => API_RATE_LIMIT_POST,
                    'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
                ]);
                sendJsonResponse(false, null, 'Too many requests. Please try again later.', 429);
            }
            
            $input = json_decode(file_get_contents('php://input'), true);
            
            if (!$input || json_last_error() !== JSON_ERROR_NONE) {
                safeAuditLog($pdo, $userId, 'PROCESS_FAILED', 'news_announcements', null, [
                    'error' => 'Invalid JSON input',
                    'json_error' => json_last_error_msg(),
                    'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
                ]);
                sendJsonResponse(false, null, 'Invalid JSON input: ' . json_last_error_msg(), 400);
            }

            if (empty($input['id'])) {
                safeAuditLog($pdo, $userId, 'UPDATE_FAILED', 'news_announcements', null, [
                    'error' => 'News ID is required for update',
                    'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
                ]);
                sendJsonResponse(false, null, 'News ID is required for update', 400);
            }

            $newsId = intval($input['id']);
            
            $currentStmt = $pdo->prepare("SELECT * FROM news_announcements WHERE id = ? FOR UPDATE");
            $currentStmt->execute([$newsId]);
            $currentData = $currentStmt->fetch(PDO::FETCH_ASSOC);
            
            if (!$currentData) {
                safeAuditLog($pdo, $userId, 'UPDATE_FAILED', 'news_announcements', $newsId, [
                    'error' => 'News announcement not found',
                    'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
                ]);
                sendJsonResponse(false, null, 'News announcement not found', 404);
            }

            safeAuditLog($pdo, $userId, 'UPDATE_ATTEMPT', 'news_announcements', $newsId, [
                'input_data' => sanitizeForLogging($input),
                'current_title' => $currentData['title'],
                'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
            ]);

            $errors = validateNewsInput($input);
            if (!empty($errors)) {
                safeAuditLog($pdo, $userId, 'VALIDATION_FAILED', 'news_announcements', $newsId, [
                    'errors' => $errors,
                    'input' => sanitizeForLogging($input),
                    'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
                ]);
                sendJsonResponse(false, null, implode(' ', $errors), 400);
            }

            $pdo->beginTransaction();
            
            try {
                // Prepare target_roles JSON
                $targetRoles = null;
                if (isset($input['target_roles']) && is_array($input['target_roles'])) {
                    $targetRoles = json_encode(array_map('intval', $input['target_roles']));
                } elseif (isset($input['target_roles']) && is_string($input['target_roles'])) {
                    $targetRoles = $input['target_roles'];
                }
                
                $sql = "
                    UPDATE news_announcements 
                    SET title = :title,
                        content = :content,
                        excerpt = :excerpt,
                        icon = :icon,
                        is_active = :is_active,
                        start_date = :start_date,
                        end_date = :end_date,
                        link_url = :link_url,
                        link_text = :link_text,
                        priority = :priority,
                        audience_type = :audience_type,
                        target_roles = :target_roles,
                        updated_at = CURRENT_TIMESTAMP
                    WHERE id = :id
                ";
                
                $stmt = $pdo->prepare($sql);
                $result = $stmt->execute([
                    ':id' => $newsId,
                    ':title' => trim($input['title']),
                    ':content' => trim($input['content']),
                    ':excerpt' => trim($input['excerpt'] ?? ''),
                    ':icon' => trim($input['icon'] ?? 'card-view'),
                    ':is_active' => isset($input['is_active']) ? (int)$input['is_active'] : 1,
                    ':start_date' => !empty($input['start_date']) ? $input['start_date'] : null,
                    ':end_date' => !empty($input['end_date']) ? $input['end_date'] : null,
                    ':link_url' => trim($input['link_url'] ?? ''),
                    ':link_text' => trim($input['link_text'] ?? 'Learn more'),
                    ':priority' => isset($input['priority']) ? (int)$input['priority'] : 2,
                    ':audience_type' => $input['audience_type'] ?? AUDIENCE_ALL,
                    ':target_roles' => $targetRoles
                ]);

                if (!$result) {
                    $errorInfo = $stmt->errorInfo();
                    throw new Exception('Database error: ' . ($errorInfo[2] ?? 'Unknown error'));
                }

                $pdo->commit();
                
                $changes = detectNewsChanges($currentData, $input);
                
                safeAuditLog($pdo, $userId, 'UPDATE_SUCCESS', 'news_announcements', $newsId, [
                    'title' => $input['title'],
                    'priority' => $input['priority'] ?? 2,
                    'audience_type' => $input['audience_type'] ?? AUDIENCE_ALL,
                    'target_roles' => $input['target_roles'] ?? [],
                    'is_active' => $input['is_active'] ?? 1,
                    'start_date' => $input['start_date'] ?? null,
                    'end_date' => $input['end_date'] ?? null,
                    'changes' => $changes,
                    'user_id' => $userId,
                    'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
                ]);

                sendJsonResponse(true, [
                    'timestamp' => time()
                ], 'News announcement updated successfully');
                
            } catch (Exception $e) {
                if ($pdo->inTransaction()) {
                    $pdo->rollBack();
                }
                
                safeAuditLog($pdo, $userId, 'TRANSACTION_FAILED', 'news_announcements', $newsId, [
                    'error' => $e->getMessage(),
                    'input_data' => sanitizeForLogging($input),
                    'user_id' => $userId
                ]);
                
                throw $e;
            }
            break;

        // 🟣 PATCH — toggle news status (admin only)
        case 'PATCH':
            if (!checkApiRateLimit('news_patch_' . $userId, API_RATE_LIMIT_POST)) {
                safeAuditLog($pdo, $userId, 'RATE_LIMIT_EXCEEDED', 'news_announcements', null, [
                    'action' => 'PATCH',
                    'limit' => API_RATE_LIMIT_POST,
                    'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
                ]);
                sendJsonResponse(false, null, 'Too many requests. Please try again later.', 429);
            }
            
            $input = json_decode(file_get_contents('php://input'), true);
            
            if (empty($input['id'])) {
                safeAuditLog($pdo, $userId, 'STATUS_UPDATE_FAILED', 'news_announcements', null, [
                    'error' => 'News ID is required',
                    'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
                ]);
                sendJsonResponse(false, null, 'News ID is required', 400);
            }

            $newsId = intval($input['id']);
            $isActive = isset($input['is_active']) ? (int)$input['is_active'] : null;
            
            if ($isActive === null) {
                safeAuditLog($pdo, $userId, 'STATUS_UPDATE_FAILED', 'news_announcements', $newsId, [
                    'error' => 'Missing status value',
                    'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
                ]);
                sendJsonResponse(false, null, 'Missing status value', 400);
            }

            $newsStmt = $pdo->prepare("SELECT * FROM news_announcements WHERE id = ? FOR UPDATE");
            $newsStmt->execute([$newsId]);
            $newsItem = $newsStmt->fetch(PDO::FETCH_ASSOC);
            
            if (!$newsItem) {
                safeAuditLog($pdo, $userId, 'STATUS_UPDATE_FAILED', 'news_announcements', $newsId, [
                    'error' => 'News announcement not found',
                    'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
                ]);
                sendJsonResponse(false, null, 'News announcement not found', 404);
            }

            if ($newsItem['is_active'] == $isActive) {
                sendJsonResponse(true, [
                    'timestamp' => time()
                ], 'Status already set to requested value');
            }

            safeAuditLog($pdo, $userId, 'STATUS_UPDATE_ATTEMPT', 'news_announcements', $newsId, [
                'old_status' => $newsItem['is_active'],
                'new_status' => $isActive,
                'title' => $newsItem['title'],
                'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
            ]);

            $pdo->beginTransaction();
            
            try {
                $stmt = $pdo->prepare("
                    UPDATE news_announcements 
                    SET is_active = ?, updated_at = CURRENT_TIMESTAMP
                    WHERE id = ?
                ");
                $result = $stmt->execute([
                    $isActive,
                    $newsId
                ]);

                if (!$result) {
                    $errorInfo = $stmt->errorInfo();
                    throw new Exception('Database error: ' . ($errorInfo[2] ?? 'Unknown error'));
                }

                $pdo->commit();
                
                $statusText = $isActive ? 'activated' : 'deactivated';
                safeAuditLog($pdo, $userId, 'STATUS_UPDATE_SUCCESS', 'news_announcements', $newsId, [
                    'title' => $newsItem['title'],
                    'old_status' => $newsItem['is_active'],
                    'new_status' => $isActive,
                    'action' => $statusText,
                    'user_id' => $userId,
                    'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
                ]);

                sendJsonResponse(true, [
                    'timestamp' => time()
                ], "News announcement {$statusText} successfully");
                
            } catch (Exception $e) {
                if ($pdo->inTransaction()) {
                    $pdo->rollBack();
                }
                throw $e;
            }
            break;

        // 🔴 DELETE — delete announcement (admin only)
        case 'DELETE':
            if (!checkApiRateLimit('news_delete_' . $userId, API_RATE_LIMIT_POST)) {
                safeAuditLog($pdo, $userId, 'RATE_LIMIT_EXCEEDED', 'news_announcements', null, [
                    'action' => 'DELETE',
                    'limit' => API_RATE_LIMIT_POST,
                    'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
                ]);
                sendJsonResponse(false, null, 'Too many requests. Please try again later.', 429);
            }
            
            $input = json_decode(file_get_contents('php://input'), true);
            
            if (!$input || json_last_error() !== JSON_ERROR_NONE) {
                safeAuditLog($pdo, $userId, 'PROCESS_FAILED', 'news_announcements', null, [
                    'error' => 'Invalid JSON input',
                    'json_error' => json_last_error_msg(),
                    'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
                ]);
                sendJsonResponse(false, null, 'Invalid JSON input: ' . json_last_error_msg(), 400);
            }

            if (empty($input['id'])) {
                safeAuditLog($pdo, $userId, 'DELETE_FAILED', 'news_announcements', null, [
                    'error' => 'News ID is required for deletion',
                    'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
                ]);
                sendJsonResponse(false, null, 'News ID is required for deletion', 400);
            }

            $newsId = intval($input['id']);
            
            $newsStmt = $pdo->prepare("SELECT * FROM news_announcements WHERE id = ? FOR UPDATE");
            $newsStmt->execute([$newsId]);
            $newsItem = $newsStmt->fetch(PDO::FETCH_ASSOC);
            
            if (!$newsItem) {
                safeAuditLog($pdo, $userId, 'DELETE_FAILED', 'news_announcements', $newsId, [
                    'error' => 'News announcement not found',
                    'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
                ]);
                sendJsonResponse(false, null, 'News announcement not found', 404);
            }

            safeAuditLog($pdo, $userId, 'DELETE_ATTEMPT', 'news_announcements', $newsId, [
                'title' => $newsItem['title'],
                'priority' => $newsItem['priority'],
                'is_active' => $newsItem['is_active'],
                'audience_type' => $newsItem['audience_type'],
                'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
            ]);

            $pdo->beginTransaction();
            
            try {
                $deleteStmt = $pdo->prepare("DELETE FROM news_announcements WHERE id = ?");
                $result = $deleteStmt->execute([$newsId]);

                if (!$result) {
                    $errorInfo = $deleteStmt->errorInfo();
                    throw new Exception('Database error: ' . ($errorInfo[2] ?? 'Unknown error'));
                }

                $pdo->commit();
                
                safeAuditLog($pdo, $userId, 'DELETE_SUCCESS', 'news_announcements', $newsId, [
                    'title' => $newsItem['title'],
                    'priority' => $newsItem['priority'],
                    'is_active' => $newsItem['is_active'],
                    'audience_type' => $newsItem['audience_type'],
                    'start_date' => $newsItem['start_date'],
                    'end_date' => $newsItem['end_date'],
                    'user_id' => $userId,
                    'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
                ]);

                sendJsonResponse(true, [
                    'timestamp' => time()
                ], 'News announcement deleted successfully');
                
            } catch (Exception $e) {
                if ($pdo->inTransaction()) {
                    $pdo->rollBack();
                }
                throw $e;
            }
            break;

        default:
            safeAuditLog($pdo, $userId, 'METHOD_NOT_ALLOWED', 'news_announcements', null, [
                'method' => $method,
                'allowed_methods' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
                'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
            ]);
            sendJsonResponse(false, null, 'Method not allowed', 405);
    }

} catch (Exception $e) {
    safeAuditLog($pdo, $userId, 'EXCEPTION', 'news_announcements', null, [
        'error' => $e->getMessage(),
        'method' => $method,
        'file' => $e->getFile(),
        'line' => $e->getLine(),
        'trace' => substr($e->getTraceAsString(), 0, 1000),
        'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
    ]);
    
    error_log("NewsAPI Error [User: $userId, Method: $method, IP: " . 
              ($_SERVER['REMOTE_ADDR'] ?? 'unknown') . "]: " . 
              $e->getMessage() . " in " . $e->getFile() . ":" . $e->getLine());
    
    sendJsonResponse(false, null, 'An error occurred while processing your request', 500);
}

/**
 * Helper Functions
 */
function validateNewsInput($input) {
    $errors = [];
    
    if (empty($input['title'])) {
        $errors[] = 'Title is required';
    } elseif (strlen($input['title']) > 255) {
        $errors[] = 'Title must not exceed 255 characters';
    }
    
    if (empty($input['content'])) {
        $errors[] = 'Content is required';
    } elseif (strlen($input['content']) > MAX_NEWS_CONTENT_SIZE) {
        $errors[] = 'Content is too long';
    }
    
    if (isset($input['excerpt']) && strlen($input['excerpt']) > 500) {
        $errors[] = 'Excerpt must not exceed 500 characters';
    }
    
    // Validate audience type
    $validAudienceTypes = [AUDIENCE_ALL, AUDIENCE_MEMBERS_ONLY, AUDIENCE_USERS_ONLY, AUDIENCE_ROLE_BASED];
    if (isset($input['audience_type']) && !in_array($input['audience_type'], $validAudienceTypes)) {
        $errors[] = 'Invalid audience type';
    }
    
    // Validate target roles if audience type is role-based
    if (isset($input['audience_type']) && $input['audience_type'] === AUDIENCE_ROLE_BASED) {
        if (empty($input['target_roles']) || !is_array($input['target_roles'])) {
            $errors[] = 'Target roles are required for role-based audience';
        }
    }
    
    if (!empty($input['start_date']) && !validateDate($input['start_date'])) {
        $errors[] = 'Invalid start date format (YYYY-MM-DD)';
    }
    
    if (!empty($input['end_date']) && !validateDate($input['end_date'])) {
        $errors[] = 'Invalid end date format (YYYY-MM-DD)';
    }
    
    if (!empty($input['start_date']) && !empty($input['end_date']) && $input['start_date'] > $input['end_date']) {
        $errors[] = 'End date must be after start date';
    }
    
    if (isset($input['priority']) && !in_array($input['priority'], [1, 2, 3])) {
        $errors[] = 'Priority must be 1 (Low), 2 (Medium), or 3 (High)';
    }
    
    if (!empty($input['link_url']) && !filter_var($input['link_url'], FILTER_VALIDATE_URL)) {
        $errors[] = 'Invalid link URL format';
    }
    
    if (isset($input['link_text']) && strlen($input['link_text']) > 50) {
        $errors[] = 'Link text must not exceed 50 characters';
    }
    
    return $errors;
}

function validateDate($date, $format = 'Y-m-d') {
    $d = DateTime::createFromFormat($format, $date);
    return $d && $d->format($format) === $date;
}

function detectNewsChanges($oldData, $newData) {
    $changes = [];
    
    $fields = [
        'title', 'content', 'excerpt', 'icon', 
        'is_active', 'start_date', 'end_date', 
        'link_url', 'link_text', 'priority',
        'audience_type', 'target_roles'
    ];
    
    foreach ($fields as $field) {
        $oldValue = $oldData[$field] ?? null;
        $newValue = $newData[$field] ?? null;
        
        if (!array_key_exists($field, $newData)) {
            continue;
        }
        
        $oldValue = is_string($oldValue) ? trim($oldValue) : $oldValue;
        $newValue = is_string($newValue) ? trim($newValue) : $newValue;
        
        if ($field === 'is_active') {
            $oldValue = (bool)$oldValue;
            $newValue = (bool)$newValue;
        }
        
        if ($field === 'target_roles') {
            $oldValue = $oldValue ? json_decode($oldValue, true) : [];
            $newValue = $newValue ? (is_array($newValue) ? $newValue : json_decode($newValue, true)) : [];
            sort($oldValue);
            sort($newValue);
        }
        
        if (in_array($field, ['start_date', 'end_date'])) {
            $oldValue = $oldValue ?: null;
            $newValue = $newValue ?: null;
        }
        
        if ($oldValue != $newValue) {
            $changes[$field] = [
                'old' => $oldValue,
                'new' => $newValue
            ];
        }
    }
    
    return $changes;
}

function sanitizeForLogging($data) {
    $sanitized = [];
    foreach ($data as $key => $value) {
        if ($key === 'content' && is_string($value) && strlen($value) > 100) {
            $sanitized[$key] = substr($value, 0, 100) . '...';
        } elseif (is_string($value) && strlen($value) > 100) {
            $sanitized[$key] = substr($value, 0, 100) . '...';
        } else {
            $sanitized[$key] = $value;
        }
    }
    return $sanitized;
}

function checkApiRateLimit($key, $limit) {
    $cacheFile = sys_get_temp_dir() . '/rate_limit_' . md5($key) . '.json';
    $currentTime = time();
    
    if (file_exists($cacheFile)) {
        $data = json_decode(file_get_contents($cacheFile), true);
        if ($currentTime - $data['timestamp'] < 60) {
            if ($data['count'] >= $limit) {
                return false;
            }
            $data['count']++;
        } else {
            $data = ['count' => 1, 'timestamp' => $currentTime];
        }
    } else {
        $data = ['count' => 1, 'timestamp' => $currentTime];
    }
    
    file_put_contents($cacheFile, json_encode($data), LOCK_EX);
    return true;
}