<?php

declare(strict_types=1);

require __DIR__ . '/dev-db.php';

$baseUrl = rtrim((string)(getenv('SMOKE_BASE_URL') ?: 'http://127.0.0.1:8080'), '/');
$pdo = dev_pdo();
$suffix = bin2hex(random_bytes(4));
$password = 'RoleMatrixTest123!';
$roles = ['owner', 'admin', 'treasurer', 'member', 'viewer'];

// Seite => Rollen, die Zugriff haben sollen. Alle anderen Rollen muessen
// abgewiesen werden. Muss mit den saas_user_has_role()-Aufrufen in den
// jeweiligen Dateien uebereinstimmen.
$pages = [
    'kaffeeliste.php' => ['owner', 'admin', 'treasurer'],
    'mitarbeiterverwalten.php' => ['owner', 'admin'],
    'hinweise.php' => ['owner', 'admin'],
    'stricheintragen.php' => ['owner', 'admin', 'treasurer'],
    'einzahlung.php' => ['owner', 'admin', 'treasurer'],
    'letzteneintraege.php' => ['owner', 'admin', 'treasurer'],
    'csvupload.php' => ['owner', 'admin', 'treasurer'],
    'exportKaffeeliste.php' => ['owner', 'admin', 'treasurer'],
    'mailversenden.php' => ['owner', 'admin', 'treasurer'],
    'jahresauswertung.php' => ['owner', 'admin', 'treasurer'],
    'mandant-einstellungen.php' => ['owner', 'admin'],
];

$denialMarkers = ['kein zugriff', 'keine zugang', 'keine berechtigung'];

function m8rm_looks_denied(string $body): bool
{
    global $denialMarkers;
    $lower = strtolower($body);
    foreach ($denialMarkers as $marker) {
        if (str_contains($lower, $marker)) {
            return true;
        }
    }

    return false;
}

/**
 * Minimaler manueller Cookie-Jar auf Basis von file_get_contents, da diese
 * PHP-Installation keine curl-Extension hat. $cookies wird per Referenz
 * aktualisiert, sobald die Antwort Set-Cookie-Header enthaelt (insbesondere
 * nach dem Login, der die Session-ID per session_regenerate_id() erneuert).
 *
 * @param array<string,string> $cookies
 * @return array{status:int, body:string}
 */
function m8rm_request(string $url, array &$cookies, string $method = 'GET', ?string $postBody = null): array
{
    $cookieHeader = '';
    if ($cookies !== []) {
        $pairs = [];
        foreach ($cookies as $name => $value) {
            $pairs[] = "{$name}={$value}";
        }
        $cookieHeader = 'Cookie: ' . implode('; ', $pairs) . "\r\n";
    }

    $headers = "User-Agent: KaffeelisteRoleMatrix/1.0\r\n" . $cookieHeader;
    if ($postBody !== null) {
        $headers .= "Content-Type: application/x-www-form-urlencoded\r\n";
    }

    $context = stream_context_create([
        'http' => [
            'method' => $method,
            'header' => $headers,
            'content' => $postBody,
            'timeout' => 15,
            'ignore_errors' => true,
            'follow_location' => 0,
        ],
    ]);

    $body = @file_get_contents($url, false, $context);
    $responseHeaders = $http_response_header ?? [];
    $status = 0;

    foreach ($responseHeaders as $header) {
        if (preg_match('~^HTTP/\S+\s+(\d{3})~', $header, $m) === 1) {
            $status = (int)$m[1];
        }
        if (preg_match('/^Set-Cookie:\s*([^=;]+)=([^;]+)/i', $header, $m) === 1) {
            $cookies[$m[1]] = $m[2];
        }
    }

    return ['status' => $status, 'body' => (string)$body];
}

$tenantId = null;
$userIds = [];

try {
    $stmt = $pdo->prepare('INSERT INTO tenants (slug, name, status) VALUES (?, ?, ?)');
    $stmt->execute(["rolematrix-{$suffix}", 'Rollenmatrix Tenant', 'active']);
    $tenantId = (int)$pdo->lastInsertId();
    $pdo->prepare('INSERT INTO tenant_settings (tenant_id) VALUES (?)')->execute([$tenantId]);

    foreach ($roles as $role) {
        $email = "rolematrix-{$role}-{$suffix}@test.local";
        $stmt = $pdo->prepare(
            'INSERT INTO users (email, email_norm, display_name, password_hash, status) VALUES (?, ?, ?, ?, ?)'
        );
        $stmt->execute([$email, $email, "Rolematrix {$role}", password_hash($password, PASSWORD_DEFAULT), 'active']);
        $userId = (int)$pdo->lastInsertId();
        $userIds[$role] = $userId;

        $pdo->prepare(
            'INSERT INTO tenant_memberships (tenant_id, user_id, role, status, joined_at) VALUES (?, ?, ?, ?, NOW())'
        )->execute([$tenantId, $userId, $role, 'active']);

        $pdo->prepare(
            'INSERT INTO participants (tenant_id, user_id, display_name, email, email_norm, active) VALUES (?, ?, ?, ?, ?, 1)'
        )->execute([$tenantId, $userId, "Rolematrix {$role}", $email, $email]);
    }
} catch (Throwable $e) {
    fwrite(STDERR, "M8 role matrix setup failed: {$e->getMessage()}\n");
    exit(1);
}

$failures = [];
$passes = 0;

foreach ($roles as $role) {
    $cookies = [];

    $loginPage = m8rm_request("{$baseUrl}/login.php", $cookies);
    preg_match('/name="csrf_token" value="([^"]+)"/', $loginPage['body'], $matches);
    $csrf = $matches[1] ?? '';

    $postBody = http_build_query([
        'csrf_token' => $csrf,
        'email' => "rolematrix-{$role}-{$suffix}@test.local",
        'password' => $password,
        'tenant_slug' => "rolematrix-{$suffix}",
    ]);
    m8rm_request("{$baseUrl}/login.php", $cookies, 'POST', $postBody);

    foreach ($pages as $page => $allowedRoles) {
        $response = m8rm_request("{$baseUrl}/{$page}", $cookies);

        $shouldHaveAccess = in_array($role, $allowedRoles, true);
        $looksGranted = !m8rm_looks_denied($response['body']);

        $label = "{$role} auf {$page}";
        if ($shouldHaveAccess === $looksGranted) {
            $passes++;
            echo "PASS {$label}\n";
        } else {
            $failures[] = $shouldHaveAccess
                ? "{$label}: Zugriff erwartet, aber verweigert"
                : "{$label}: Zugriff verweigert erwartet, aber gewährt";
            echo "FAIL {$label}\n";
        }
    }
}

// Aufraeumen
$pdo->prepare('DELETE FROM participants WHERE tenant_id = ?')->execute([$tenantId]);
$pdo->prepare('DELETE FROM tenant_memberships WHERE tenant_id = ?')->execute([$tenantId]);
foreach ($userIds as $uid) {
    $pdo->prepare('DELETE FROM user_auth_tokens WHERE user_id = ?')->execute([$uid]);
    $pdo->prepare('DELETE FROM users WHERE id = ?')->execute([$uid]);
}
$pdo->prepare('DELETE FROM tenant_settings WHERE tenant_id = ?')->execute([$tenantId]);
$pdo->prepare('DELETE FROM tenants WHERE id = ?')->execute([$tenantId]);

if ($failures !== []) {
    echo "\nM8 role matrix check failed with " . count($failures) . " failure(s):\n";
    foreach ($failures as $failure) {
        echo "- {$failure}\n";
    }
    exit(1);
}

echo "\nM8 role matrix check passed with {$passes} assertions.\n";
