<?php
/*
 * package examples/crash_reporter/backend/public/index.php
 * index.php
 * Created at: Wed 20 May 2026 14:31:55 +0200
 * Updated at: Wed 20 May 2026 15:17:13 +0200 by Anton Afanasyeu <a.afanasieff@gmail.com>
 * Commit: 5d8e82d2e60a21fff3138d2a394ee4e8b4c6dcb8
 * Contributors:
 *    - Anton Afanasyeu <a.afanasieff@gmail.com> (2 commits, 63 lines)
 *    - Cursor Agent (project assistant)
 * Digest: SHA256	90d9b287d10572f80696355281ba308437d49936dba1d421037afbabc1dc135e
 */
declare(strict_types=1);
require_once __DIR__ . '/../src/bootstrap.php';

$uri = parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) ?: '/';
$base = Auth::basePath();
$route = resolve_console_route($uri);

if ($route === '/api/upload.php' || str_ends_with($route, '/api/upload.php')) {
    require __DIR__ . '/api/upload.php';
    exit;
}

if ($route === '/api/diag.php' || str_ends_with($route, '/api/diag.php')) {
    require __DIR__ . '/api/diag.php';
    exit;
}

if ($route === '/api/heartbeat.php' || str_ends_with($route, '/api/heartbeat.php')) {
    require __DIR__ . '/api/heartbeat.php';
    exit;
}

if ($route === '/api/reports.php' || str_ends_with($route, '/api/reports.php')) {
    require __DIR__ . '/api/reports.php';
    exit;
}

if ($route === '/api/report_viewed.php' || str_ends_with($route, '/api/report_viewed.php')) {
    require __DIR__ . '/api/report_viewed.php';
    exit;
}

if ($route === '/api/report_tags.php' || str_ends_with($route, '/api/report_tags.php')) {
    require __DIR__ . '/api/report_tags.php';
    exit;
}

if ($route === '/api/ticket_upload.php' || str_ends_with($route, '/api/ticket_upload.php')) {
    require __DIR__ . '/api/ticket_upload.php';
    exit;
}

if ($route === '/api/ticket_create.php' || str_ends_with($route, '/api/ticket_create.php')) {
    require __DIR__ . '/api/ticket_create.php';
    exit;
}

if ($route === '/api/tickets.php' || str_ends_with($route, '/api/tickets.php')) {
    require __DIR__ . '/api/tickets.php';
    exit;
}

if ($route === '/api/ticket_tags.php' || str_ends_with($route, '/api/ticket_tags.php')) {
    require __DIR__ . '/api/ticket_tags.php';
    exit;
}

if ($route === '/api/ticket_update.php' || str_ends_with($route, '/api/ticket_update.php')) {
    require __DIR__ . '/api/ticket_update.php';
    exit;
}

if ($route === '/api/ticket_comments.php' || str_ends_with($route, '/api/ticket_comments.php')) {
    require __DIR__ . '/api/ticket_comments.php';
    exit;
}

if ($route === '/api/users.php' || str_ends_with($route, '/api/users.php')) {
    require __DIR__ . '/api/users.php';
    exit;
}

if ($route === '/api/ticket_attachments.php' || str_ends_with($route, '/api/ticket_attachments.php')) {
    require __DIR__ . '/api/ticket_attachments.php';
    exit;
}

if ($route === '/api/graphs.php' || str_ends_with($route, '/api/graphs.php') || str_ends_with($route, '/graphs/api/graphs.php')) {
    require __DIR__ . '/api/graphs.php';
    exit;
}

if ($route === '/api/graph_upload.php' || str_ends_with($route, '/api/graph_upload.php') || str_ends_with($route, '/graphs/api/graph_upload.php')) {
    require __DIR__ . '/api/graph_upload.php';
    exit;
}

if ($route === '/api/remote_access.php' || str_ends_with($route, '/api/remote_access.php')) {
    require __DIR__ . '/api/remote_access.php';
    exit;
}

if ($route === '/api/live_cast.php' || str_ends_with($route, '/api/live_cast.php')) {
    require __DIR__ . '/api/live_cast.php';
    exit;
}

if ($route === '/api/live_cast_signaling.php' || str_ends_with($route, '/api/live_cast_signaling.php')) {
    require __DIR__ . '/api/live_cast_signaling.php';
    exit;
}

if ($route === '/live/join' || str_ends_with($route, '/live/join')) {
    require __DIR__ . '/../views/live_join.php';
    exit;
}

if ($route === '/live/education' || str_ends_with($route, '/live/education')) {
    require __DIR__ . '/../views/live_education.php';
    exit;
}

if ($route === '/api/short_links.php' || str_ends_with($route, '/api/short_links.php')) {
    require __DIR__ . '/api/short_links.php';
    exit;
}

if ($route === '/api/rbac.php' || str_ends_with($route, '/api/rbac.php')) {
    require __DIR__ . '/api/rbac.php';
    exit;
}

