<?php

use Illuminate\Support\Facades\Route;

// Laravel 13 string-based lazy-loading контроллеров (Sprint 2 Phase A, O-stack-03).
//
// Контроллеры передаются как строки 'FQN@method' вместо
// [Class::class, 'method']. При таком синтаксисе Laravel НЕ автозагружает
// класс при boot'е route'ов — резолвит из строки только при матче маршрута.
// Экономит ~50-100 ms на cold-boot для middleware-пайплайна с 20+ контроллерами.
//
// Почему именно строки, а не FQN-class:
//   - Pint default preset (Laravel) применяет fixer fully_qualified_strict_types,
//     который сворачивает \App\Http\...\X::class обратно в use-импорт.
//     На строки этот fixer не действует — синтаксис стабилен под форматтером.
//
// SPA-API маршруты auth-flow. Размещены в web.php (а не api.php), потому
// что Sanctum SPA mode использует session-cookie auth, а session middleware
// добавляется только к web-группе. См. laravel.com/docs/sanctum#spa-authentication.
Route::prefix('/api/auth')->group(function () {
    // Route-throttle (P1 go-live): per-IP лимит поверх per-credential rate-limit
    // в контроллерах. Именованные лимитеры — в AppServiceProvider::boot.
    Route::post('/login', 'App\Http\Controllers\Api\AuthController@login')
        ->middleware('throttle:auth-login');
    Route::post('/register', 'App\Http\Controllers\Api\RegistrationController@register')
        ->middleware('throttle:auth-register');
    Route::post('/confirm-email', 'App\Http\Controllers\Api\RegistrationController@confirmEmail')
        ->middleware('throttle:auth-register');
    Route::post('/resend-code', 'App\Http\Controllers\Api\RegistrationController@resendCode')
        ->middleware('throttle:auth-register');
    // /2fa/verify публичный — у user'а ещё нет полноценной session-auth, только
    // pending_user_id в session. Verify завершает login после проверки TOTP.
    //
    // Sprint 3 Phase B (audit O-refactor-02): 2FA verify/recovery-use вынесены
    // из AuthController в TwoFactorController; forgot/reset — в PasswordResetController.
    // URL без изменений.
    Route::post('/2fa/verify', 'App\Http\Controllers\Api\TwoFactorController@verifyTwoFactor')
        ->middleware('throttle:auth-2fa');
    // /2fa/recovery-use — публичный (нет полноценной session-auth до verify).
    Route::post('/2fa/recovery-use', 'App\Http\Controllers\Api\TwoFactorController@useRecoveryCode')
        ->middleware('throttle:auth-2fa');
    // /forgot — публичный (anti-enumeration unified-ответ + rate-limit).
    Route::post('/forgot', 'App\Http\Controllers\Api\PasswordResetController@forgotPassword')
        ->middleware('throttle:auth-password');
    // /reset-password — публичный (deep-link из email с token+email+password).
    Route::post('/reset-password', 'App\Http\Controllers\Api\PasswordResetController@resetPassword')
        ->middleware('throttle:auth-password');
    Route::middleware('auth:sanctum')->group(function () {
        Route::get('/me', 'App\Http\Controllers\Api\AuthController@me');
        Route::patch('/me', 'App\Http\Controllers\Api\AuthController@updateProfile');
        Route::post('/logout', 'App\Http\Controllers\Api\AuthController@logout');
        Route::patch('/me/notification-preferences', 'App\Http\Controllers\Api\AuthController@updateNotificationPreferences');
    });
});

// Аккаунт — вкладка «Безопасность» (UI-аудит 21.06.2026): смена пароля + недавние входы.
Route::middleware('auth:sanctum')->prefix('/api/account')->group(function () {
    Route::post('/change-password', 'App\Http\Controllers\Api\AccountController@changePassword')
        ->middleware('throttle:auth-password');
    Route::get('/security', 'App\Http\Controllers\Api\AccountController@security');
    Route::delete('/sessions/{id}', 'App\Http\Controllers\Api\AccountController@revokeSession')
        ->whereNumber('id');
});

