<?php
/* =============================================================================
 * File   : /brocrm/mobile/remember_me_v2.php
 * Goal   : Remember-me without prepared statements (NO PDO), mysqli only.
 * Notes  : - Uses HEX literals for binary (selector/token_hash/ip) so no bind_param.
 *          - Auto-creates the tokens table with utf8mb4_general_ci.
 *          - Exposes rm2_issue / rm2_autologin / rm2_clear_all public APIs.
 * DB     : Connection comes from config.php (mysqli).
 * ============================================================================= */

declare(strict_types=1);

/* -------- Table location --------
 * If cross-DB privileges are limited, set to 'auth_remember_tokens'
 * to use the CURRENT database from config.php.
 */
if (!defined('AUTH_REMEMBER_TABLE_V2')) {
    define('AUTH_REMEMBER_TABLE_V2', 'broriserin_broriserin.auth_remember_tokens');
}

/* ---------- helpers ---------- */
function rm2_cookie_set(string $name, string $value, int $days = 365): void {
    $p       = session_get_cookie_params();
    $expire  = time() + (86400 * $days);
    $isHttps = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || ((int)($_SERVER['SERVER_PORT'] ?? 80) === 443);
    setcookie($name, $value, [
        'expires'  => $expire,
        'path'     => $p['path']   ?? '/',
        'domain'   => $p['domain'] ?? '',
        'secure'   => $isHttps,
        'httponly' => true,
        'samesite' => 'Lax',
    ]);
}
function rm2_cookie_clear(string $name): void {
    $p       = session_get_cookie_params();
    $isHttps = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || ((int)($_SERVER['SERVER_PORT'] ?? 80) === 443);
    setcookie($name, '', [
        'expires'  => time() - 3600,
        'path'     => $p['path']   ?? '/',
        'domain'   => $p['domain'] ?? '',
        'secure'   => $isHttps,
        'httponly' => true,
        'samesite' => 'Lax',
    ]);
}
function rm2_ip_bin(): ?string {
    $ip = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? ($_SERVER['REMOTE_ADDR'] ?? '');
    $ip = trim(explode(',', $ip)[0]);
    $bin = @inet_pton($ip);
    return $bin !== false ? $bin : null; // VARBINARY(16) or NULL
}
function rm2_hex(?string $bin): ?string { return is_string($bin) ? bin2hex($bin) : null; }
function rm2_table_sql(): string {
    $t = AUTH_REMEMBER_TABLE_V2;
    if (strpos($t, '.') !== false) {
        [$db, $tbl] = explode('.', $t, 2);
        $db  = preg_replace('/[^A-Za-z0-9_]/', '', $db);
        $tbl = preg_replace('/[^A-Za-z0-9_]/', '', $tbl);
        return "`{$db}`.`{$tbl}`";
    }
    $tbl = preg_replace('/[^A-Za-z0-9_]/', '', $t);
    return "`{$tbl}`";
}

/* ---------- ensure table exists ---------- */
function rm2_touch_schema(mysqli $db): void {
    @mysqli_set_charset($db, 'utf8mb4');
    $T = rm2_table_sql();
    $ddl = "
        CREATE TABLE IF NOT EXISTS {$T} (
          `id`           INT UNSIGNED NOT NULL AUTO_INCREMENT,
          `user_id`      INT UNSIGNED NOT NULL,
          `selector`     VARBINARY(12)  NOT NULL,
          `token_hash`   VARBINARY(32)  NOT NULL,
          `user_agent`   VARCHAR(255)   NULL,
          `ip_address`   VARBINARY(16)  NULL,
          `expires_at`   DATETIME       NOT NULL,
          `created_at`   DATETIME       NOT NULL DEFAULT CURRENT_TIMESTAMP,
          `last_used_at` DATETIME       NOT NULL DEFAULT CURRENT_TIMESTAMP,
          PRIMARY KEY (`id`),
          UNIQUE KEY `uniq_selector` (`selector`),
          KEY `idx_user_id` (`user_id`),
          KEY `idx_expires` (`expires_at`)
        ) ENGINE=InnoDB
          DEFAULT CHARSET=utf8mb4
          COLLATE=utf8mb4_general_ci;";
    if (!$db->query($ddl)) { error_log('RM2 DDL failed: '.$db->error); }
}