if ($route === '/api/ticket_attachment.php' || str_ends_with($route, '/api/ticket_attachment.php')) {
    require __DIR__ . '/api/ticket_attachment.php';
    exit;
}

if ($route === '/api/hub_deploy_docs.php' || str_ends_with($route, '/api/hub_deploy_docs.php')) {
    require __DIR__ . '/api/hub_deploy_docs.php';
    exit;
}

if ($route === '/api/tag_catalog.php' || str_ends_with($route, '/api/tag_catalog.php')) {
    require __DIR__ . '/api/tag_catalog.php';
    exit;
}

if ($route === '/api/auth_register.php' || str_ends_with($route, '/api/auth_register.php')) {
    require __DIR__ . '/api/auth_register.php';
    exit;
}

if ($route === '/logout') {
    Auth::logout();
    header('Location: ' . Auth::authUrl('/login'));
    exit;
}

if ($route === '/login' && $_SERVER['REQUEST_METHOD'] === 'POST') {
    $user = trim($_POST['username'] ?? '');
    $pass = $_POST['password'] ?? '';
    $result = Auth::login($user, $pass);
    if ($result === true) {
        header('Location: ' . $base . '/');
        exit;
    }
    if ($result === 'pending_2fa') {
        header('Location: ' . Auth::authUrl('/two-factor'));
        exit;
    }
    $loginError = 'Invalid credentials';
    $st = Database::pdo()->prepare('SELECT status FROM users WHERE username = ? LIMIT 1');
    $st->execute([$user]);
    $row = $st->fetch(PDO::FETCH_ASSOC);
    if (is_array($row) && ($row['status'] ?? '') === 'pending') {
        $loginError = 'Verify your email before signing in.';
    } elseif (AuthAttempts::isRateLimited($user)) {
        $loginError = 'Too many attempts — try again later or ask an admin to clear lockouts.';
    }
    require __DIR__ . '/../views/login.php';
    exit;
}

if ($route === '/login') {
    if (Auth::pending2faUserId() > 0) {
        header('Location: ' . Auth::authUrl('/two-factor'));
        exit;
    }
    require __DIR__ . '/../views/login.php';
    exit;
}

if ($route === '/register' && $_SERVER['REQUEST_METHOD'] === 'POST') {
    $email = trim($_POST['email'] ?? '');
    $username = trim($_POST['username'] ?? '');
    $password = $_POST['password'] ?? '';
    $registerEmail = $email;
    $registerUsername = $username;
    $out = AuthRegistration::register($email, $password, $username);
    if ($out['ok']) {
        $registerSuccess = 'Check your email for a verification link.';
        require __DIR__ . '/../views/register.php';
        exit;
    }
    $registerError = match ($out['error'] ?? '') {
        'invalid_email' => 'Enter a valid email address.',
        'weak_password' => 'Password must be at least 10 characters.',
        'already_registered' => 'An account with this email or username already exists.',
        'rate_limited' => 'Too many attempts — try again later.',
        default => 'Registration failed.',
    };
    require __DIR__ . '/../views/register.php';
    exit;
}

if ($route === '/register') {
    require __DIR__ . '/../views/register.php';
    exit;
}

if ($route === '/verify-email') {
    $token = trim($_GET['token'] ?? '');
    $result = AuthRegistration::verifyEmailToken($token);
    $verifyOk = $result['ok'] ?? false;
    if (!$verifyOk) {
        $verifyError = match ($result['error'] ?? '') {
            'expired_token' => 'This link has expired.',
            'invalid_token' => 'Invalid verification link.',
            default => 'Verification failed.',
        };
    }
    require __DIR__ . '/../views/verify_email.php';
    exit;
}

if ($route === '/two-factor' && $_SERVER['REQUEST_METHOD'] === 'POST') {
    if (Auth::pending2faUserId() <= 0) {
        header('Location: ' . Auth::authUrl('/login'));
        exit;
    }
    $code = trim($_POST['code'] ?? '');
    if (Auth::completeTotpLogin($code)) {
        header('Location: ' . $base . '/');
        exit;
    }
    $twofaError = 'Invalid code';
    require __DIR__ . '/../views/two_factor_challenge.php';
    exit;
}

if ($route === '/two-factor') {
    if (Auth::pending2faUserId() <= 0) {
        header('Location: ' . Auth::authUrl('/login'));
        exit;
    }
    $twofaQr = AuthTwoFactorPage::qrPayload();
    require __DIR__ . '/../views/two_factor_challenge.php';
    exit;
}