// In-app уведомления (P0 этап 2b). Все endpoint'ы под Sanctum SPA auth —
// уведомления USER-personal, читать/писать может только сам user.
Route::middleware(['auth:sanctum,impersonation', 'tenant'])->prefix('/api/notifications')->group(function () {
    Route::get('/', 'App\Http\Controllers\Api\InAppNotificationController@index');
    Route::patch('/{id}/read', 'App\Http\Controllers\Api\InAppNotificationController@markRead')->where('id', '[0-9]+');
    Route::post('/mark-all-read', 'App\Http\Controllers\Api\InAppNotificationController@markAllRead');
    Route::delete('/{id}', 'App\Http\Controllers\Api\InAppNotificationController@destroy')->where('id', '[0-9]+');
});

// Реквизиты тенанта (G1/SP2). Лёгкий гейт первого проекта + дозаполнение в ЛК.
Route::middleware(['auth:sanctum', 'tenant'])->prefix('/api/tenant/requisites')->group(function () {
    Route::get('/', 'App\Http\Controllers\Api\TenantRequisitesController@show');
    Route::put('/', 'App\Http\Controllers\Api\TenantRequisitesController@update');
    Route::post('/lookup-inn', 'App\Http\Controllers\Api\TenantRequisitesController@lookupInn')
        ->middleware('throttle:30,1');
});

// Reports backend. Schema §13.5 report_jobs. Auth обязательный.
// Этапы 1+2 (CRUD + provider/formatter) + этап 3 (retry/cancel/delete +
// retention cron `reports:cleanup-expired`).
Route::middleware(['auth:sanctum,impersonation', 'tenant'])->prefix('/api/reports/jobs')->group(function () {
    Route::get('/', 'App\Http\Controllers\Api\ReportJobController@index');
    Route::post('/', 'App\Http\Controllers\Api\ReportJobController@store');
    Route::get('/{id}', 'App\Http\Controllers\Api\ReportJobController@show')->where('id', '[0-9]+');
    Route::post('/{id}/retry', 'App\Http\Controllers\Api\ReportJobController@retry')->where('id', '[0-9]+');
    Route::post('/{id}/cancel', 'App\Http\Controllers\Api\ReportJobController@cancel')->where('id', '[0-9]+');
    Route::delete('/{id}', 'App\Http\Controllers\Api\ReportJobController@destroy')->where('id', '[0-9]+');
});

// F2 (audit): скачивание готового файла отчёта по signed URL (24 ч, OPEN-И-20).
// НЕ под auth:sanctum — подпись URL = capability-token (генерируется только
// в ReportJobController::toResource() для отчётов своего тенанта).
Route::get('/api/reports/jobs/{id}/file', 'App\Http\Controllers\Api\ReportJobController@download')
    ->where('id', '[0-9]+')
    ->name('reports.download')
    ->middleware('signed');

