<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
*/

// PRC-57 — ACME HTTP-01 challenge endpoint (Let's Encrypt et al.)
Route::get('/.well-known/acme-challenge/{token}', function (string $token) {
    $payload = \App\Services\Ssl\AcmeService::resolveChallengeToken($token);
    abort_if($payload === null, 404);

    return response($payload, 200, ['Content-Type' => 'text/plain']);
})->where('token', '[A-Za-z0-9_\-]+')->name('acme.challenge');

// PRC-1121 — Apple Pay domain association (PayPal v6 SDK Apple Pay). Public, auth-siz;
// içerik admin config'inden (paypal.apple_pay_domain_association) servis edilir.
Route::get('/.well-known/apple-developer-merchantid-domain-association',
    [\App\Http\Controllers\Store\PaymentController::class, 'applePayDomainAssociation'])
    ->name('paypal.apple-pay-domain');

// PRC-30 — Public unsubscribe (token-based, throttled)
Route::middleware('throttle:10,1')->group(function () {
    Route::get('/email/unsubscribe/{token}',
        [\App\Http\Controllers\PublicUnsubscribeController::class, 'show'])
        ->where('token', '[a-f0-9]{32}')
        ->name('email.unsubscribe.show');
    Route::post('/email/unsubscribe/{token}',
        [\App\Http\Controllers\PublicUnsubscribeController::class, 'confirm'])
        ->where('token', '[a-f0-9]{32}')
        ->name('email.unsubscribe.confirm');
});

// PRC-31 — Email campaign tracking (open pixel + click redirect)
// PRC-229 — bumped from 60/min/IP to 600/min/IP. Corporate mail proxies
// (Gmail, Outlook, Microsoft Defender link rewriting) routinely batch-
// prefetch many tracking pixels behind a single egress IP; 60/min would
// false-positive block legitimate cohorts. Token is 32-hex (128 bits) so
// brute-force enumeration is infeasible regardless of throttle.
Route::middleware('throttle:600,1')->group(function () {
    Route::get('/email/campaign-track/open/{token}',
        [\App\Http\Controllers\EmailCampaignTrackingController::class, 'open'])
        ->where('token', '[a-f0-9]{32}(\.gif)?')
        ->name('email.campaign-track.open');
    Route::get('/email/campaign-track/click/{token}/{linkHash}',
        [\App\Http\Controllers\EmailCampaignTrackingController::class, 'click'])
        ->where(['token' => '[a-f0-9]{32}', 'linkHash' => '[a-f0-9]{16}'])
        ->name('email.campaign-track.click');
});

// PRC-31 — Provider webhooks (CSRF excluded via VerifyCsrfToken or no session middleware)
Route::middleware('throttle:120,1')->group(function () {
    Route::post('/email/campaign-webhook/mailchimp',
        [\App\Http\Controllers\EmailCampaignWebhookController::class, 'mailchimp'])
        ->name('email.campaign-webhook.mailchimp');
    Route::post('/email/campaign-webhook/mailerlite',
        [\App\Http\Controllers\EmailCampaignWebhookController::class, 'mailerlite'])
        ->name('email.campaign-webhook.mailerlite');

    // PRC-1059 — Payment machine (S2S) webhook. İmzalı sunucu-sunucu gateway
    // bildirimleri (Conekta Digest / BTCPay-CoinPayments-BitPay HMAC / PayTR bildirim /
    // iPay88 BackendURL) auth:client-gated browser-return callback'e ULAŞAMAZ. Bu
    // auth-siz, throttle'lı, CSRF-exempt endpoint faturayı çözer, trusted expected_*
    // inject eder ve otantikliği modülün fail-closed imza doğrulamasına bırakır.
    Route::post('/payment/webhook/{gateway}/{invoice}',
        [\App\Http\Controllers\Store\PaymentController::class, 'machineWebhook'])
        ->where(['gateway' => '[A-Za-z0-9_-]+', 'invoice' => '[0-9]+'])
        ->name('store.payment.webhook');
});

// Sandbox Payment Routes — local/testing ONLY. PRC-269: previously always-on,
// which let an attacker craft a token in production and fake a 3DS success →
// invoice marked paid without real money moving. Env gate added so production
// 404's the surface entirely.
if (app()->environment(['local', 'testing'])) {
    Route::prefix('sandbox')->name('sandbox.')->group(function () {
        Route::get('/payu/3d/{token}', [App\Http\Controllers\Sandbox\SandboxPaymentController::class, 'showPayu3D'])
            ->name('payu.3d');
        Route::post('/payu/3d/{token}/result', [App\Http\Controllers\Sandbox\SandboxPaymentController::class, 'handlePayu3DResult'])
            ->name('payu.3d.result');
    });
}

// Store Routes (store locale)
Route::middleware('store.locale')->group(function () {
    require __DIR__.'/store.php';
});

// Admin Routes (admin locale)
Route::middleware('admin.locale')->group(function () {
    require __DIR__.'/admin.php';
});

// Redirect clientarea to client dashboard
Route::get('/clientarea', function () {
    return redirect()->route('store.auth.dashboard');
});

// PRC-236 — Admin API token creation. Originally unbounded: any authenticated
// admin could create unlimited tokens (no permission, no rate, no audit). Now:
//   - throttle:10,1  → max 10 token creations per IP per minute
//   - can:manage api tokens → explicit permission key (gated, not implicit auth)
//   - 2FA enforced via EnsureTwoFactorVerified
Route::middleware([
    'auth:admin',
    \App\Http\Middleware\Admin\EnsureTwoFactorVerified::class,
    'throttle:10,1',
    'can:manage api tokens',
])->group(function () {
    Route::post('/sanctum/token', function (Request $request) {
        // PRC-209: admin tokens are intentionally full-scope (wildcard).
        // Surface is hardened by auth:admin + 2FA + can:manage api tokens +
        // throttle 10/min above. Token name + abilities passed explicitly so
        // future readers see the choice is deliberate, not a Sanctum default.
        $token = $request->user()->createToken('admin-token', ['*']);

        return response()->json(['token' => $token->plainTextToken]);
    })->name('sanctum.token');
});

// Module assets route — PRC-251: previously vulnerable to `..` path traversal
// (e.g. `/modules/payment/Stripe/../../../config/database.php`). Three guards
// now: (1) module name limited to `[A-Za-z0-9_]`, (2) file extension on an
// allow-list of static asset types, (3) realpath() must remain under the
// module's own directory.
Route::get('/modules/payment/{module}/{file}', function ($module, $file) {
    $allowedExtensions = ['svg', 'png', 'jpg', 'jpeg', 'gif', 'webp', 'ico', 'css', 'js'];
    $ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));

    if (! preg_match('/^[A-Za-z0-9_]+$/', $module)) {
        abort(404);
    }

    if (! in_array($ext, $allowedExtensions, true)) {
        abort(404);
    }

    $moduleDir = app_path('Modules/Payment/'.ucfirst($module));
    $modulePath = $moduleDir.'/'.$file;
    $realPath = realpath($modulePath);
    $realModuleDir = realpath($moduleDir);

    if ($realPath === false || $realModuleDir === false) {
        abort(404);
    }

    if (! str_starts_with($realPath, $realModuleDir.DIRECTORY_SEPARATOR)) {
        abort(404);
    }

    $mimeType = mime_content_type($realPath);

    return response()->file($realPath, [
        'Content-Type' => $mimeType,
        'Cache-Control' => 'public, max-age=31536000',
    ]);
})->where('file', '.*');
