<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\View;
use Illuminate\Support\Facades\Vite;
use Symfony\Component\HttpFoundation\Response;

class ContentSecurityPolicy
{
    public function handle(Request $request, Closure $next): Response
    {
        $scriptNonce = base64_encode(random_bytes(16));
        $styleNonce = base64_encode(random_bytes(16));

        $request->attributes->set('csp_script_nonce', $scriptNonce);
        $request->attributes->set('csp_style_nonce', $styleNonce);

        View::share('cspNonce', $scriptNonce);
        View::share('cspStyleNonce', $styleNonce);

        Vite::useCspNonce($scriptNonce);

        $response = $next($request);

        if (app()->environment('local') || config('app.debug')) {
            return $response;
        }

        if ($response->headers->has('Content-Security-Policy')) {
            return $response;
        }

        $matomoOrigin = $this->normaliseOrigin(config('services.matomo.url'));
        $scriptSources = [
            "'self'",
            "'nonce-{$scriptNonce}'",
            'https://cdn.paddle.com',
            'https://global.localizecdn.com',
        ];

        $styleSources = [
            "'self'",
            "'nonce-{$styleNonce}'",
            'https:',
        ];

        $connectSources = [
            "'self'",
            'https://api.paddle.com',
            'https://sandbox-api.paddle.com',
            'https://checkout.paddle.com',
            'https://sandbox-checkout.paddle.com',
            'https://checkout-service.paddle.com',
            'https://sandbox-checkout-service.paddle.com',
            'https://global.localizecdn.com',
        ];

        $frameSources = [
            "'self'",
            'https://checkout.paddle.com',
            'https://sandbox-checkout.paddle.com',
            'https://checkout-service.paddle.com',
            'https://sandbox-checkout-service.paddle.com',
        ];

        $imgSources = [
            "'self'",
            'data:',
            'blob:',
            'https:',
        ];

        $fontSources = [
            "'self'",
            'data:',
            'https:',
        ];

        $mediaSources = [
            "'self'",
            'data:',
            'blob:',
            'https:',
        ];

        if ($matomoOrigin) {
            $scriptSources[] = $matomoOrigin;
            $connectSources[] = $matomoOrigin;
            $imgSources[] = $matomoOrigin;
        }

        $isDev = app()->environment(['local', 'development']) || config('app.debug');

        if ($isDev) {
            $devHosts = [
                'http://fotospiel-app.test:5173',
                'http://127.0.0.1:5173',
                'https://localhost:5173',
                'https://127.0.0.1:5173',
            ];
            $wsHosts = [
                'ws://fotospiel-app.test:5173',
                'ws://127.0.0.1:5173',
                'wss://localhost:5173',
                'wss://127.0.0.1:5173',
            ];

            $scriptSources = array_merge($scriptSources, $devHosts, ["'unsafe-inline'", "'unsafe-eval'"]);
            $styleSources = array_merge($styleSources, $devHosts, ["'unsafe-inline'"]);
            $connectSources = array_merge($connectSources, $devHosts, $wsHosts);
            $fontSources = array_merge($fontSources, $devHosts);
            $mediaSources = array_merge($mediaSources, $devHosts);
        }

        $styleSources[] = 'data:';
        $connectSources[] = 'https:';
        $fontSources[] = 'https:';

        $directives = [
            'default-src' => ["'self'"],
            'script-src' => array_unique($scriptSources),
            'style-src' => array_unique($styleSources),
            'img-src' => array_unique($imgSources),
            'font-src' => array_unique($fontSources),
            'connect-src' => array_unique($connectSources),
            'media-src' => array_unique($mediaSources),
            'frame-src' => array_unique($frameSources),
            'form-action' => ["'self'"],
            'base-uri' => ["'self'"],
            'object-src' => ["'none'"],
            'frame-ancestors' => ["'self'"],
        ];

        $csp = collect($directives)
            ->map(fn ($values, $directive) => $directive.' '.implode(' ', array_filter($values)))
            ->implode('; ');

        $response->headers->set('Content-Security-Policy', $csp);

        return $response;
    }

    private function normaliseOrigin(?string $url): ?string
    {
        if (! $url) {
            return null;
        }

        $parsed = parse_url($url);
        if (! $parsed || ! isset($parsed['scheme'], $parsed['host'])) {
            return null;
        }

        $origin = strtolower($parsed['scheme'].'://'.$parsed['host']);

        if (isset($parsed['port'])) {
            $origin .= ':'.$parsed['port'];
        }

        return $origin;
    }
}