// SaaS-admin зона (/api/admin/*). Гейт `saas-admin` = EnsureSaasAdmin: на проде
// fail-closed по nginx HTTP Basic Auth (^~ /api/admin, .htpasswd-admin) + M-1
// app-слой (REMOTE_USER ∈ ADMIN_ALLOWED_USERS, закрывает обходы фронт-контроллера)
// + запрет входа во время impersonation. Реальный Yandex 360 SSO — TODO под Б-1+DO-4.
// admin_user_id для audit — трейт ResolvesAdminUserId (отдельная зона).
// admin-db (UseAdminConnection) — ПОСЛЕ saas-admin: на время admin-запроса
// default-подключение = pgsql_admin (роль crm_admin_user, srv_bypass), чтобы
// AdminTenants/AdminBillingController видели все тенанты после переезда на
// Managed PG (Путь А). Контроллеры на pgsql_supplier не затрагиваются.
Route::middleware(['saas-admin', 'admin-db'])->group(function () {
    // Командный центр (дашборд) — read-only агрегаты L1 + L2.
    Route::get('/api/admin/dashboard', 'App\Http\Controllers\Api\AdminDashboardController@summary');
    Route::get('/api/admin/dashboard/finance', 'App\Http\Controllers\Api\AdminDashboardController@finance');
    Route::get('/api/admin/dashboard/health', 'App\Http\Controllers\Api\AdminDashboardController@health');
    Route::get('/api/admin/dashboard/leads', 'App\Http\Controllers\Api\AdminDashboardController@leads');
    Route::get('/api/admin/dashboard/supply', 'App\Http\Controllers\Api\AdminDashboardController@supply');
    Route::get('/api/admin/dashboard/balances', 'App\Http\Controllers\Api\AdminDashboardController@balances');
    Route::get('/api/admin/dashboard/clients', 'App\Http\Controllers\Api\AdminDashboardController@clients');
    Route::get('/api/admin/leads', 'App\Http\Controllers\Api\AdminLeadsController@index');
    Route::get('/api/admin/leads/{id}', 'App\Http\Controllers\Api\AdminLeadsController@show')->whereNumber('id');

    // SaaS-admin impersonation flow (Ю-1). Авторизация — через гейт группы (EnsureSaasAdmin).
    Route::prefix('/api/admin/impersonation')->group(function () {
        Route::get('/active', 'App\Http\Controllers\Api\ImpersonationController@active');
        Route::get('/recent', 'App\Http\Controllers\Api\ImpersonationController@recent');
        Route::post('/init', 'App\Http\Controllers\Api\ImpersonationController@init');
        Route::post('/verify', 'App\Http\Controllers\Api\ImpersonationController@verify');
        Route::post('/end', 'App\Http\Controllers\Api\ImpersonationController@end');
    });

    // SaaS-admin → Тенанты: lookup + детали для AdminTenantsView/AdminTenantDetailView.
    Route::get('/api/admin/tenants', 'App\Http\Controllers\Api\AdminTenantsController@index');
    Route::get('/api/admin/tenants/{subdomain}', 'App\Http\Controllers\Api\AdminTenantsController@show')
        ->where('subdomain', '[a-z0-9_-]+');
    Route::patch('/api/admin/tenants/{id}/balance', 'App\Http\Controllers\Api\AdminTenantsController@updateBalance')
        ->where('id', '[0-9]+');

    // SaaS-admin → Биллинг: aggregates пополнений/списаний за текущий месяц.
    Route::get('/api/admin/billing', 'App\Http\Controllers\Api\AdminBillingController@index');

    // Sprint 3D (G4): SaaS-admin billing row-actions — приостановка/возврат/смена тарифа.
    Route::get('/api/admin/billing/tariff-plans', 'App\Http\Controllers\Api\AdminBillingController@tariffPlans');
    Route::patch('/api/admin/billing/tenants/{id}/status', 'App\Http\Controllers\Api\AdminBillingController@updateStatus')
        ->where('id', '[0-9]+');
    Route::post('/api/admin/billing/tenants/{id}/refund', 'App\Http\Controllers\Api\AdminBillingController@refund')
        ->where('id', '[0-9]+');
    Route::patch('/api/admin/billing/tenants/{id}/tariff', 'App\Http\Controllers\Api\AdminBillingController@changeTariff')
        ->where('id', '[0-9]+');

    // SaaS-admin → Инциденты: чтение incidents_log для AdminIncidentsView.
    Route::get('/api/admin/incidents', 'App\Http\Controllers\Api\AdminIncidentsController@index');

    // Sprint 3D (G5): SaaS-admin incident detail-view drill-down.
    Route::get('/api/admin/incidents/{id}', 'App\Http\Controllers\Api\AdminIncidentsController@show')
        ->where('id', '[0-9]+');

    // Sprint 3D (G6): РКН-notify endpoint (152-ФЗ).
    Route::post('/api/admin/incidents/{id}/rkn-notify', 'App\Http\Controllers\Api\AdminIncidentsController@notifyRkn')
        ->where('id', '[0-9]+');

    // SaaS-admin → Система: edit-flow для system_settings + audit-log (4-eyes-pattern).
    // Авторизация — через гейт группы saas-admin (EnsureSaasAdmin: nginx-basic + M-1).
    Route::prefix('/api/admin/system-settings')->group(function () {
        Route::get('/', 'App\Http\Controllers\Api\AdminSystemSettingsController@index');
        Route::put('/{key}', 'App\Http\Controllers\Api\AdminSystemSettingsController@update')->where('key', '[a-z0-9_\.]+');
    });

    // SaaS-admin → Биллинг: ввод секретных ключей платёжного шлюза (config зашифрован).
    Route::put('/api/admin/payment-gateways/{code}', 'App\Http\Controllers\Api\AdminPaymentGatewayController@update')
        ->where('code', '[a-z0-9_]+');

    // Plan 4: SaaS-admin pricing-tiers editor.
    // CRUD для 7-ступенчатого тарифа. effective_from auto-computed = 1-е число
    // следующего месяца (МСК). Audit-trail в saas_admin_audit_log.
    Route::prefix('/api/admin/pricing-tiers')->group(function () {
        Route::get('/', 'App\Http\Controllers\Api\AdminPricingTiersController@index');
        Route::post('/', 'App\Http\Controllers\Api\AdminPricingTiersController@store');
        Route::delete('/scheduled/{effective_from}',
            'App\Http\Controllers\Api\AdminPricingTiersController@deleteScheduled')
            ->where('effective_from', '\d{4}-\d{2}-\d{2}');
    });

    // Plan 4 Task 10: SaaS-admin supplier prices editor.
    // CRUD для B1/B2/B3 закупочных цен. Audit-trail в saas_admin_audit_log.
    Route::get('/api/admin/suppliers', 'App\Http\Controllers\Api\AdminSuppliersController@index');
    Route::patch('/api/admin/suppliers/{id}', 'App\Http\Controllers\Api\AdminSuppliersController@update')
        ->where('id', '[0-9]+');

    // Резервный CSV-канал (Путь 2): здоровье канала + ручной запуск сверки.
    Route::get('/api/admin/supplier-integration', 'App\Http\Controllers\Api\AdminSupplierIntegrationController@index');
    Route::post('/api/admin/supplier-integration/reconcile', 'App\Http\Controllers\Api\AdminSupplierIntegrationController@reconcile');

    // Эпик 5: история вечерних заливок проектов поставщику (supplier_sync_runs).
    Route::get('/api/admin/supplier-integration/sync-runs', 'App\Http\Controllers\Api\AdminSupplierIntegrationController@syncRuns');

    // Резерв канала миграции проектов (ярус 3): ручная очередь оператора.
    Route::get('/api/admin/supplier-integration/manual-queue', 'App\Http\Controllers\Api\AdminSupplierIntegrationController@manualQueueIndex');
    Route::post('/api/admin/supplier-integration/manual-queue/{id}/resolve', 'App\Http\Controllers\Api\AdminSupplierIntegrationController@manualQueueResolve')
        ->where('id', '[0-9]+');

    // Plan 4 Task 1: глобальный тумблер режима экспорта проектов (online|batch).
    Route::get('/api/admin/supplier-integration/export-mode', 'App\Http\Controllers\Api\AdminSupplierIntegrationController@getExportMode');
    Route::post('/api/admin/supplier-integration/export-mode', 'App\Http\Controllers\Api\AdminSupplierIntegrationController@setExportMode');

    // Тумблер «Разблокировка смены источника» (флаг routing_match_by_snapshot).
    Route::get('/api/admin/supplier-integration/source-edit-flag', 'App\Http\Controllers\Api\AdminSupplierIntegrationController@getSourceEditFlag');
    Route::post('/api/admin/supplier-integration/source-edit-flag', 'App\Http\Controllers\Api\AdminSupplierIntegrationController@setSourceEditFlag');

    // Plan 4 Task 2: экран «Проекты у поставщика» — список + bulk-delete.
    Route::get('/api/admin/supplier-integration/projects', 'App\Http\Controllers\Api\AdminSupplierIntegrationController@projectsIndex');
    Route::post('/api/admin/supplier-integration/projects/delete', 'App\Http\Controllers\Api\AdminSupplierIntegrationController@projectsDestroy');

    // 152-ФЗ: обращения субъектов ПДн + анонимизация (дыра #4).
    Route::prefix('/api/admin/pd-subject-requests')->group(function () {
        Route::get('/', 'App\Http\Controllers\Api\AdminPdSubjectRequestsController@index');
        Route::post('/', 'App\Http\Controllers\Api\AdminPdSubjectRequestsController@store');
        Route::get('/{id}', 'App\Http\Controllers\Api\AdminPdSubjectRequestsController@show')
            ->where('id', '[0-9]+');
        Route::post('/{id}/erase', 'App\Http\Controllers\Api\AdminPdSubjectRequestsController@executeErasure')
            ->where('id', '[0-9]+');
    });
});

