<?php
// =============================================================================
// admin_security.php - shared administrator MFA, session, and audit helpers
// =============================================================================

function admin_base32_decode(string $secret): string {
    $alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
    $secret = strtoupper(preg_replace('/[^A-Z2-7]/i', '', $secret));
    $bits = '';
    $output = '';

    for ($i = 0, $len = strlen($secret); $i < $len; $i++) {
        $value = strpos($alphabet, $secret[$i]);
        if ($value === false) {
            continue;
        }
        $bits .= str_pad(decbin($value), 5, '0', STR_PAD_LEFT);
    }

    for ($i = 0, $len = strlen($bits); $i + 8 <= $len; $i += 8) {
        $output .= chr(bindec(substr($bits, $i, 8)));
    }

    return $output;
}

function admin_base32_encode(string $bytes): string {
    $alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
    $bits = '';
    $output = '';

    for ($i = 0, $len = strlen($bytes); $i < $len; $i++) {
        $bits .= str_pad(decbin(ord($bytes[$i])), 8, '0', STR_PAD_LEFT);
    }

    for ($i = 0, $len = strlen($bits); $i < $len; $i += 5) {
        $chunk = substr($bits, $i, 5);
        if (strlen($chunk) < 5) {
            $chunk = str_pad($chunk, 5, '0', STR_PAD_RIGHT);
        }
        $output .= $alphabet[bindec($chunk)];
    }

    return $output;
}

function admin_totp_secret(int $bytes = 20): string {
    return admin_base32_encode(random_bytes($bytes));
}

function admin_totp_uri(string $issuer, string $account, string $secret): string {
    $issuer = trim($issuer) !== '' ? trim($issuer) : 'SHT30 SMS';
    $account = trim($account) !== '' ? trim($account) : ADMIN_USER;
    $label = rawurlencode($issuer . ':' . $account);

    return 'otpauth://totp/' . $label
        . '?secret=' . rawurlencode(strtoupper(preg_replace('/[^A-Z2-7]/i', '', $secret)))
        . '&issuer=' . rawurlencode($issuer)
        . '&algorithm=SHA1'
        . '&digits=6'
        . '&period=30';
}

function admin_hotp(string $secret, int $counter, int $digits = 6): string {
    $key = admin_base32_decode($secret);
    if ($key === '') {
        return '';
    }

    $binaryCounter = pack('N2', intdiv($counter, 0x100000000), $counter & 0xffffffff);
    $hash = hash_hmac('sha1', $binaryCounter, $key, true);
    $offset = ord($hash[19]) & 0x0f;
    $value = (
        ((ord($hash[$offset]) & 0x7f) << 24) |
        ((ord($hash[$offset + 1]) & 0xff) << 16) |
        ((ord($hash[$offset + 2]) & 0xff) << 8) |
        (ord($hash[$offset + 3]) & 0xff)
    );

    return str_pad((string)($value % (10 ** $digits)), $digits, '0', STR_PAD_LEFT);
}

function admin_totp_code(string $secret, ?int $time = null, int $step = 30, int $digits = 6): string {
    $time = $time ?? time();
    return admin_hotp($secret, intdiv($time, $step), $digits);
}

function admin_totp_verify(string $secret, string $code, ?int $time = null, int $window = 1): bool {
    $code = preg_replace('/\D+/', '', $code);
    if ($secret === '' || !preg_match('/^\d{6}$/', $code)) {
        return false;
    }

    $time = $time ?? time();
    for ($i = -$window; $i <= $window; $i++) {
        $candidate = admin_totp_code($secret, $time + ($i * 30));
        if ($candidate !== '' && hash_equals($candidate, $code)) {
            return true;
        }
    }

    return false;
}

function admin_client_ip(): string {
    return filter_var($_SERVER['REMOTE_ADDR'] ?? '0.0.0.0', FILTER_VALIDATE_IP) ?: '0.0.0.0';
}

function admin_audit_log(string $event, string $username = '', bool $success = true, array $detail = []): void {
    $dir = __DIR__ . '/var';
    if (!is_dir($dir)) {
        @mkdir($dir, 0755, true);
    }

    $safeDetail = [];
    foreach ($detail as $key => $value) {
        if (is_scalar($value) || $value === null) {
            $safeDetail[(string)$key] = $value;
        }
    }

    $record = [
        'time' => date('c'),
        'event' => $event,
        'username' => substr($username, 0, 100),
        'success' => $success,
        'ip' => admin_client_ip(),
        'user_agent' => substr($_SERVER['HTTP_USER_AGENT'] ?? '', 0, 255),
        'detail' => $safeDetail,
    ];

    @file_put_contents(
        $dir . '/admin_audit.log',
        json_encode($record, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL,
        FILE_APPEND | LOCK_EX
    );
}

function admin_audit_log_path(): string {
    return __DIR__ . '/var/admin_audit.log';
}

function admin_mfa_configured(): bool {
    return defined('ADMIN_TOTP_SECRET') && ADMIN_TOTP_SECRET !== '';
}
