<?php

declare(strict_types=1);

use Budget\ExportService;
use Budget\Mailgun;
use Budget\ReminderService;
use Budget\Support;
use Budget\Totp;

require __DIR__ . '/../app/bootstrap.php';

if (!class_exists('Support', false)) {
    class_alias(Support::class, 'Support');
}

function h(mixed $value): string
{
    return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
}

function url(string $route, array $parameters = []): string
{
    return '?' . http_build_query(['route' => $route] + $parameters);
}

function csrf_field(): string
{
    return '<input type="hidden" name="csrf_token" value="' . h(Support::csrf()) . '">';
}

function icon(string $name, string $class = ''): string
{
    $paths = [
        'home' => '<path d="M3 10.8 12 3l9 7.8"/><path d="M5.5 9.5V21h13V9.5M9 21v-7h6v7"/>',
        'expense' => '<path d="M4 7h16M4 12h16M4 17h10"/><path d="m17 15 3 3-3 3"/>',
        'calendar' => '<rect x="3" y="5" width="18" height="16" rx="2"/><path d="M16 3v4M8 3v4M3 10h18"/>',
        'chart' => '<path d="M4 19V9M10 19V5M16 19v-7M22 19H2"/>',
        'vault' => '<rect x="3" y="4" width="18" height="16" rx="3"/><circle cx="12" cy="12" r="3"/><path d="M12 9V7M12 17v-2M9 12H7M17 12h-2"/>',
        'settings' => '<circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.9l.1.1-2.8 2.8-.1-.1a1.7 1.7 0 0 0-1.9-.3 1.7 1.7 0 0 0-1 1.6v.2h-4V21a1.7 1.7 0 0 0-1-1.6 1.7 1.7 0 0 0-1.9.3l-.1.1L4.2 17l.1-.1a1.7 1.7 0 0 0 .3-1.9A1.7 1.7 0 0 0 3 14H2.8v-4H3a1.7 1.7 0 0 0 1.6-1 1.7 1.7 0 0 0-.3-1.9L4.2 7 7 4.2l.1.1a1.7 1.7 0 0 0 1.9.3A1.7 1.7 0 0 0 10 3V2.8h4V3a1.7 1.7 0 0 0 1 1.6 1.7 1.7 0 0 0 1.9-.3l.1-.1L19.8 7l-.1.1a1.7 1.7 0 0 0-.3 1.9 1.7 1.7 0 0 0 1.6 1h.2v4H21a1.7 1.7 0 0 0-1.6 1Z"/>',
        'data' => '<path d="M8 7h11l-3-3M16 17H5l3 3"/><path d="m19 7-3 3M5 17l3-3"/>',
        'plus' => '<path d="M12 5v14M5 12h14"/>',
        'wallet' => '<path d="M4 6.5h14a2 2 0 0 1 2 2V19H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h12"/><path d="M20 11h-5a2 2 0 0 0 0 4h5"/>',
        'bell' => '<path d="M18 8a6 6 0 0 0-12 0c0 7-3 7-3 9h18c0-2-3-2-3-9M10 21h4"/>',
        'edit' => '<path d="m4 20 4.5-1 10-10a2.1 2.1 0 0 0-3-3l-10 10L4 20Z"/><path d="m14 7 3 3"/>',
        'trash' => '<path d="M4 7h16M9 7V4h6v3M7 7l1 14h8l1-14M10 11v6M14 11v6"/>',
        'search' => '<circle cx="11" cy="11" r="7"/><path d="m20 20-4-4"/>',
        'menu' => '<path d="M4 7h16M4 12h16M4 17h16"/>',
        'close' => '<path d="m6 6 12 12M18 6 6 18"/>',
        'sun' => '<circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/>',
        'logout' => '<path d="M10 5H5v14h5M14 8l4 4-4 4M8 12h10"/>',
        'clock' => '<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/>',
    ];
    $body = $paths[$name] ?? $paths['home'];
    return '<svg class="icon ' . h($class) . '" viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">' . $body . '</svg>';
}

function redirect_to(string $route, array $parameters = []): never
{
    header('Location: ' . url($route, $parameters));
    exit;
}

function render_view(string $view, array $data = [], bool $authLayout = false): never
{
    global $settings, $auth;
    extract($data, EXTR_SKIP);
    $flashes = Support::pullFlash();
    ob_start();
    require APP_ROOT . '/views/' . $view . '.php';
    $content = (string) ob_get_clean();
    require APP_ROOT . '/views/' . ($authLayout ? 'auth-layout.php' : 'layout.php');
    exit;
}