// Plan 4 Task 11: tenant charges ledger (read-only + CSV export).
// RLS изоляция через SetTenantContext (auth:sanctum + tenant) — текущий tenant
// видит только свои lead_charges. Pagination 20/page, фильтры period/source.
Route::middleware(['auth:sanctum,impersonation', 'tenant'])->prefix('/api/billing/charges')->group(function () {
    Route::get('/', 'App\Http\Controllers\Api\TenantChargesController@index');
    Route::post('/export', 'App\Http\Controllers\Api\TenantChargesController@export');
});

// Биллинг тенанта: пополнение/кошелёк/транзакции/счета (audit E1/E3).
// RLS на balance_transactions / saas_invoices требует tenant middleware.
Route::middleware(['auth:sanctum', 'tenant'])->prefix('/api/billing')->group(function () {
    Route::post('/topup', 'App\Http\Controllers\Api\BillingController@topup');
    Route::get('/wallet', 'App\Http\Controllers\Api\BillingController@wallet');
    Route::get('/balance-status', 'App\Http\Controllers\Api\BillingController@balanceStatus');
    Route::get('/transactions', 'App\Http\Controllers\Api\BillingController@transactions');
    Route::get('/invoices', 'App\Http\Controllers\Api\BillingController@invoices');
});

// API-ключи тенанта (audit D2/D3/J5). RLS на api_keys требует tenant middleware.
Route::middleware(['auth:sanctum', 'tenant'])->prefix('/api/api-keys')->group(function () {
    Route::get('/', 'App\Http\Controllers\Api\ApiKeyController@index');
    Route::post('/regenerate', 'App\Http\Controllers\Api\ApiKeyController@regenerate');
});

