<?php
/**
 * Authentication and Security Functions
 * LandSales Pro - Handles session management, CSRF protection, password hashing
 */

if (!defined('APP_RUNNING')) {
    define('APP_RUNNING', true);
}

require_once __DIR__ . '/../config.php';

/**
 * Generate a secure random token
 */
function generateToken($length = 32) {
    return bin2hex(random_bytes($length));
}

/**
 * Generate JWT token for API authentication
 */
function generateJWT($user_id, $role, $expiry = null) {
    $header = json_encode(['typ' => 'JWT', 'alg' => 'HS256']);
    $expiry = $expiry ?? time() + JWT_EXPIRY;

    $payload = json_encode([
        'user_id' => $user_id,
        'role' => $role,
        'iat' => time(),
        'exp' => $expiry,
        'jti' => generateToken(16)
    ]);

    $base64Header = base64UrlEncode($header);
    $base64Payload = base64UrlEncode($payload);

    $signature = hash_hmac('sha256', "$base64Header.$base64Payload", JWT_SECRET, true);
    $base64Signature = base64UrlEncode($signature);

    return "$base64Header.$base64Payload.$base64Signature";
}

/**
 * Verify and decode JWT token
 */
function verifyJWT($token) {
    $parts = explode('.', $token);
    if (count($parts) !== 3) {
        return false;
    }

    list($base64Header, $base64Payload, $base64Signature) = $parts;

    $signature = hash_hmac('sha256', "$base64Header.$base64Payload", JWT_SECRET, true);
    $expectedSignature = base64UrlEncode($signature);

    if (!hash_equals($expectedSignature, $base64Signature)) {
        return false;
    }

    $payload = json_decode(base64UrlDecode($base64Payload), true);

    if (!isset($payload['exp']) || $payload['exp'] < time()) {
        return false;
    }

    return $payload;
}

/**
 * Base64 URL encode
 */
function base64UrlEncode($data) {
    return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}

/**
 * Base64 URL decode
 */
function base64UrlDecode($data) {
    return base64_decode(strtr($data, '-_', '+/'));
}

/**
 * Generate CSRF token
 */
function generateCSRFToken() {
    if (!isset($_SESSION['csrf_token']) || !isset($_SESSION['csrf_token_time']) ||
        (time() - $_SESSION['csrf_token_time']) > CSRF_TOKEN_LIFETIME) {
        $_SESSION['csrf_token'] = generateToken();
        $_SESSION['csrf_token_time'] = time();
    }
    return $_SESSION['csrf_token'];
}

/**
 * Verify CSRF token
 */
function verifyCSRFToken($token) {
    if (!isset($_SESSION['csrf_token']) || !isset($_SESSION['csrf_token_time'])) {
        return false;
    }

    if ((time() - $_SESSION['csrf_token_time']) > CSRF_TOKEN_LIFETIME) {
        return false;
    }

    return hash_equals($_SESSION['csrf_token'], $token);
}

/**
 * Output CSRF hidden field for forms
 */
function csrfField() {
    return '<input type="hidden" name="csrf_token" value="' . generateCSRFToken() . '">';
}

/**
 * Initialize secure session
 */
function initSecureSession() {
    if (session_status() === PHP_SESSION_NONE) {
        ini_set('session.cookie_httponly', 1);
        ini_set('session.cookie_secure', IS_PRODUCTION ? 1 : 0);
        ini_set('session.use_only_cookies', 1);
        ini_set('session.cookie_samesite', 'Lax');
        ini_set('session.gc_maxlifetime', SESSION_LIFETIME);

        session_name(SESSION_NAME);
        session_start();

        // Regenerate session ID periodically
        if (!isset($_SESSION['last_regeneration'])) {
            $_SESSION['last_regeneration'] = time();
        } elseif (time() - $_SESSION['last_regeneration'] > 300) {
            session_regenerate_id(true);
            $_SESSION['last_regeneration'] = time();
        }
    }
}

/**
 * Check if user is authenticated
 */