if ($route === '/account-security' && $_SERVER['REQUEST_METHOD'] === 'POST') {
    Auth::check();
    $user = Auth::user();
    $uid = (int) ($user['id'] ?? 0);
    $action = (string) ($_POST['action'] ?? '');
    if ($action === 'start_totp') {
        if (AuthFactors::hasTotp($uid)) {
            $securityError = 'Remove the current authenticator before enrolling a new one.';
        } else {
            $secret = AuthTotp::generateSecret();
            $_SESSION['totp_enroll_secret'] = $secret;
            $_SESSION['totp_enroll_exp'] = time() + 900;
        }
    } elseif ($action === 'confirm_totp') {
        if (AuthFactors::hasTotp($uid)) {
            unset($_SESSION['totp_enroll_secret'], $_SESSION['totp_enroll_exp']);
            $securityError = 'Authenticator is already enrolled.';
        } else {
            $secret = (string) ($_SESSION['totp_enroll_secret'] ?? '');
            $exp = (int) ($_SESSION['totp_enroll_exp'] ?? 0);
            $code = trim($_POST['code'] ?? '');
            if ($secret === '' || $exp < time()) {
                unset($_SESSION['totp_enroll_secret'], $_SESSION['totp_enroll_exp']);
                $securityError = 'Setup expired — start again.';
            } elseif (!AuthTotp::verify($secret, $code, 2)) {
                $securityError = 'Invalid code — check device time and try again.';
            } else {
                unset($_SESSION['totp_enroll_secret'], $_SESSION['totp_enroll_exp']);
                AuthFactors::enrollTotp($uid, $secret);
                $securitySuccess = 'Authenticator enrolled.';
            }
        }
    } elseif ($action === 'remove_totp') {
        AuthFactors::removeTotp($uid);
        $securitySuccess = 'Authenticator removed.';
    }
    if (!empty($securityError)) {
        $_SESSION['security_flash_error'] = $securityError;
    }
    if (!empty($securitySuccess)) {
        $_SESSION['security_flash_success'] = $securitySuccess;
    }
    header('Location: ' . Auth::basePath() . '/account-security', true, 303);
    exit;
}

if ($route === '/account-security') {
    Auth::check();
    $user = Auth::user();
    $uid = (int) ($user['id'] ?? 0);
    $securityError = (string) ($_SESSION['security_flash_error'] ?? '');
    $securitySuccess = (string) ($_SESSION['security_flash_success'] ?? '');
    unset($_SESSION['security_flash_error'], $_SESSION['security_flash_success']);
    $totpSecret = '';
    if (!AuthFactors::hasTotp($uid)) {
        $exp = (int) ($_SESSION['totp_enroll_exp'] ?? 0);
        if ($exp >= time()) {
            $totpSecret = (string) ($_SESSION['totp_enroll_secret'] ?? '');
        }
    }
    require __DIR__ . '/../views/account_security.php';
    exit;
}

if ($route === '/graphs' || $route === '/graphs/' || str_ends_with($route, '/app/androidcast_project/graphs') || str_ends_with($route, '/app/androidcast_project/graphs/')) {
    $_GET['view'] = 'graphs';
    $route = '/';
}

Auth::check();

$grouped = isset($_GET['group']) && $_GET['group'] === '1';
$view = $_GET['view'] ?? 'home';

if ($view === 'short_links' && !Rbac::can('short_links_view')) {
    http_response_code(403);
    echo 'Forbidden';
    exit;
}

if ($view === 'report' && isset($_GET['id'])) {
    try {
        $report = ReportRepository::getById((int) $_GET['id']);
        if (!$report) {
            http_response_code(404);
            echo 'Not found';
            exit;
        }
        $uid = (int) (Auth::user()['id'] ?? 0);
        if ($uid > 0) {
            try {
                ReportRepository::markViewed((int) $report['id'], $uid);
            } catch (Throwable $e) {
                error_log('markViewed: ' . $e->getMessage());
            }
        }
        $pageTitle = 'Report';
        require __DIR__ . '/../views/layout.php';
    } catch (Throwable $e) {
        error_log('report view: ' . $e->getMessage());
        http_response_code(500);
        if (cfg('debug')) {
            header('Content-Type: text/plain; charset=utf-8');
            echo 'Report view failed: ' . $e->getMessage();
        } else {
            echo 'Report view failed';
        }
    }
    exit;
}

if ($view === 'ticket' && isset($_GET['id'])) {
    try {
        Database::requireTicketsTable();
        $ticket = TicketRepository::getById((int) $_GET['id']);
        if (!$ticket) {
            http_response_code(404);
            echo 'Not found';
            exit;
        }
        $pageTitle = 'Ticket';
        require __DIR__ . '/../views/layout.php';
    } catch (Throwable $e) {
        error_log('ticket view: ' . $e->getMessage());
        http_response_code(500);
        echo cfg('debug') ? 'Ticket view failed: ' . $e->getMessage() : 'Ticket view failed';
    }
    exit;
}

$pageTitle = match ($view) {
    'home' => 'Home',
    'tickets' => 'Tickets',
    'graphs' => 'Analytics',
    'live_sessions' => 'Live sessions',
    'remote_access' => 'Remote access',
    'short_links' => 'Short links',
    'rbac' => 'Access control',
    'reports', 'report' => 'Issues',
    default => 'Console',
};
require __DIR__ . '/../views/layout.php';
