<?php
declare(strict_types=1);

if (defined('OG_SESSION_BOOTSTRAP_LOADED')) {
    return;
}

define('OG_SESSION_BOOTSTRAP_LOADED', true);
define('OG_SESSION_TIMEOUT_DEFAULT', 24 * 60 * 60);
define('OG_SESSION_TIMEOUT_REMEMBER', 7 * 24 * 60 * 60);

function og_session_is_secure_request(): bool
{
    if (!empty($_SERVER['HTTPS']) && strtolower((string) $_SERVER['HTTPS']) !== 'off') {
        return true;
    }

    if (isset($_SERVER['SERVER_PORT']) && (int) $_SERVER['SERVER_PORT'] === 443) {
        return true;
    }

    return false;
}

function og_session_cookie_options(int $lifetime): array
{
    return [
        'lifetime' => $lifetime,
        'path' => '/',
        'secure' => og_session_is_secure_request(),
        'httponly' => true,
        'samesite' => 'Lax',
    ];
}

function og_session_configure(int $timeout): void
{
    if (session_status() === PHP_SESSION_ACTIVE) {
        return;
    }

    ini_set('session.gc_maxlifetime', (string) $timeout);
    ini_set('session.cookie_httponly', '1');
    ini_set('session.use_strict_mode', '1');

    if (PHP_VERSION_ID >= 70300) {
        session_set_cookie_params(og_session_cookie_options($timeout));
        return;
    }

    $path = '/; samesite=Lax';
    session_set_cookie_params($timeout, $path, '', og_session_is_secure_request(), true);
}

function og_session_get_pdo(): ?PDO
{
    static $pdo = null;

    if ($pdo instanceof PDO) {
        return $pdo;
    }

    try {
        $pdo = new PDO(
            'mysql:host=localhost;dbname=togethere_cloud;charset=utf8mb4',
            'root',
            'HasloDoSQL',
            [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            ]
        );
        $pdo->exec('SET NAMES utf8mb4 COLLATE utf8mb4_unicode_ci');
        return $pdo;
    } catch (Throwable $e) {
        return null;
    }
}

function og_session_ensure_remember_tokens_table(?PDO $pdo = null): bool
{
    $pdo = $pdo ?: og_session_get_pdo();
    if (!$pdo instanceof PDO) {
        return false;
    }

    $pdo->exec(
        'CREATE TABLE IF NOT EXISTS remember_tokens (
            id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
            user_id INT NOT NULL,
            token VARCHAR(255) NOT NULL,
            expires_at DATETIME NOT NULL,
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
            UNIQUE KEY uniq_remember_token (token),
            KEY idx_remember_user (user_id),
            KEY idx_remember_expires (expires_at)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci'
    );

    return true;
}

function og_session_remember_cookie_value(): string
{
    return isset($_COOKIE['remember_token']) ? trim((string) $_COOKIE['remember_token']) : '';
}

function og_session_uses_remember_me(): bool
{
    if (!empty($_SESSION['remember_me'])) {
        return true;
    }

    return og_session_remember_cookie_value() !== '';
}

function og_session_timeout_seconds(): int
{
    return og_session_uses_remember_me() ? OG_SESSION_TIMEOUT_REMEMBER : OG_SESSION_TIMEOUT_DEFAULT;
}

function og_session_refresh_cookie(string $name, string $value, int $lifetime): void
{
    if (headers_sent()) {
        return;
    }

    $expiresAt = time() + $lifetime;

    if (PHP_VERSION_ID >= 70300) {
        $options = og_session_cookie_options($lifetime);
        $options['expires'] = $expiresAt;
        setcookie($name, $value, $options);
        return;
    }

    setcookie($name, $value, $expiresAt, '/; samesite=Lax', '', og_session_is_secure_request(), true);
}

function og_session_clear_cookie(string $name): void
{
    if (headers_sent()) {
        return;
    }

    if (PHP_VERSION_ID >= 70300) {
        $options = og_session_cookie_options(0);
        $options['expires'] = time() - 3600;
        setcookie($name, '', $options);
        return;
    }

    setcookie($name, '', time() - 3600, '/; samesite=Lax', '', og_session_is_secure_request(), true);
}

