<?php
// Shared admin access guard: validates X-Admin-Key and enforces write rate limits.
require_once __DIR__ . '/../config.php';

function api_guard_json_response(array $data, int $code = 200): void
{
    http_response_code($code);
    echo json_encode($data, JSON_UNESCAPED_UNICODE);
    exit;
}

function api_guard_get_authorization_header(): string
{
    $header = (string) ($_SERVER['HTTP_AUTHORIZATION'] ?? '');
    if ($header !== '') {
        return $header;
    }

    if (function_exists('getallheaders')) {
        $headers = getallheaders();
        if (is_array($headers)) {
            foreach ($headers as $key => $value) {
                if (strtolower((string) $key) === 'authorization') {
                    return (string) $value;
                }
            }
        }
    }

    return '';
}

function api_guard_get_admin_key_from_request(): string
{
    $headerKey = trim((string) ($_SERVER['HTTP_X_ADMIN_KEY'] ?? ''));
    if ($headerKey !== '') {
        return $headerKey;
    }

    $authorization = trim(api_guard_get_authorization_header());
    if ($authorization !== '' && preg_match('/^Bearer\s+(.+)$/i', $authorization, $matches)) {
        return trim((string) ($matches[1] ?? ''));
    }

    return '';
}

function api_guard_get_client_ip(): string
{
    $forwarded = (string) ($_SERVER['HTTP_X_FORWARDED_FOR'] ?? '');
    if ($forwarded !== '') {
        $parts = explode(',', $forwarded);
        if (!empty($parts)) {
            return trim((string) $parts[0]);
        }
    }

    return (string) ($_SERVER['REMOTE_ADDR'] ?? 'unknown');
}

function api_guard_get_rate_limit_storage_path(): string
{
    $path = defined('ADMIN_RATE_LIMIT_STORAGE_FILE')
        ? (string) ADMIN_RATE_LIMIT_STORAGE_FILE
        : (__DIR__ . '/../logs/admin_rate_limit.json');

    $dir = dirname($path);
    if (!is_dir($dir)) {
        @mkdir($dir, 0755, true);
    }

    return $path;
}

function api_guard_require_admin_key(): string
{
    $configuredKey = defined('ADMIN_API_KEY') ? (string) ADMIN_API_KEY : '';
    if ($configuredKey === '') {
        api_guard_json_response([
            'success' => false,
            'message' => 'Admin key is not configured',
        ], 500);
    }

    $providedKey = api_guard_get_admin_key_from_request();
    if ($providedKey === '' || !hash_equals($configuredKey, $providedKey)) {
        header('WWW-Authenticate: X-Admin-Key');
        api_guard_json_response([
            'success' => false,
            'message' => 'Unauthorized',
        ], 401);
    }

    return $providedKey;
}

function api_guard_enforce_rate_limit(string $scope, string $adminKey): void
{
    $limit = defined('ADMIN_RATE_LIMIT_MAX_REQUESTS') ? (int) ADMIN_RATE_LIMIT_MAX_REQUESTS : 10;
    $windowSeconds = defined('ADMIN_RATE_LIMIT_WINDOW_SECONDS') ? (int) ADMIN_RATE_LIMIT_WINDOW_SECONDS : 60;

    if ($limit <= 0 || $windowSeconds <= 0) {
        return;
    }

    $storagePath = api_guard_get_rate_limit_storage_path();
    $fp = @fopen($storagePath, 'c+');
    if (!$fp) {
        return;
    }

    if (!flock($fp, LOCK_EX)) {
        fclose($fp);
        return;
    }

    $raw = stream_get_contents($fp);
    $state = [];
    if ($raw !== false && trim($raw) !== '') {
        $decoded = json_decode($raw, true);
        if (is_array($decoded)) {
            $state = $decoded;
        }
    }

    $now = time();
    $cleanupBefore = $now - ($windowSeconds * 3);
    foreach ($state as $key => $entry) {
        $windowStart = is_array($entry) ? (int) ($entry['window_start'] ?? 0) : 0;
        if ($windowStart < $cleanupBefore) {
            unset($state[$key]);
        }
    }

    $clientIp = api_guard_get_client_ip();
    $keyFingerprint = substr(hash('sha256', $adminKey), 0, 16);
    $bucket = hash('sha256', $scope . '|' . $clientIp . '|' . $keyFingerprint);

    $entry = is_array($state[$bucket] ?? null)
        ? $state[$bucket]
        : ['window_start' => $now, 'count' => 0];

    $windowStart = (int) ($entry['window_start'] ?? $now);
    $count = (int) ($entry['count'] ?? 0);

    if (($now - $windowStart) >= $windowSeconds) {
        $windowStart = $now;
        $count = 0;
    }

    if ($count >= $limit) {
        $retryAfter = max(1, $windowSeconds - ($now - $windowStart));
        header('Retry-After: ' . $retryAfter);

        $state[$bucket] = [
            'window_start' => $windowStart,
            'count' => $count,
        ];

        ftruncate($fp, 0);
        rewind($fp);
        fwrite($fp, json_encode($state, JSON_UNESCAPED_UNICODE));
        fflush($fp);
        flock($fp, LOCK_UN);
        fclose($fp);

        api_guard_json_response([
            'success' => false,
            'message' => 'Rate limit exceeded. Try again later.',
        ], 429);
    }

    $count++;
    $state[$bucket] = [
        'window_start' => $windowStart,
        'count' => $count,
    ];

    ftruncate($fp, 0);
    rewind($fp);
    fwrite($fp, json_encode($state, JSON_UNESCAPED_UNICODE));
    fflush($fp);
    flock($fp, LOCK_UN);
    fclose($fp);
}

function api_guard_require_admin_with_rate_limit(string $scope): string
{
    $adminKey = api_guard_require_admin_key();
    api_guard_enforce_rate_limit($scope, $adminKey);
    return $adminKey;
}