// G6: публичный read-API сделок по API-ключу (middleware apikey — без sanctum/tenant).
// apiv1-rate (приёмка 21.06): throttle:api-v1 (120/мин/источник) ПЕРЕД apikey —
// прикрывает bcrypt/DB-работу аутентификации от brute/DoS (лимитер — AppServiceProvider).
Route::middleware(['throttle:api-v1', 'apikey'])->prefix('/api/v1')->group(function () {
    Route::get('/deals', 'App\Http\Controllers\Api\V1\DealsController@index')->name('api.v1.deals.index');
});

// Настройки исходящего webhook'а тенанта (audit D4/D5/J5).
Route::middleware(['auth:sanctum', 'tenant'])->group(function () {
    Route::get('/api/tenants/me/webhook-settings', 'App\Http\Controllers\Api\WebhookSettingsController@show');
    Route::put('/api/tenants/me/webhook-settings', 'App\Http\Controllers\Api\WebhookSettingsController@update');
    Route::post('/api/webhooks/test', 'App\Http\Controllers\Api\WebhookSettingsController@test');
});

// Дашборд — агрегат KPI/баланса/активности/воронки (audit J3). Go-live: auth:sanctum
// + tenant; tenant_id из auth()->user()->tenant_id (SetTenantContext), НЕ из параметра
// запроса — закрывает кросс-tenant утечку KPI (как DealController J1).
Route::middleware(['auth:sanctum,impersonation', 'tenant'])->group(function () {
    Route::get('/api/dashboard/summary', 'App\Http\Controllers\Api\DashboardController@summary');
});

// G7-A: клиентские заявки в техподдержку.
Route::middleware(['auth:sanctum,impersonation', 'tenant'])->post('/api/support-requests', 'App\Http\Controllers\Api\SupportRequestController@store');

// G7-B: выход из режима поддержки из самого кабинета клиента.
Route::middleware('auth:sanctum')->post('/api/impersonation/leave', 'App\Http\Controllers\Api\ImpersonationController@leave');

