<?php

use App\Http\Middleware\ForceJsonResponse;
use App\Http\Middleware\HandleAppearance;
use App\Http\Middleware\HandleInertiaRequests;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
use Illuminate\Http\Middleware\AddLinkHeadersForPreloadedAssets;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Inertia\Inertia;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        api: __DIR__.'/../routes/api.php',
        commands: __DIR__.'/../routes/console.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->encryptCookies(except: ['appearance', 'sidebar_state']);

        // Stripe signs its webhooks itself; CSRF tokens don't apply to them.
        $middleware->validateCsrfTokens(except: ['stripe/*']);

        $middleware->api(prepend: [
            ForceJsonResponse::class,
        ]);

        $middleware->web(append: [
            HandleAppearance::class,
            HandleInertiaRequests::class,
            AddLinkHeadersForPreloadedAssets::class,
        ]);
    })
    ->withExceptions(
        function (Exceptions $exceptions) {

            // A Stripe webhook whose signature does not verify is rejected by Cashier's
            // middleware before any controller runs, so nothing would otherwise record
            // it — the delivery looks, from this side, exactly like one that never
            // arrived. Almost always a STRIPE_WEBHOOK_SECRET that does not match the
            // endpoint's signing secret (they differ between `stripe listen` and each
            // Dashboard endpoint — see SUBSCRIPTIONS.md §1).
            $exceptions->render(
                function (AccessDeniedHttpException $e, Request $request) {
                    if (! $request->is('stripe/*')) {
                        return null;
                    }

                    Log::channel('stripe')->error('Webhook signature rejected', [
                        'path' => $request->path(),
                        'reason' => $e->getMessage(),
                        'has_signature_header' => $request->hasHeader('Stripe-Signature'),
                    ]);

                    // Plain 403 rather than the Inertia error page below: the caller is
                    // Stripe, which reads the status code and retries.
                    return response()->json(['message' => 'Invalid signature.'], 403);
                }
            );

            // https://inertiajs.com/docs/v3/advanced/error-handling
            $exceptions->respond(function (Response $response, Throwable $exception, Request $request) {
                // Stripe is a machine reading status codes, not a browser: leave the
                // webhook's plain JSON response alone rather than dressing it up as the
                // Inertia error page.
                if ($request->is('stripe/*')) {
                    return $response;
                }

                $message = null;

                if ($exception instanceof \Symfony\Component\HttpKernel\Exception\HttpException) {
                    $msg = $exception->getMessage();

                    if ($response->getStatusCode() === 403) {
                        if (str_contains($msg, 'signature') || str_contains($msg, 'Invalid signature')) {
                            $message = 'The verification link is expired or invalid, please try again.';
                        } elseif (str_contains($msg, 'password')) {
                            $message = 'The password reset link has expired or is invalid, please try again.';
                        } else {
                            $message = 'Unauthorized action, this link has expired or is invalid, please try again.';
                        }
                    }
                }

                if (in_array($response->getStatusCode(), [500, 503, 404, 403, 401])) {
                    return Inertia::render('Error', [
                        'status' => $response->getStatusCode(),
                        'message' => $message,
                    ])
                        ->toResponse($request)
                        ->setStatusCode($response->getStatusCode());
                } elseif ($response->getStatusCode() === 419) {
                    return back()->with([
                        'message' => 'The page expired, please try again.',
                    ]);
                }

                return $response;
            });

            // https://laravel.com/docs/12.x/errors#rendering-exceptions-as-json
            $exceptions->shouldRenderJsonWhen(
                function (Request $request) {
                    if ($request->is('api/*')) {
                        return true;
                    }

                    return $request->expectsJson();
                }
            );

            $exceptions->render(
                function (NotFoundHttpException $e, Request $request) {
                    if ($request->is('api/*')) {
                        return response()->json(
                            [
                                'message' => 'Route not found.',
                            ],
                            404
                        );
                    }
                }
            );
        }
    )
    ->create();