function isAuthenticated() {
    initSecureSession();
    return isset($_SESSION['user_id']) && isset($_SESSION['auth_token']);
}

/**
 * Check if user has specific role
 */
function hasRole($required_role) {
    if (!isAuthenticated()) return false;

    $user_role = $_SESSION['role'] ?? 'subscriber';

    // Super admin has all permissions
    if ($user_role === 'super_admin') return true;

    // Admin has most permissions
    if ($user_role === 'admin' && $required_role !== 'super_admin') return true;

    if (is_array($required_role)) {
        return in_array($user_role, $required_role);
    }

    return $user_role === $required_role;
}

/**
 * Check if current user is admin (super_admin or admin)
 */
function isAdmin() {
    return hasRole(['super_admin', 'admin']);
}

/**
 * Check if current user is subscriber
 */
function isSubscriber() {
    return hasRole('subscriber');
}

/**
 * Check if current user is a staff member
 */
function isStaff() {
    return isset($_SESSION['is_staff']) && $_SESSION['is_staff'] === true;
}

/**
 * Get the owner user ID (for staff, returns their owner's ID; for subscribers, returns their own ID)
 */
function getOwnerId() {
    if (isStaff()) {
        return $_SESSION['owner_id'] ?? $_SESSION['user_id'];
    }
    return $_SESSION['user_id'];
}

/**
 * Check if staff user has a specific permission
 */
function hasPermission($permission) {
    // Non-staff users (subscribers) have all permissions
    if (!isStaff()) {
        return true;
    }

    // Get permissions from session
    $permissions = $_SESSION['staff_permissions'] ?? [];
    return in_array($permission, $permissions);
}

/**
 * Check if staff has view permission for a module
 */
function canView($module) {
    if (!isStaff()) return true;
    return hasPermission($module . '_view') || hasPermission($module . '_manage') || hasPermission($module);
}

/**
 * Check if staff has manage permission for a module
 */
function canManage($module) {
    if (!isStaff()) return true;
    return hasPermission($module . '_manage');
}

/**
 * Require a specific permission
 */
function requirePermission($permission, $redirect = null) {
    requireAuth();
    if (!hasPermission($permission) && !hasPermission(str_replace('_view', '_manage', $permission))) {
        $redirect = $redirect ?? SITE_URL . '/dashboard?error=access_denied';
        header('HTTP/1.1 403 Forbidden');
        header('Location: ' . $redirect);
        exit;
    }
}

/**
 * Require view permission for a module
 */
function requireViewPermission($module, $redirect = null) {
    requireAuth();
    if (!canView($module)) {
        $redirect = $redirect ?? SITE_URL . '/dashboard?error=access_denied';
        header('HTTP/1.1 403 Forbidden');
        header('Location: ' . $redirect);
        exit;
    }
}

/**
 * Require manage permission for a module
 */
function requireManagePermission($module, $redirect = null) {
    requireAuth();
    if (!canManage($module)) {
        $redirect = $redirect ?? SITE_URL . '/dashboard?error=access_denied';
        header('HTTP/1.1 403 Forbidden');
        header('Location: ' . $redirect);
        exit;
    }
}

/**
 * Require authentication - redirect if not authenticated
 */
function requireAuth($redirect = null) {
    if (!isAuthenticated()) {
        $redirect = $redirect ?? SITE_URL . '/login';
        header('Location: ' . $redirect);
        exit;
    }
}

/**
 * Require specific role
 */
function requireRole($role, $redirect = null) {
    requireAuth();
    if (!hasRole($role)) {
        $redirect = $redirect ?? SITE_URL . '/dashboard?error=access_denied';
        header('HTTP/1.1 403 Forbidden');
        header('Location: ' . $redirect);
        exit;
    }
}

/**
 * Require admin access
 */
function requireAdmin() {
    requireRole(['super_admin', 'admin'], SITE_URL . '/login?error=admin_required');
}

/**
 * Require subscriber access
 */
function requireSubscriber() {
    requireRole(['subscriber', 'staff'], SITE_URL . '/login');
}