// Сделки — single-resource CRUD + bulk + export. J1 (Sprint 3F, audit):
// auth:sanctum + tenant. tenant_id берётся из auth()->user()->tenant_id
// (SetTenantContext), НЕ из параметра запроса — закрывает кросс-tenant утечку.
//
// Sprint 3 Phase A (audit O-refactor-01): single-resource CRUD в
// DealController, bulk (transition/destroy/restore) — в
// DealBulkActionController, export — в DealExportController.
Route::middleware(['auth:sanctum,impersonation', 'tenant'])->group(function () {
    Route::get('/api/deals', 'App\Http\Controllers\Api\DealController@index');
    Route::get('/api/deals/{id}', 'App\Http\Controllers\Api\DealController@show')->where('id', '[0-9]+');
    Route::post('/api/deals', 'App\Http\Controllers\Api\DealController@store');
    Route::post('/api/deals/export', 'App\Http\Controllers\Api\DealExportController@export');
    Route::post('/api/deals/transition', 'App\Http\Controllers\Api\DealBulkActionController@transition');
    Route::patch('/api/deals/{id}', 'App\Http\Controllers\Api\DealController@update')->where('id', '[0-9]+');
    Route::delete('/api/deals', 'App\Http\Controllers\Api\DealBulkActionController@destroy');
    Route::post('/api/deals/restore', 'App\Http\Controllers\Api\DealBulkActionController@restore');
});

// Sprint 4 — CSV-импорт исторических лидов (ТЗ §6).
// ВАЖНО: /unknown-statuses и /unknown-statuses/resolve объявлены ДО
// /{importLog}, иначе литеральный сегмент перехватывается параметром.
Route::middleware(['auth:sanctum,impersonation', 'tenant'])->group(function () {
    Route::get('/api/imports/unknown-statuses', 'App\Http\Controllers\Api\ImportController@unknownStatuses');
    Route::post('/api/imports/unknown-statuses/resolve', 'App\Http\Controllers\Api\ImportController@resolveUnknownStatuses');
    Route::get('/api/imports', 'App\Http\Controllers\Api\ImportController@index');
    Route::post('/api/imports', 'App\Http\Controllers\Api\ImportController@store');
    Route::get('/api/imports/{importLog}', 'App\Http\Controllers\Api\ImportController@show');
});

// Lookup endpoints — заполняют v-select'ы (NewDealDialog, smart-filters).
// Go-live: auth:sanctum. /api/managers — tenant-scoped (tenant_id из authed-user, НЕ из
// параметра — закрывает кросс-tenant утечку списка пользователей); /api/lead-statuses —
// глобальная таблица (без tenant_id), нужен только auth:sanctum.
Route::middleware(['auth:sanctum,impersonation', 'tenant'])->group(function () {
    Route::get('/api/managers', 'App\Http\Controllers\Api\ManagerController@index');
});
Route::middleware('auth:sanctum,impersonation')->group(function () {
    Route::get('/api/lead-statuses', 'App\Http\Controllers\Api\LeadStatusController@index');
});

// Plan 5 Task 2: Projects CRUD — расширенный API с auth:sanctum + RLS.
// Заменяет старый GET /api/projects?tenant_id={id} (без auth, MVP-версия).
// ⚠️ NewDealDialog использовал старый endpoint (tenant_id param, без auth) —
// после этой замены получит 401. Defer fix до Task 7 (frontend phase).
Route::middleware(['auth:sanctum,impersonation', 'tenant'])->prefix('/api/projects')->group(function () {
    Route::get('/', 'App\Http\Controllers\Api\ProjectController@index')->name('projects.index');
    Route::post('/', 'App\Http\Controllers\Api\ProjectController@store')->name('projects.store');
    // /bulk MUST be declared before /{id} parameterized routes so the literal
    // segment matches before the regex placeholder is even considered.
    Route::post('/bulk', 'App\Http\Controllers\Api\ProjectController@bulk')->name('projects.bulk');
    Route::get('/{id}', 'App\Http\Controllers\Api\ProjectController@show')->name('projects.show')->where('id', '[0-9]+');
    Route::patch('/{id}', 'App\Http\Controllers\Api\ProjectController@update')->name('projects.update')->where('id', '[0-9]+');
    Route::delete('/{id}', 'App\Http\Controllers\Api\ProjectController@destroy')->name('projects.destroy')->where('id', '[0-9]+');
    Route::post('/{id}/sync', 'App\Http\Controllers\Api\ProjectController@sync')->name('projects.sync')->where('id', '[0-9]+');
    Route::patch('/{id}/toggle-active', 'App\Http\Controllers\Api\ProjectController@toggleActive')->name('projects.toggle')->where('id', '[0-9]+');
});