/* ---------- public API (NO bind_param anywhere) ---------- */
function rm2_issue(mysqli $db, int $user_id, int $days = 365): void {
    if ($user_id <= 0) { error_log('RM2 issue: invalid user_id'); return; }
    if (!$db->ping()) { @require __DIR__ . '/config.php'; }
    rm2_touch_schema($db);

    $selector   = random_bytes(12);                 // binary
    $validator  = random_bytes(32);                 // binary (cookie only)
    $tokenHash  = hash('sha256', $validator, true); // binary
    $ua         = substr($_SERVER['HTTP_USER_AGENT'] ?? '', 0, 255);
    $ipb        = rm2_ip_bin();
    $expires    = (new DateTimeImmutable("+{$days} days"))->format('Y-m-d H:i:s');

    $T      = rm2_table_sql();
    $uid    = (int)$user_id;
    $ua_sql = "'" . $db->real_escape_string($ua) . "'";
    $expSql = "'" . $db->real_escape_string($expires) . "'";
    $selHex = rm2_hex($selector);
    $tokHex = rm2_hex($tokenHash);
    $ipHex  = rm2_hex($ipb);

    $sql = "INSERT INTO {$T}
              (user_id, selector, token_hash, user_agent, ip_address, expires_at, created_at, last_used_at)
            VALUES (
              {$uid},
              x'{$selHex}',
              x'{$tokHex}',
              {$ua_sql},
              " . ($ipHex ? "x'{$ipHex}'" : "NULL") . ",
              {$expSql},
              NOW(),
              NOW()
            )";
    if (!$db->query($sql)) { error_log('RM2 INSERT failed: '.$db->error.' | '.$sql); return; }

    rm2_cookie_set('rm', base64_encode($selector).'|'.base64_encode($validator), $days);
}

function rm2_clear_all(mysqli $db, int $user_id): void {
    rm2_touch_schema($db);
    $T = rm2_table_sql();
    $uid = (int)$user_id;
    if ($uid > 0) {
        $sql = "DELETE FROM {$T} WHERE user_id = {$uid}";
        if (!$db->query($sql)) { error_log('RM2 CLEAR failed: '.$db->error.' | '.$sql); }
    }
    rm2_cookie_clear('rm');
}

function rm2_autologin(mysqli $db, callable $loadUserById): void {
    if (!empty($_SESSION['auth'])) return;
    if (!$db->ping()) { @require __DIR__ . '/config.php'; }

    $cookie = $_COOKIE['rm'] ?? '';
    if (!$cookie || strpos($cookie, '|') === false) return;

    [$selB64, $valB64] = explode('|', $cookie, 2);
    $selector  = base64_decode($selB64, true);
    $validator = base64_decode($valB64, true);
    if ($selector === false || $validator === false) return;

    rm2_touch_schema($db);
    $T      = rm2_table_sql();
    $selHex = rm2_hex($selector);

    $sql = "SELECT id, user_id, token_hash, expires_at
              FROM {$T}
             WHERE selector = x'{$selHex}'
             LIMIT 1";
    $res = $db->query($sql);
    if (!$res) { error_log('RM2 SELECT failed: '.$db->error.' | '.$sql); return; }
    $row = $res->fetch_assoc(); $res->free();
    if (!$row) return;

    if (new DateTimeImmutable($row['expires_at']) < new DateTimeImmutable('now')) {
        $db->query("DELETE FROM {$T} WHERE id = ".(int)$row['id']);
        rm2_cookie_clear('rm'); return;
    }

    $calc = hash('sha256', $validator, true);
    if (!hash_equals($row['token_hash'], $calc)) {
        $db->query("DELETE FROM {$T} WHERE id = ".(int)$row['id']);
        rm2_cookie_clear('rm'); return;
    }

    $user = $loadUserById($db, (int)$row['user_id']);
    if (!$user) {
        $db->query("DELETE FROM {$T} WHERE id = ".(int)$row['id']);
        rm2_cookie_clear('rm'); return;
    }

    if (empty($_SESSION['auth']['logged_in'])) {
        if (function_exists('sa_start_session')) {
            sa_start_session($user, null);
        } else {
            $_SESSION['auth'] = [
                'id'        => (int)($user['id'] ?? 0),
                'name'      => trim(($user['first_name'] ?? '').' '.($user['last_name'] ?? '')),
                'email'     => $user['email'] ?? '',
                'role_id'   => (int)($user['role_id'] ?? 0),
                'role_name' => $user['role_name'] ?? '',
                'logged_in' => true,
                'time'      => time(),
            ];
        }
    }

    $newValidator = random_bytes(32);
    $newHashHex   = rm2_hex(hash('sha256', $newValidator, true));
    $rid          = (int)$row['id'];
    $upd = "UPDATE {$T} SET token_hash = x'{$newHashHex}', last_used_at = NOW() WHERE id = {$rid}";
    if (!$db->query($upd)) { error_log('RM2 UPDATE failed: '.$db->error.' | '.$upd); }

    rm2_cookie_set('rm', base64_encode($selector).'|'.base64_encode($newValidator), 365);
}