/**
 * Login user and create session
 */
function loginUser($user) {
    global $conn;

    initSecureSession();
    session_regenerate_id(true);

    $auth_token = generateToken();

    $_SESSION['user_id'] = $user['id'];
    $_SESSION['email'] = $user['email'];
    $_SESSION['full_name'] = $user['full_name'];
    $_SESSION['user_name'] = $user['full_name'];
    $_SESSION['company_name'] = $user['company_name'] ?? '';
    $_SESSION['role'] = $user['role'];
    $_SESSION['auth_token'] = $auth_token;
    $_SESSION['login_time'] = time();
    $_SESSION['ip_address'] = getClientIP();
    $_SESSION['user_agent'] = $_SERVER['HTTP_USER_AGENT'] ?? '';
    $_SESSION['is_staff'] = false;

    // Update last login
    $stmt = $conn->prepare("UPDATE users SET last_login = NOW(), login_count = login_count + 1 WHERE id = ?");
    $stmt->bind_param("i", $user['id']);
    $stmt->execute();

    // Log the login
    logActivity($user['id'], 'login', 'User logged in');

    return $auth_token;
}

/**
 * Login staff user and create session
 */
function loginStaffUser($staff, $owner) {
    global $conn;

    initSecureSession();
    session_regenerate_id(true);

    $auth_token = generateToken();

    // Staff user ID with a prefix to differentiate
    $_SESSION['user_id'] = $owner['id']; // Use owner's ID for data access
    $_SESSION['staff_id'] = $staff['id'];
    $_SESSION['owner_id'] = $owner['id'];
    $_SESSION['email'] = $staff['email'];
    $_SESSION['full_name'] = $staff['full_name'];
    $_SESSION['user_name'] = $staff['full_name'];
    $_SESSION['company_name'] = $owner['company_name'] ?? '';
    $_SESSION['role'] = 'staff';
    $_SESSION['auth_token'] = $auth_token;
    $_SESSION['login_time'] = time();
    $_SESSION['ip_address'] = getClientIP();
    $_SESSION['user_agent'] = $_SERVER['HTTP_USER_AGENT'] ?? '';
    $_SESSION['is_staff'] = true;

    // Load staff permissions
    $stmt = $conn->prepare("SELECT permission FROM staff_permissions WHERE staff_id = ?");
    $stmt->bind_param("i", $staff['id']);
    $stmt->execute();
    $result = $stmt->get_result();
    $permissions = [];
    while ($row = $result->fetch_assoc()) {
        $permissions[] = $row['permission'];
    }
    $_SESSION['staff_permissions'] = $permissions;

    // Update staff last login
    $stmt = $conn->prepare("UPDATE staff_users SET last_login = NOW(), login_count = login_count + 1 WHERE id = ?");
    $stmt->bind_param("i", $staff['id']);
    $stmt->execute();

    // Log the login
    logActivity($owner['id'], 'staff_login', "Staff user {$staff['full_name']} logged in");

    return $auth_token;
}

/**
 * Logout user and destroy session
 */
function logoutUser() {
    global $conn;

    initSecureSession();

    if (isset($_SESSION['user_id'])) {
        logActivity($_SESSION['user_id'], 'logout', 'User logged out');
    }

    $_SESSION = [];

    if (isset($_COOKIE[session_name()])) {
        setcookie(session_name(), '', time() - 3600, '/');
    }

    session_destroy();
}

/**
 * Validate session integrity
 */
function validateSession() {
    if (!isset($_SESSION['user_agent'])) {
        return false;
    }

    if ($_SESSION['user_agent'] !== ($_SERVER['HTTP_USER_AGENT'] ?? '')) {
        return false;
    }

    if (isset($_SESSION['login_time']) && (time() - $_SESSION['login_time']) > SESSION_LIFETIME) {
        return false;
    }

    return true;
}

/**
 * Hash password securely
 */
function hashPassword($password) {
    return password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);
}

/**
 * Verify password
 */
function verifyPassword($password, $hash) {
    return password_verify($password, $hash);
}