// Supplier-integration webhook (Plan 2/5, spec §5.1).
// Platform-wide endpoint: единый {secret} в URL для всех лидов от crm.bp-gr.ru.
// Auth: secret (system_settings.supplier_webhook_secret) + IP allowlist
// (system_settings.supplier_ip_allowlist). Не пересекается с legacy /api/webhook/{token}.
// Secretless-вариант: аутентификация по HMAC-подписи (X-Webhook-Signature),
// чтобы секрет не светился в URL/access-логах (P2/E4). receive() c $secret=''.
Route::post('/api/webhook/supplier', 'App\Http\Controllers\Api\SupplierWebhookController@receive');
Route::post('/api/webhook/supplier/{secret}', 'App\Http\Controllers\Api\SupplierWebhookController@receive')
    ->where('secret', '[A-Za-z0-9_\-]+');

// Платёжный webhook (ЮKassa). Публичный, под маской api/webhook/* → CSRF-exempt.
// Подлинность — server-to-server сверкой статуса (не доверяем телу). Plan billing-yookassa Task 7.
Route::post('/api/webhook/payment', 'App\Http\Controllers\Api\PaymentWebhookController@receive');

// Публичная (без auth) тарифная сетка — для страницы цен и модерации ЮKassa.
Route::get('/api/public/pricing', 'App\Http\Controllers\Api\PublicPricingController@index');

// 2FA setup wizard — все эндпоинты под auth:sanctum (только для уже залогиненных).
Route::prefix('/api/2fa')->middleware('auth:sanctum')->group(function () {
    Route::post('/init', 'App\Http\Controllers\Api\TwoFactorSetupController@init');
    Route::post('/confirm', 'App\Http\Controllers\Api\TwoFactorSetupController@confirm');
    Route::post('/disable', 'App\Http\Controllers\Api\TwoFactorSetupController@disable');
    Route::post('/regenerate-recovery-codes', 'App\Http\Controllers\Api\TwoFactorSetupController@regenerateRecoveryCodes');
});

// SPA-страницы: каждый путь отдаёт Vue-shell (один Blade-template `welcome`).
// Vue Router (createWebHistory) разруливает /login, /register и т.п. на фронте.
//
// Регистрируем явно, а не catch-all `/{any?}` — иначе тесты, регистрирующие
// runtime routes через `Route::get(...)` в beforeEach (например _test/...),
// будут перехвачены catch-all'ом и вернут welcome view вместо expected response.
Route::view('/', 'welcome');
Route::view('/login', 'welcome');
Route::view('/register', 'welcome');
Route::view('/forgot', 'welcome');
Route::view('/reset', 'welcome'); // SPA-router рендерит ResetPasswordView для /reset/{token}
Route::view('/2fa', 'welcome');
Route::view('/recovery-use', 'welcome');
Route::view('/legal/offer', 'welcome');
Route::view('/legal/privacy', 'welcome');
Route::view('/dashboard', 'welcome');
Route::view('/deals', 'welcome');
Route::view('/kanban', 'welcome');
Route::view('/projects', 'welcome');
Route::view('/billing', 'welcome');
Route::view('/settings', 'welcome');
Route::view('/reports', 'welcome');
Route::view('/import', 'welcome'); // Sprint 4 — CSV-импорт исторических лидов §6
Route::view('/admin', 'welcome');
Route::view('/admin/tenants', 'welcome');
Route::view('/admin/billing', 'welcome');
Route::view('/admin/incidents', 'welcome');
Route::view('/admin/system', 'welcome');
Route::view('/admin/pricing-tiers', 'welcome');
Route::view('/admin/supplier-prices', 'welcome');
Route::view('/admin/supplier-integration', 'welcome');
Route::view('/admin/impersonation', 'welcome');
Route::view('/403', 'welcome');
Route::view('/500', 'welcome');

// Fallback для всех неизвестных путей — Vue Router catch-all отрисует 404.
// Срабатывает ПОСЛЕ всех явных route'ов выше и runtime-route'ов от Pest
// beforeEach (они регистрируются в момент теста, до запроса).
Route::fallback(fn () => view('welcome'));
