<?php
if (!defined('SECURE_ACCESS')) {
    header("HTTP/1.1 403 Forbidden");
    exit("Direct access not allowed.");
}
require_once __DIR__ . '/config.php';

// Check if user is logged in
function isLoggedIn() {
    return isset($_SESSION['user_id']);
}

// Redirect to login page if user is not logged in
function requireLogin() {
    if (!isLoggedIn()) {
        $_SESSION['flash_error'] = "Please log in to access this page.";
        $prefix = (strpos($_SERVER['PHP_SELF'], '/admin/') !== false) ? '../' : '';
        header("Location: " . $prefix . "login.php");
        exit();
    }
    // DB status revalidation — invalidate session if user is deactivated or deleted
    global $pdo;
    if ($pdo) {
        try {
            $stmt = $pdo->prepare("SELECT status FROM users WHERE id = ? LIMIT 1");
            $stmt->execute([$_SESSION['user_id']]);
            $db_status = $stmt->fetchColumn();
            if ($db_status === false || $db_status !== 'active') {
                session_unset();
                session_destroy();
                $prefix = (strpos($_SERVER['PHP_SELF'], '/admin/') !== false) ? '../' : '';
                header("Location: " . $prefix . "login?reason=account_inactive");
                exit();
            }
        } catch (PDOException $e) {
            // If DB check fails, allow the session to continue to avoid locking out users during DB issues
        }
    }
}

// Get current user details from session
function getCurrentUser() {
    if (!isLoggedIn()) {
        return null;
    }
    return [
        'id' => $_SESSION['user_id'],
        'username' => $_SESSION['username'] ?? '',
        'email' => $_SESSION['email'] ?? '',
        'role' => $_SESSION['role'] ?? '',
        'status' => $_SESSION['status'] ?? 'active'
    ];
}

// Check if current user is admin
function isAdmin() {
    return isLoggedIn() && $_SESSION['role'] === 'admin';
}

// Check if current user is organizer
function isOrganizer() {
    return isLoggedIn() && $_SESSION['role'] === 'organizer';
}

// Check if current user can manage events (Admin or Organizer)
function isEventManager() {
    return isAdmin() || isOrganizer();
}

// Require event manager access (Admin or Organizer)
function requireEventManager() {
    requireLogin();
    if (!isEventManager()) {
        $_SESSION['flash_error'] = "Access denied. You must be an Organizer or Administrator to view this page.";
        $prefix = (strpos($_SERVER['PHP_SELF'], '/admin/') !== false) ? '../' : '';
        header("Location: " . $prefix . "dashboard.php");
        exit();
    }
}

// Require a specific role to access a page
function requireRole($role) {
    requireLogin();
    if ($_SESSION['role'] !== $role) {
        $_SESSION['flash_error'] = "Access denied. You do not have permission to view this page.";
        $prefix = (strpos($_SERVER['PHP_SELF'], '/admin/') !== false) ? '../' : '';
        header("Location: " . $prefix . "dashboard.php");
        exit();
    }
}

// Require admin role
function requireAdmin() {
    requireRole('admin');
}

// Redirect if already logged in
function redirectIfLoggedIn() {
    if (isLoggedIn()) {
        $prefix = (strpos($_SERVER['PHP_SELF'], '/admin/') !== false) ? '../' : '';
        header("Location: " . $prefix . "dashboard.php");
        exit();
    }
}

// Fetch system settings from the database
function getSystemSettings() {
    global $pdo;
    $settings = [];
    try {
        if ($pdo !== null) {
            $stmt = $pdo->query("SELECT \"key\", value FROM system_settings");
            while ($row = $stmt->fetch()) {
                $settings[$row['key']] = $row['value'];
            }
        }
    } catch (PDOException $e) {
        // Fallbacks will merge
    }
    return array_merge([
        'emp_code_prefix' => 'REPL',
        'emp_code_length' => '15',
        'emp_code_default_type' => 'generated',
        'emp_code_custom_value' => 'EMP-DEFAULT'
    ], $settings);
}

