<?php
declare(strict_types=1);

/**
 * /public_html/test_erp/config/session.php
 * Session Guard v2.1-TEST
 * Updated: 2026-07-22
 *
 * Test-specific behavior:
 * - Separate cookie name from live ERP.
 * - Host-only cookie; no wildcard domain sharing.
 * - Mobile IP changes do not force logout.
 * - User-Agent binding, idle timeout, absolute timeout and periodic ID rotation remain enabled.
 */

if (session_status() === PHP_SESSION_NONE) {
    $forwardedProto = strtolower((string)($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? ''));
    $secure = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
        || $forwardedProto === 'https';

    ini_set('session.use_only_cookies', '1');
    ini_set('session.use_strict_mode', '1');
    ini_set('session.cookie_httponly', '1');

    session_name('JEWELLERSTESTSESSID');

    // Empty domain creates a host-only cookie and prevents accidental sharing.
    session_set_cookie_params([
        'lifetime' => 0,
        'path'     => '/',
        'domain'   => '',
        'secure'   => $secure,
        'httponly' => true,
        'samesite' => 'Lax',
    ]);

    session_start();
}

$SESSION_IDLE_TIMEOUT     = 1800;   // 30 minutes inactivity
$SESSION_ABSOLUTE_TIMEOUT = 28800;  // 8 hours total
$SESSION_REGEN_INTERVAL   = 900;    // 15 minutes

function _erp_client_ip(): string
{
    foreach (['HTTP_CF_CONNECTING_IP', 'REMOTE_ADDR', 'HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP'] as $key) {
        if (empty($_SERVER[$key])) {
            continue;
        }

        $ip = trim(explode(',', (string)$_SERVER[$key])[0]);
        if (filter_var($ip, FILTER_VALIDATE_IP)) {
            return $ip;
        }
    }

    return '0.0.0.0';
}

function _erp_ua_hash(): string
{
    return hash('sha256', (string)($_SERVER['HTTP_USER_AGENT'] ?? ''));
}

function csrf_token(): string
{
    $token = (string)($_SESSION['csrf_token'] ?? '');
    if (!preg_match('/^[a-f0-9]{64}$/', $token)) {
        $token = bin2hex(random_bytes(32));
        $_SESSION['csrf_token'] = $token;
    }

    return $token;
}

function csrf_rotate_token(): string
{
    $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    return (string)$_SESSION['csrf_token'];
}

function _erp_force_logout(string $reason): void
{
    try {
        if (isset($GLOBALS['conn']) && !empty($_SESSION['user_id'])) {
            $conn = $GLOBALS['conn'];
            $uid  = (int)$_SESSION['user_id'];
            $ip   = _erp_client_ip();
            $note = 'Session force-logout: ' . $reason;

            $stmt = $conn->prepare(
                "INSERT INTO audit_log
                    (table_name, record_id, action, changed_by, changed_at, ip_address, note)
                 VALUES ('users', ?, 'LOGOUT', ?, NOW(), ?, ?)"
            );

            if ($stmt) {
                $stmt->bind_param('iiss', $uid, $uid, $ip, $note);
                $stmt->execute();
                $stmt->close();
            }
        }
    } catch (Throwable $e) {
        // Session cleanup must continue even if audit logging fails.
    }

    $_SESSION = [];

    if (ini_get('session.use_cookies')) {
        $params = session_get_cookie_params();
        setcookie(
            session_name(),
            '',
            time() - 42000,
            $params['path'] ?? '/',
            $params['domain'] ?? '',
            (bool)($params['secure'] ?? false),
            (bool)($params['httponly'] ?? true)
        );
    }

    session_destroy();

    http_response_code(401);
    header('Content-Type: application/json; charset=UTF-8');
    echo json_encode([
        'status'  => 401,
        'success' => false,
        'message' => 'সেশন বাতিল হয়েছে, আবার লগইন করুন',
        'reason'  => $reason,
        'data'    => null,
    ], JSON_UNESCAPED_UNICODE);
    exit;
}

if (!empty($_SESSION['user_id'])) {
    if (isset($_SESSION['LAST_ACTIVITY'])
        && (time() - (int)$_SESSION['LAST_ACTIVITY']) > $SESSION_IDLE_TIMEOUT) {
        _erp_force_logout('idle_timeout');
    }

    if (isset($_SESSION['LOGIN_TIME'])
        && (time() - (int)$_SESSION['LOGIN_TIME']) > $SESSION_ABSOLUTE_TIMEOUT) {
        _erp_force_logout('absolute_timeout');
    }

    $currentUa = _erp_ua_hash();
    if (empty($_SESSION['BIND_UA'])) {
        $_SESSION['BIND_UA'] = $currentUa;
    } elseif (!hash_equals((string)$_SESSION['BIND_UA'], $currentUa)) {
        _erp_force_logout('ua_mismatch');
    }

    // Mobile networks frequently change IP. Record the latest valid IP,
    // but do not terminate a legitimate session only because the IP changed.
    $currentIp = _erp_client_ip();
    if (empty($_SESSION['BIND_IP']) || $_SESSION['BIND_IP'] !== $currentIp) {
        $_SESSION['BIND_IP'] = $currentIp;
        $_SESSION['LAST_IP_CHANGE'] = time();
    }

    if (empty($_SESSION['LAST_REGEN'])
        || (time() - (int)$_SESSION['LAST_REGEN']) > $SESSION_REGEN_INTERVAL) {
        session_regenerate_id(true);
        $_SESSION['LAST_REGEN'] = time();
    }
}

$_SESSION['LAST_ACTIVITY'] = time();

function is_logged_in(): bool
{
    return !empty($_SESSION['user_id']);
}

function get_logged_in_user(): array
{
    return [
        'user_id'  => $_SESSION['user_id'] ?? null,
        'name'     => $_SESSION['user_name'] ?? null,
        'role'     => $_SESSION['user_role'] ?? ($_SESSION['role'] ?? null),
        'location' => $_SESSION['location_id'] ?? null,
    ];
}

function require_login(): void
{
    if (is_logged_in()) {
        return;
    }

    http_response_code(401);
    header('Content-Type: application/json; charset=UTF-8');
    echo json_encode([
        'status'  => 401,
        'success' => false,
        'message' => 'লগইন প্রয়োজন',
        'data'    => null,
    ], JSON_UNESCAPED_UNICODE);
    exit;
}