/**
 * Validate password strength
 */
function validatePasswordStrength($password) {
    $errors = [];

    if (strlen($password) < PASSWORD_MIN_LENGTH) {
        $errors[] = "Password must be at least " . PASSWORD_MIN_LENGTH . " characters";
    }

    return $errors;
}

/**
 * Sanitize input
 */
function sanitizeInput($input) {
    if (is_array($input)) {
        return array_map('sanitizeInput', $input);
    }
    return htmlspecialchars(trim($input), ENT_QUOTES, 'UTF-8');
}

/**
 * Get client IP address
 */
function getClientIP() {
    $ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';

    if (!empty($_SERVER['HTTP_CLIENT_IP'])) {
        $ip = $_SERVER['HTTP_CLIENT_IP'];
    } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $ip = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0];
    }

    return filter_var($ip, FILTER_VALIDATE_IP) ? $ip : '0.0.0.0';
}

/**
 * Log activity
 */
function logActivity($user_id, $action, $description = '', $entity_type = null, $entity_id = null) {
    global $conn;

    $ip = getClientIP();
    $user_agent = substr($_SERVER['HTTP_USER_AGENT'] ?? '', 0, 255);

    $stmt = $conn->prepare("INSERT INTO activity_logs (user_id, action, description, entity_type, entity_id, ip_address, user_agent) VALUES (?, ?, ?, ?, ?, ?, ?)");
    $stmt->bind_param("isssisd", $user_id, $action, $description, $entity_type, $entity_id, $ip, $user_agent);
    $stmt->execute();
}

/**
 * Check login attempts for rate limiting
 */
function checkLoginAttempts($email) {
    global $conn;

    $stmt = $conn->prepare("SELECT COUNT(*) as attempts FROM activity_logs WHERE action = 'login_failed' AND description LIKE ? AND created_at > DATE_SUB(NOW(), INTERVAL ? MINUTE)");
    $search = "%$email%";
    $minutes = LOGIN_LOCKOUT_MINUTES;
    $stmt->bind_param("si", $search, $minutes);
    $stmt->execute();
    $result = $stmt->get_result();
    $row = $result->fetch_assoc();

    return $row['attempts'] < MAX_LOGIN_ATTEMPTS;
}

/**
 * Record failed login attempt
 */
function recordFailedLogin($email) {
    global $conn;

    $ip = getClientIP();
    $description = "Failed login attempt for: $email";

    $stmt = $conn->prepare("INSERT INTO activity_logs (action, description, ip_address) VALUES ('login_failed', ?, ?)");
    $stmt->bind_param("ss", $description, $ip);
    $stmt->execute();
}

/**
 * Get current user data from session
 */
function currentUser() {
    if (!isAuthenticated()) {
        return null;
    }

    return [
        'id' => $_SESSION['user_id'],
        'email' => $_SESSION['email'],
        'full_name' => $_SESSION['full_name'],
        'company_name' => $_SESSION['company_name'] ?? '',
        'role' => $_SESSION['role']
    ];
}

/**
 * Generate URL with base path
 */
function url($path = '') {
    $path = ltrim($path, '/');
    return SITE_URL . '/' . $path;
}

/**
 * Redirect to a URL
 */
function redirect($path, $params = []) {
    $url = url($path);
    if (!empty($params)) {
        $url .= '?' . http_build_query($params);
    }
    header('Location: ' . $url);
    exit;
}

/**
 * Set flash message
 */
function setFlash($type, $message) {
    initSecureSession();
    $_SESSION['flash'][$type] = $message;
}

/**
 * Get and clear flash message
 */
function getFlash($type) {
    initSecureSession();
    if (isset($_SESSION['flash'][$type])) {
        $message = $_SESSION['flash'][$type];
        unset($_SESSION['flash'][$type]);
        return $message;
    }
    return null;
}

/**
 * Check if there's a flash message
 */
function hasFlash($type) {
    initSecureSession();
    return isset($_SESSION['flash'][$type]);
}