// Generate random alphanumeric employee code matching settings
function generateEmployeeCode($prefix, $length) {
    $random_len = (int)$length - strlen($prefix);
    if ($random_len < 0) {
        $random_len = 0;
    }
    $chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    $random_part = '';
    $chars_len = strlen($chars) - 1;
    for ($i = 0; $i < $random_len; $i++) {
        $random_part .= $chars[random_int(0, $chars_len)];
    }
    return $prefix . $random_part;
}

// Find placement for a referred user in a forced matrix with spillover using BFS
function findMatrixPlacement($sponsor_id, $matrix_width, $matrix_depth) {
    global $pdo;
    if ($pdo === null) return $sponsor_id;
    
    try {
        $stmt = $pdo->prepare("
            WITH RECURSIVE downline AS (
                SELECT id, parent_id, 0 AS level
                FROM users
                WHERE id = ?
                
                UNION ALL
                
                SELECT u.id, u.parent_id, d.level + 1
                FROM users u
                INNER JOIN downline d ON u.parent_id = d.id
                WHERE d.level < ?
            )
            SELECT id, parent_id, level FROM downline ORDER BY id ASC
        ");
        $stmt->execute([$sponsor_id, $matrix_depth]);
        $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
    } catch (PDOException $e) {
        $rows = [];
    }
    
    if (empty($rows)) {
        return $sponsor_id;
    }
    
    $children_map = [];
    $node_levels = [];
    foreach ($rows as $row) {
        $pid = $row['parent_id'];
        $cid = $row['id'];
        $node_levels[$cid] = (int)$row['level'];
        if ($pid !== null) {
            $children_map[$pid][] = $cid;
        }
    }
    
    $queue = [$sponsor_id];
    $visited = [$sponsor_id => true];
    
    while (!empty($queue)) {
        $current_id = array_shift($queue);
        $curr_depth = $node_levels[$current_id] ?? 0;
        
        if ($curr_depth >= $matrix_depth) {
            continue;
        }
        
        $children = $children_map[$current_id] ?? [];
        if (count($children) < $matrix_width) {
            return $current_id;
        }
        
        foreach ($children as $child_id) {
            if (isset($visited[$child_id])) {
                continue;
            }
            $visited[$child_id] = true;
            $queue[] = $child_id;
        }
    }
    
    return $sponsor_id;
}

// Generate a CSRF token if one doesn't exist
function getCSRFToken() {
    if (session_status() === PHP_SESSION_NONE) {
        session_start();
    }
    if (empty($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }
    return $_SESSION['csrf_token'];
}

// Verify a CSRF token from request and regenerate for single-use protection
function verifyCSRFToken($token) {
    if (session_status() === PHP_SESSION_NONE) {
        session_start();
    }
    return !empty($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token);
}

// Get accessible event IDs for a user based on designation
function getAccessibleEventIdsForUser($userId, $userRole, $pdo) {
    if ($userRole === 'admin' || $userRole === 'organizer') {
        return null; // Admins and Organizers have full access
    }
    
    $stmt = $pdo->prepare("
        SELECT d.accessible_event_ids 
        FROM users u 
        JOIN designations d ON u.designation_id = d.id 
        WHERE u.id = ?
    ");
    $stmt->execute([$userId]);
    $accessible_events = $stmt->fetchColumn();
    
    if ($accessible_events !== false && trim($accessible_events) !== '') {
        return array_filter(array_map('intval', explode(',', $accessible_events)));
    }
    
    return []; // No access to any events if no designation or empty
}

// Audit log helper function to record sensitive operations (#14)
function logAuditEvent($action, $entityType = null, $entityId = null, $details = null) {
    global $pdo;
    if (!$pdo) return;
    
    try {
        $userId = $_SESSION['user_id'] ?? null;
        $ip = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
        
        $stmt = $pdo->prepare("
            INSERT INTO audit_log (user_id, action, entity_type, entity_id, details, ip_address)
            VALUES (?, ?, ?, ?, ?, ?)
        ");
        $stmt->execute([$userId, $action, $entityType, $entityId, $details, $ip]);
    } catch (PDOException $e) {
        error_log("Audit log error: " . $e->getMessage());
    }
}

// Automatically evaluate user stats against requirements and promote designation (Gap #23)
function checkAndApplyDesignationPromotion($userId, $pdo) {
    if (!$pdo || !$userId) return false;
    
    try {
        // Fetch current user designation requirements and info
        $stmt_user = $pdo->prepare("
            SELECT u.id, u.designation_id, u.created_at, d.title as current_title
            FROM users u
            LEFT JOIN designations d ON u.designation_id = d.id
            WHERE u.id = ?
        ");
        $stmt_user->execute([$userId]);
        $user_info = $stmt_user->fetch(PDO::FETCH_ASSOC);
        if (!$user_info) return false;
        
        $current_designation_id = $user_info['designation_id'];
        
        // 1. Calculate direct referrals (active)
        $stmt_direct = $pdo->prepare("SELECT COUNT(*) FROM users WHERE referrer_id = ? AND status = 'active'");
        $stmt_direct->execute([$userId]);
        $direct_referrals = (int)$stmt_direct->fetchColumn();
        
        // 2. Calculate team size (matrix downline level-independent count)
        $stmt_team = $pdo->prepare("
            WITH RECURSIVE team_tree AS (
                SELECT id, parent_id FROM users WHERE parent_id = ? AND status = 'active'
                UNION ALL
                SELECT u.id, u.parent_id FROM users u INNER JOIN team_tree t ON u.parent_id = t.id WHERE u.status = 'active'
            )
            SELECT COUNT(*) FROM team_tree
        ");
        $stmt_team->execute([$userId]);
        $team_size = (int)$stmt_team->fetchColumn();
        
        // 3. Calculate active duration in months
        $created_time = strtotime($user_info['created_at']);
        $duration_months = (int)floor((time() - $created_time) / (30 * 24 * 3600));
        
        // Fetch all designations ordered by criteria requirements descending (highest rank VP first)
        $stmt_desigs = $pdo->query("
            SELECT * FROM designations 
            ORDER BY criteria_team_size DESC, criteria_direct_referrals DESC, id DESC
        ");
        $designations = $stmt_desigs->fetchAll(PDO::FETCH_ASSOC);
        
        $promoted_desig = null;
        foreach ($designations as $d) {
            $req_directs = (int)$d['criteria_direct_referrals'];
            $req_team = (int)$d['criteria_team_size'];
            $req_months = (int)$d['criteria_min_duration_months'];
            
            if ($direct_referrals >= $req_directs && $team_size >= $req_team && $duration_months >= $req_months) {
                $promoted_desig = $d;
                break; // Found highest qualified designation
            }
        }
        
        if ($promoted_desig && $promoted_desig['id'] != $current_designation_id) {
            $stmt_upd = $pdo->prepare("UPDATE users SET designation_id = ? WHERE id = ?");
            $stmt_upd->execute([$promoted_desig['id'], $userId]);
            
            logAuditEvent('designation_promotion', 'user', $userId, "Promoted user rank from " . ($user_info['current_title'] ?: 'None') . " to " . $promoted_desig['title'] . " (Directs: $direct_referrals, Team: $team_size)");
            
            // Send dynamic notification
            require_once __DIR__ . '/notification_helper.php';
            sendMultichannelNotification($userId, 'designation_promotion', [
                'designation_title' => $promoted_desig['title']
            ]);
            
            return true;
        }
    } catch (PDOException $e) {
        error_log("Promotion check error: " . $e->getMessage());
    }
    return false;
}