$route = (string) ($_GET['route'] ?? ($auth->check() ? 'dashboard' : 'login'));
$month = Support::month((string) ($_GET['month'] ?? $_POST['month'] ?? date('Y-m')));

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $action = (string) ($_POST['action'] ?? '');
    try {
        Support::verifyCsrf($_POST['csrf_token'] ?? null);

        if ($action === 'setup') {
            if (!$auth->needsSetup()) {
                throw new RuntimeException('Setup has already been completed.');
            }
            $email = filter_var($_POST['email'] ?? '', FILTER_VALIDATE_EMAIL);
            $password = (string) ($_POST['password'] ?? '');
            if (!$email) {
                throw new RuntimeException('Enter a valid administrator email.');
            }
            if (strlen($password) < 10 || $password !== (string) ($_POST['password_confirmation'] ?? '')) {
                throw new RuntimeException('Use at least 10 characters and make sure both passwords match.');
            }
            $secret = $auth->setup((string) $email, $password, isset($_POST['enable_2fa']));
            if ($secret) {
                $_SESSION['new_totp_secret'] = $secret;
            }
            Support::flash('success', 'Administrator account created. Your planner is ready.');
            redirect_to($secret ? 'settings' : 'dashboard');
        }

        if ($action === 'login') {
            $result = $auth->attempt((string) ($_POST['email'] ?? ''), (string) ($_POST['password'] ?? ''));
            if ($result === 'ok') {
                redirect_to('dashboard');
            }
            if ($result === '2fa') {
                redirect_to('two-factor');
            }
            throw new RuntimeException(
                $result === 'unconfigured_2fa'
                    ? 'Two-factor authentication is required but not configured for this account.'
                    : 'Email or password was not recognized.'
            );
        }

        if ($action === 'verify_2fa') {
            if (!$auth->verifyTwoFactor((string) ($_POST['code'] ?? ''))) {
                throw new RuntimeException('That authentication code is invalid or expired.');
            }
            redirect_to('dashboard');
        }

        if (!$auth->check()) {
            redirect_to('login');
        }

        if ($action === 'logout') {
            $auth->logout();
            header('Location: ' . url('login'));
            exit;
        }

        if ($action === 'plan.save') {
            $budget->savePlan(
                $month,
                (float) ($_POST['starting_income'] ?? 0),
                (float) ($_POST['rollover_in'] ?? 0),
                isset($_POST['rolling_enabled']),
                (string) ($_POST['notes'] ?? '')
            );
            Support::audit($pdo, $auth->id(), 'plan.updated', 'monthly_plan', null, ['month' => $month]);
            Support::flash('success', 'Monthly starting point updated.');
            redirect_to('dashboard', ['month' => $month]);
        }

        if ($action === 'plan.refresh_rollover') {
            $rollover = $budget->refreshRollover($month);
            Support::flash('success', 'Rollover refreshed to ' . Support::money($rollover, (string) $settings->get('app.currency_symbol', '$')) . '.');
            redirect_to('dashboard', ['month' => $month]);
        }

        if ($action === 'income.add') {
            $id = $budget->addIncome(
                $month,
                (string) ($_POST['label'] ?? ''),
                (float) ($_POST['amount'] ?? 0),
                (string) ($_POST['received_on'] ?? date('Y-m-d')),
                (string) ($_POST['notes'] ?? '')
            );
            Support::audit($pdo, $auth->id(), 'income.created', 'income', $id, ['month' => $month]);
            Support::flash('success', 'Income added and automatic withholding applied.');
            redirect_to('dashboard', ['month' => $month]);
        }

        if ($action === 'income.update') {
            $budget->updateIncome(
                (int) ($_POST['id'] ?? 0),
                $month,
                (string) ($_POST['label'] ?? ''),
                (float) ($_POST['amount'] ?? 0),
                (string) ($_POST['received_on'] ?? date('Y-m-d')),
                (string) ($_POST['notes'] ?? '')
            );
            Support::audit($pdo, $auth->id(), 'income.updated', 'income', (int) ($_POST['id'] ?? 0), ['month' => $month]);
            Support::flash('success', 'Income entry updated.');
            redirect_to('dashboard', ['month' => $month]);
        }

        if ($action === 'income.delete') {
            $budget->deleteIncome((int) ($_POST['id'] ?? 0));
            Support::audit($pdo, $auth->id(), 'income.deleted', 'income', (int) ($_POST['id'] ?? 0));
            Support::flash('success', 'Income entry removed.');
            redirect_to('dashboard', ['month' => $month]);
        }

        if ($action === 'category.add') {
            $id = $budget->addCategory(
                (string) ($_POST['name'] ?? ''),
                (string) ($_POST['color'] ?? '#7c5cff'),
                (float) ($_POST['monthly_limit'] ?? 0)
            );
            Support::audit($pdo, $auth->id(), 'category.created', 'category', $id);
            Support::flash('success', 'Category created.');
            $returnRoute = (string) ($_POST['return_route'] ?? 'dashboard');
            redirect_to(in_array($returnRoute, ['dashboard', 'settings'], true) ? $returnRoute : 'dashboard', $returnRoute === 'dashboard' ? ['month' => $month] : []);
        }

        if ($action === 'category.update') {
            $budget->updateCategory(
                (int) ($_POST['id'] ?? 0),
                (string) ($_POST['name'] ?? ''),
                (string) ($_POST['color'] ?? '#7c5cff'),
                (float) ($_POST['monthly_limit'] ?? 0)
            );
            Support::audit($pdo, $auth->id(), 'category.updated', 'category', (int) ($_POST['id'] ?? 0));
            Support::flash('success', 'Category updated.');
            redirect_to('settings');
        }

        if ($action === 'category.archive') {
            $budget->archiveCategory((int) ($_POST['id'] ?? 0));
            Support::audit($pdo, $auth->id(), 'category.archived', 'category', (int) ($_POST['id'] ?? 0));
            Support::flash('success', 'Category archived. Historical records were preserved.');
            redirect_to('settings');
        }

        if ($action === 'item.add') {
            $id = $budget->addBudgetItem(
                $month,
                !empty($_POST['category_id']) ? (int) $_POST['category_id'] : null,
                (string) ($_POST['name'] ?? ''),
                (float) ($_POST['planned_amount'] ?? 0),
                !empty($_POST['due_date']) ? (string) $_POST['due_date'] : null,
                (string) ($_POST['recurrence'] ?? 'none'),
                (string) ($_POST['notes'] ?? '')
            );
            Support::audit($pdo, $auth->id(), 'budget_item.created', 'budget_item', $id, ['month' => $month]);
            Support::flash('success', 'Budget item added.');
            redirect_to('dashboard', ['month' => $month]);
        }

        if ($action === 'item.delete') {
            $budget->deleteBudgetItem((int) ($_POST['id'] ?? 0));
            Support::audit($pdo, $auth->id(), 'budget_item.deleted', 'budget_item', (int) ($_POST['id'] ?? 0));
            Support::flash('success', 'Budget item removed. Existing expenses were preserved.');
            redirect_to('dashboard', ['month' => $month]);
        }

        if ($action === 'item.update') {
            $budget->updateBudgetItem(
                (int) ($_POST['id'] ?? 0),
                $month,
                !empty($_POST['category_id']) ? (int) $_POST['category_id'] : null,
                (string) ($_POST['name'] ?? ''),
                (float) ($_POST['planned_amount'] ?? 0),
                !empty($_POST['due_date']) ? (string) $_POST['due_date'] : null,
                (string) ($_POST['recurrence'] ?? 'none'),
                (string) ($_POST['notes'] ?? '')
            );
            Support::audit($pdo, $auth->id(), 'budget_item.updated', 'budget_item', (int) ($_POST['id'] ?? 0), ['month' => $month]);
            Support::flash('success', 'Budget item updated.');
            redirect_to('dashboard', ['month' => $month]);
        }

        if ($action === 'transaction.add') {
            $id = $budget->addTransaction(
                $month,
                !empty($_POST['budget_item_id']) ? (int) $_POST['budget_item_id'] : null,
                !empty($_POST['category_id']) ? (int) $_POST['category_id'] : null,
                (string) ($_POST['merchant'] ?? ''),
                (float) ($_POST['amount'] ?? 0),
                (string) ($_POST['transacted_on'] ?? date('Y-m-d')),
                (string) ($_POST['notes'] ?? '')
            );
            Support::audit($pdo, $auth->id(), 'transaction.created', 'transaction', $id, ['month' => $month]);
            Support::flash('success', 'Expense recorded and budget progress updated.');
            redirect_to('transactions', ['month' => $month]);
        }

        if ($action === 'transaction.delete') {
            $budget->deleteTransaction((int) ($_POST['id'] ?? 0));
            Support::audit($pdo, $auth->id(), 'transaction.deleted', 'transaction', (int) ($_POST['id'] ?? 0));
            Support::flash('success', 'Expense and its automatic dormant transfer were removed.');
            redirect_to('transactions', ['month' => $month]);
        }

        if ($action === 'transaction.update') {
            $budget->updateTransaction(
                (int) ($_POST['id'] ?? 0),
                $month,
                !empty($_POST['budget_item_id']) ? (int) $_POST['budget_item_id'] : null,
                !empty($_POST['category_id']) ? (int) $_POST['category_id'] : null,
                (string) ($_POST['merchant'] ?? ''),
                (float) ($_POST['amount'] ?? 0),
                (string) ($_POST['transacted_on'] ?? date('Y-m-d')),
                (string) ($_POST['notes'] ?? '')
            );
            Support::audit($pdo, $auth->id(), 'transaction.updated', 'transaction', (int) ($_POST['id'] ?? 0), ['month' => $month]);
            Support::flash('success', 'Expense updated and budget progress recalculated.');
            redirect_to('transactions', ['month' => $month]);
        }

        if ($action === 'dead.move') {
            $id = $budget->moveDeadAccount(
                $month,
                (string) ($_POST['direction'] ?? 'deposit'),
                (float) ($_POST['amount'] ?? 0),
                (string) ($_POST['notes'] ?? ''),
                (string) ($_POST['destination_type'] ?? 'income'),
                !empty($_POST['destination_id']) ? (int) $_POST['destination_id'] : null
            );
            Support::audit($pdo, $auth->id(), 'dead_account.moved', 'dead_entry', $id, ['month' => $month]);
            Support::flash('success', 'Dormant account transfer recorded.');
            redirect_to('dead-account', ['month' => $month]);
        }

        if ($action === 'event.add') {
            $id = $budget->addCalendarEvent(
                $month,
                (string) ($_POST['title'] ?? ''),
                (string) ($_POST['event_date'] ?? date('Y-m-d')),
                (string) ($_POST['event_type'] ?? 'note'),
                (string) ($_POST['notes'] ?? '')
            );
            Support::audit($pdo, $auth->id(), 'calendar_event.created', 'calendar_event', $id);
            Support::flash('success', 'Calendar event added.');
            redirect_to('calendar', ['month' => $month]);
        }

        if ($action === 'event.delete') {
            $budget->deleteCalendarEvent((int) ($_POST['id'] ?? 0));
            Support::audit($pdo, $auth->id(), 'calendar_event.deleted', 'calendar_event', (int) ($_POST['id'] ?? 0));
            Support::flash('success', 'Calendar event removed.');
            redirect_to('calendar', ['month' => $month]);
        }

        if ($action === 'settings.save') {
            $postSetting = static function (string $key, mixed $default = ''): mixed {
                if (array_key_exists($key, $_POST)) {
                    return $_POST[$key];
                }
                return $_POST[str_replace('.', '_', $key)] ?? $default;
            };
            $stringSettings = [
                'app.name', 'app.currency_symbol', 'app.timezone', 'theme.default',
                'dead.income_mode', 'dead.transaction_mode', 'alerts.email',
                'mailgun.domain', 'mailgun.region', 'mailgun.from',
            ];
            foreach ($stringSettings as $key) {
                $settings->set($key, trim((string) $postSetting($key)));
            }
            foreach (['dead.income_value', 'dead.transaction_value'] as $key) {
                $settings->set($key, max(0, (float) $postSetting($key, 0)));
            }
            foreach (['income', 'bills', 'calendar', 'dead_account', 'comparisons', 'alerts', 'import_export'] as $module) {
                $settings->set('modules.' . $module, isset($_POST['modules_' . $module]));
            }
            $settings->set('dead.hide_balance', isset($_POST['dead_hide_balance']));
            $windows = array_values(array_unique(array_filter(array_map(
                static fn (string $value): int => max(0, (int) trim($value)),
                explode(',', (string) $postSetting('alerts.windows', '7,3,1'))
            ), static fn (int $value): bool => $value <= 365)));
            $times = array_values(array_filter(array_map('trim', explode(',', (string) $postSetting('alerts.times', '09:00'))), static fn (string $time): bool => (bool) preg_match('/^(?:[01]\d|2[0-3]):[0-5]\d$/', $time)));
            $settings->set('alerts.windows', $windows ?: [7, 3, 1]);
            $settings->set('alerts.times', $times ?: ['09:00']);
            if (trim((string) $postSetting('mailgun.api_key')) !== '') {
                $settings->set('mailgun.api_key', trim((string) $postSetting('mailgun.api_key')));
            } elseif (isset($_POST['clear_mailgun_key'])) {
                $settings->set('mailgun.api_key', '');
            }

            $requireTwoFactor = isset($_POST['security_2fa_required']);
            $user = $auth->user();
            if ($requireTwoFactor && empty($user['totp_secret'])) {
                $secret = Totp::generateSecret();
                $statement = $pdo->prepare('UPDATE users SET totp_secret = :secret, updated_at = CURRENT_TIMESTAMP WHERE id = :id');
                $statement->execute(['secret' => $secret, 'id' => $auth->id()]);
                $_SESSION['new_totp_secret'] = $secret;
            }
            $settings->set('security.2fa_required', $requireTwoFactor);
            Support::audit($pdo, $auth->id(), 'settings.updated');
            Support::flash('success', 'Planner configuration saved.');
            redirect_to('settings');
        }

        if ($action === '2fa.regenerate') {
            $secret = Totp::generateSecret();
            $statement = $pdo->prepare('UPDATE users SET totp_secret = :secret, updated_at = CURRENT_TIMESTAMP WHERE id = :id');
            $statement->execute(['secret' => $secret, 'id' => $auth->id()]);
            $_SESSION['new_totp_secret'] = $secret;
            Support::audit($pdo, $auth->id(), '2fa.regenerated');
            Support::flash('success', 'A new two-factor secret was generated.');
            redirect_to('settings');
        }

        if ($action === 'alerts.run') {
            $runner = new ReminderService($pdo, $settings, new Mailgun($settings));
            $result = $runner->run(true);
            $message = sprintf('%d reminder(s) sent; %d skipped.', $result['sent'], $result['skipped']);
            if ($result['errors'] !== []) {
                $message .= ' ' . implode(' ', $result['errors']);
                Support::flash('error', $message);
            } else {
                Support::flash('success', $message);
            }
            redirect_to('settings');
        }

        if ($action === 'data.import') {
            if (!isset($_FILES['planner_file']) || !is_uploaded_file($_FILES['planner_file']['tmp_name'])) {
                throw new RuntimeException('Choose a planner JSON package to import.');
            }
            $json = file_get_contents($_FILES['planner_file']['tmp_name']);
            if ($json === false) {
                throw new RuntimeException('The uploaded package could not be read.');
            }
            $exporter = new ExportService($pdo, $settings, $budget);
            $result = $exporter->importPackage($json, isset($_POST['replace_existing']));
            Support::audit($pdo, $auth->id(), 'data.imported', null, null, $result);
            Support::flash('success', 'Planner package imported successfully.');
            redirect_to('data');
        }

        if ($action === 'database.migrate') {
            $config = [
                'host' => trim((string) ($_POST['host'] ?? '127.0.0.1')),
                'port' => (int) ($_POST['port'] ?? 3306),
                'database' => trim((string) ($_POST['database'] ?? '')),
                'username' => trim((string) ($_POST['username'] ?? '')),
                'password' => (string) ($_POST['password'] ?? ''),
                'charset' => 'utf8mb4',
            ];
            if ($config['database'] === '' || $config['username'] === '') {
                throw new RuntimeException('MySQL database name and username are required.');
            }
            $count = $database->migrateSqliteToMysql($config, APP_ROOT);
            Support::flash('success', $count . ' records copied. MySQL mode is active on the next request.');
            redirect_to('data');
        }

        throw new RuntimeException('Unknown action.');
    } catch (Throwable $exception) {
        Support::flash('error', $exception->getMessage());
        $fallbackRoute = in_array($route, ['login', 'setup', 'two-factor'], true) ? $route : ($route ?: 'dashboard');
        redirect_to($fallbackRoute, $auth->check() && $month ? ['month' => $month] : []);
    }
}