function og_session_refresh_remember_token(): void
{
    if (empty($_SESSION['remember_me'])) {
        return;
    }

    $token = og_session_remember_cookie_value();
    if ($token === '') {
        return;
    }

    $pdo = og_session_get_pdo();
    if (!$pdo instanceof PDO || !og_session_ensure_remember_tokens_table($pdo)) {
        return;
    }

    $expiresAt = date('Y-m-d H:i:s', time() + OG_SESSION_TIMEOUT_REMEMBER);
    $stmt = $pdo->prepare('UPDATE remember_tokens SET expires_at = :expires WHERE token = :token');
    $stmt->execute([
        ':expires' => $expiresAt,
        ':token' => hash('sha256', $token),
    ]);

    og_session_refresh_cookie('remember_token', $token, OG_SESSION_TIMEOUT_REMEMBER);
}

function og_session_clear_remember_token(): void
{
    $token = og_session_remember_cookie_value();

    if ($token !== '') {
        $pdo = og_session_get_pdo();
        if ($pdo instanceof PDO && og_session_ensure_remember_tokens_table($pdo)) {
            $stmt = $pdo->prepare('DELETE FROM remember_tokens WHERE token = :token');
            $stmt->execute([':token' => hash('sha256', $token)]);
        }
    }

    og_session_clear_cookie('remember_token');
}

function og_session_destroy_auth(bool $clearRememberToken = false): void
{
    $_SESSION = [];

    if ($clearRememberToken) {
        og_session_clear_remember_token();
    }

    if (session_status() === PHP_SESSION_ACTIVE) {
        if (!headers_sent()) {
            og_session_clear_cookie(session_name());
        }
        session_destroy();
    }
}

function og_session_find_remember_user(PDO $pdo, string $token): ?array
{
    if ($token === '') {
        return null;
    }

    if (!og_session_ensure_remember_tokens_table($pdo)) {
        return null;
    }

    $stmt = $pdo->prepare(
        'SELECT u.id, u.username, u.email, COALESCE(u.role, "user") AS role
         FROM remember_tokens rt
         INNER JOIN users u ON u.id = rt.user_id
         WHERE rt.token = :token
           AND rt.expires_at > NOW()
         LIMIT 1'
    );
    $stmt->execute([':token' => hash('sha256', $token)]);
    $row = $stmt->fetch();

    return is_array($row) ? $row : null;
}

function og_session_restore_from_remember_cookie(): void
{
    if (!empty($_SESSION['logged_in']) && !empty($_SESSION['user_id'])) {
        return;
    }

    $token = og_session_remember_cookie_value();
    if ($token === '') {
        return;
    }

    $pdo = og_session_get_pdo();
    if (!$pdo instanceof PDO) {
        return;
    }

    $user = og_session_find_remember_user($pdo, $token);
    if (!$user) {
        og_session_clear_remember_token();
        return;
    }

    session_regenerate_id(true);
    $_SESSION['logged_in'] = true;
    $_SESSION['user_id'] = (int) $user['id'];
    $_SESSION['username'] = (string) $user['username'];
    $_SESSION['email'] = (string) ($user['email'] ?? '');
    $_SESSION['role'] = (string) ($user['role'] ?? 'user');
    $_SESSION['remember_me'] = true;
    $_SESSION['last_activity'] = time();

    og_session_refresh_remember_token();
}

function og_session_enforce_inactivity_timeout(): void
{
    if (empty($_SESSION['logged_in']) || empty($_SESSION['user_id'])) {
        return;
    }

    $lastActivity = isset($_SESSION['last_activity']) ? (int) $_SESSION['last_activity'] : 0;
    if ($lastActivity <= 0) {
        return;
    }

    if ((time() - $lastActivity) > og_session_timeout_seconds()) {
        og_session_destroy_auth(og_session_uses_remember_me());
    }
}

function og_session_touch(): void
{
    if (empty($_SESSION['logged_in']) || empty($_SESSION['user_id'])) {
        return;
    }

    $_SESSION['last_activity'] = time();
    og_session_refresh_cookie(session_name(), session_id(), og_session_timeout_seconds());
    og_session_refresh_remember_token();
}

$preSessionTimeout = og_session_remember_cookie_value() !== ''
    ? OG_SESSION_TIMEOUT_REMEMBER
    : OG_SESSION_TIMEOUT_DEFAULT;

og_session_configure($preSessionTimeout);

if (session_status() !== PHP_SESSION_ACTIVE) {
    session_start();
}

og_session_restore_from_remember_cookie();
og_session_enforce_inactivity_timeout();
og_session_touch();