if ($route === 'logout') {
    redirect_to('login');
}

if ($auth->needsSetup()) {
    render_view('setup', ['title' => 'Create your planner'], true);
}

if (!$auth->check()) {
    if ($route === 'two-factor' && isset($_SESSION['pending_2fa_user'])) {
        render_view('two-factor', ['title' => 'Two-factor verification'], true);
    }
    render_view('login', ['title' => 'Administrator sign in'], true);
}

Support::trackVisit($pdo, $route);

if (in_array($route, ['export-json', 'export-html', 'export-pdf'], true)) {
    $exporter = new ExportService($pdo, $settings, $budget);
    if ($route === 'export-json') {
        header('Content-Type: application/json; charset=utf-8');
        header('Content-Disposition: attachment; filename="neon-ledger-' . date('Ymd-His') . '.json"');
        echo $exporter->package();
        exit;
    }
    if ($route === 'export-html') {
        header('Content-Type: text/html; charset=utf-8');
        header('Content-Disposition: attachment; filename="budget-' . $month . '.html"');
        echo $exporter->html($month);
        exit;
    }
    header('Content-Type: application/pdf');
    header('Content-Disposition: attachment; filename="budget-' . $month . '.pdf"');
    echo $exporter->pdf($month);
    exit;
}

$plan = $budget->ensureMonth($month);
$summary = $budget->summary($month);
$symbol = (string) $settings->get('app.currency_symbol', '$');

switch ($route) {
    case 'transactions':
        render_view('transactions', [
            'title' => 'Expenses',
            'route' => $route,
            'month' => $month,
            'summary' => $summary,
            'items' => $budget->budgetItems($month),
            'categories' => $budget->categories(),
            'transactions' => $budget->transactions($month),
            'symbol' => $symbol,
        ]);
    case 'calendar':
        render_view('calendar', [
            'title' => 'Bill calendar',
            'route' => $route,
            'month' => $month,
            'entries' => $budget->calendarEntries($month),
            'symbol' => $symbol,
        ]);
    case 'comparisons':
        $year = (int) substr($month, 0, 4);
        render_view('comparisons', [
            'title' => 'Comparisons',
            'route' => $route,
            'month' => $month,
            'comparison' => $budget->comparison($month),
            'yearly' => $budget->yearlySummary($year),
            'categoryStats' => $budget->categoryStats($month),
            'symbol' => $symbol,
        ]);
    case 'dead-account':
        render_view('dead-account', [
            'title' => 'Dormant account',
            'route' => $route,
            'month' => $month,
            'balance' => $budget->deadBalance(),
            'entries' => $budget->deadEntries(null, 150),
            'items' => $budget->budgetItems($month),
            'hideBalance' => (bool) $settings->get('dead.hide_balance', true),
            'symbol' => $symbol,
        ]);
    case 'settings':
        $user = $auth->user();
        $newSecret = $_SESSION['new_totp_secret'] ?? null;
        unset($_SESSION['new_totp_secret']);
        render_view('settings', [
            'title' => 'Settings',
            'route' => $route,
            'month' => $month,
            'allSettings' => $settings->all(),
            'user' => $user,
            'newSecret' => $newSecret,
            'totpUri' => $newSecret ? Totp::uri($newSecret, (string) $user['email'], (string) $settings->get('app.name', 'Neon Ledger')) : null,
            'mailgunConfigured' => (new Mailgun($settings))->configured(),
            'categories' => $budget->categories(),
        ]);
    case 'data':
        render_view('data', [
            'title' => 'Import & export',
            'route' => $route,
            'month' => $month,
            'databaseDriver' => $database->driver(),
            'databaseConfig' => $database->config(),
        ]);
    case 'dashboard':
    default:
        render_view('dashboard', [
            'title' => 'Command center',
            'route' => 'dashboard',
            'month' => $month,
            'plan' => $plan,
            'summary' => $summary,
            'incomes' => $budget->incomes($month),
            'items' => $budget->budgetItems($month),
            'categories' => $budget->categories(),
            'categoryStats' => $budget->categoryStats($month),
            'comparison' => $budget->comparison($month, 6),
            'previousSummary' => $budget->summary(date('Y-m', strtotime($month . '-01 -1 month')), false),
            'upcomingBills' => $budget->upcomingBills($month),
            'recentActivity' => $budget->recentActivity($month),
            'visitorStats' => $budget->visitorStats(),
            'deadBalance' => $budget->deadBalance(),
            'symbol' => $symbol,
        ]);
}
