<?php
declare(strict_types=1);

/**
 * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
 * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
 * @link      https://cakephp.org CakePHP(tm) Project
 * @since     3.3.0
 * @license   https://opensource.org/licenses/mit-license.php MIT License
 */
namespace App;

use Cake\Core\Configure;
use Cake\Core\ContainerInterface;
use Cake\Datasource\FactoryLocator;
use Cake\Error\Middleware\ErrorHandlerMiddleware;
use Cake\Http\BaseApplication;
use Cake\Http\Middleware\BodyParserMiddleware;
use Cake\Http\Middleware\CsrfProtectionMiddleware;
use Cake\Http\MiddlewareQueue;
use Cake\ORM\Locator\TableLocator;
use Cake\Routing\Middleware\AssetMiddleware;
use Cake\Routing\Middleware\RoutingMiddleware;
use Cake\Http\Middleware\CspMiddleware;
use Cake\Http\Middleware\SecurityHeadersMiddleware;
use Cake\Http\Middleware\HttpsEnforcerMiddleware;
use Cake\Http\Middleware\EncryptedCookieMiddleware;

/**
 * Application setup class.
 *
 * This defines the bootstrapping logic and middleware layers you
 * want to use in your application.
 */
class Application extends BaseApplication
{
    /**
     * Load all the application configuration and bootstrap logic.
     *
     * @return void
     */
    public function bootstrap(): void
    {
        // Call parent to load bootstrap from files.
        parent::bootstrap();

        if (PHP_SAPI === 'cli') {
            $this->bootstrapCli();
        } else {
            FactoryLocator::add(
                'Table',
                (new TableLocator())->allowFallbackClass(false)
            );
        }

        /*
         * Only try to load DebugKit in development mode
         * Debug Kit should not be installed on a production system
         */
        if (Configure::read('debug')) {
            $this->addPlugin('DebugKit');
        }

        // Load more plugins here
    }

    /**
     * Setup the middleware queue your application will use.
     *
     * @param \Cake\Http\MiddlewareQueue $middlewareQueue The middleware queue to setup.
     * @return \Cake\Http\MiddlewareQueue The updated middleware queue.
     */
    public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
    {
               // Define the initial CSP policy array
        $policy = [
            // Control loading of scripts
            'script-src' => [
                'allow' => [
                    'https://www.google-analytics.com',
                    'https://cdnjs.cloudflare.com',
                ],
                'self' => true,
                'unsafe-inline' => false,
                'unsafe-eval' => false,
            ],

            // Control loading of styles
            'style-src' => [
                'allow' => [
                    'https://cdn.jsdelivr.net',
                    'https://cdnjs.cloudflare.com',
                ],
                'self' => true,
                'unsafe-inline' => false,
            ],

            // Control loading of images
            'img-src' => [
                  'allow' => [
                    'https://cdn.jsdelivr.net',
                    'https://cdnjs.cloudflare.com',
                ],
                'self' => true,
            ],

            // Control which endpoints the application can connect to
            'connect-src' => [
                'self' => true,
            ],

            // Control loading of frames
            'frame-src' => [
                'self' => true,
                'allow' => [
                    'https://maps.google.com',
                    'https://www.google.com',
                ],
            ],
            // Control where the site can be framed (frame-ancestors)
            'frame-ancestors' => [
                'self' => true,
            ],

            // Control loading of fonts
            'font-src' => [
                'self' => true,  // Allow fonts from the same origin
                'allow' => [
                    'https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/fonts/bootstrap-icons.woff2',
                    'https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/fonts/bootstrap-icons.woff',
                ],
            ],

            // If you don't allow any media sources, use 'none'
            'media-src' => [
                'self' => true,  // Disallow media sources
            ],

            // If you don't allow any objects like Flash, set 'none'
            'object-src' => [
                'self' => true,
            ],

            // If you don't use manifests, set 'none'
            'manifest-src' => [
                'self' => true,
            ],

            // Control where forms can be submitted
            'form-action' => [
                'self' => true,  // Only allow forms to be submitted to your domain
            ],

            // Require Trusted Types for scripts
            'require-trusted-types-for' => [
                'script',
            ],
        ];

        // Enable automatic nonce addition to script & CSS link tags.
        $csp = new CspMiddleware($policy, [
            'scriptNonce' => true,
            'styleNonce' => true,
        ]);

        $https = new HttpsEnforcerMiddleware([
            'redirect' => true,
            'hsts' => [
                // How long the header value should be cached for.
                'maxAge' => 60 * 60 * 24 * 365,
                // should this policy apply to subdomains?
                'includeSubDomains' => true,
                // Should the header value be cacheable in google's HSTS preload
                // service? While not part of the spec it is widely implemented.
                'preload' => true,
            ]
        ]);

        $cookies = new EncryptedCookieMiddleware(
            // Names of cookies to protect
            ['csrfToken', 'protected'],
            Configure::read('Security.cookieKey')
        );
        
         $securityHeaders = new SecurityHeadersMiddleware();
        $securityHeaders
            ->setCrossDomainPolicy()   // Restricts how your content can be used on other domains
            ->setReferrerPolicy()      // Controls how much referrer information is sent
            ->setXFrameOptions()       // Prevents clickjacking by disallowing framing
            ->setXssProtection()       // Enables XSS protection in the browser
            ->noOpen()                 // Prevents file downloads from being opened in the context of your site
            ->noSniff();               // Prevents MIME type sniffing by the browser


        $middlewareQueue
            // Catch any exceptions in the lower layers,
            // and make an error page/response
            ->add(new ErrorHandlerMiddleware(Configure::read('Error'), $this))

            // Handle plugin/theme assets like CakePHP normally does.
            ->add(new AssetMiddleware([
                'cacheTime' => Configure::read('Asset.cacheTime'),
            ]))

            // Add routing middleware.
            // If you have a large number of routes connected, turning on routes
            // caching in production could improve performance.
            // See https://github.com/CakeDC/cakephp-cached-routing
            ->add(new RoutingMiddleware($this))

            // Parse various types of encoded request bodies so that they are
            // available as array through $request->getData()
            // https://book.cakephp.org/4/en/controllers/middleware.html#body-parser-middleware
            ->add(new BodyParserMiddleware())

            // Cross Site Request Forgery (CSRF) Protection Middleware
            // https://book.cakephp.org/4/en/security/csrf.html#cross-site-request-forgery-csrf-middleware
            ->add(new CsrfProtectionMiddleware([
                'secure' => true,
                'httponly' => true,
                'expiry' => '+1 hour',
                'samesite' => 'Strict',
            ]))

            /**
             * Adds HTTPS headers to the response.
             *
             * This method is responsible for adding headers that enforce HTTPS usage,
             * ensuring that the application is accessed securely over the HTTPS protocol.
             *
             * @param \Cake\Http\Response $https The HTTPS headers to be added to the response.
             * @return \Cake\Http\Response The updated response with HTTPS headers applied.
             */
            ->add($https)

            /**
             * Adds cookie settings to the HTTP response.
             *
             * This method adds the provided cookies to the current response object. 
             * It is typically used to manage client-side cookies for the application.
             *
             * @param \Cake\Http\Cookie\Cookie[] $cookies An array of cookies to be added to the response.
             * 
             * @return $this
             */
            ->add($cookies)


            /**
             * Adds Content Security Policy (CSP) settings to the HTTP response.
             *
             * This method adds the specified CSP settings to the response to enhance
             * the security of the application by controlling which resources (e.g., scripts, styles) 
             * are allowed to load. It is typically part of setting up security headers.
             *
             * @param \Cake\Http\Response $csp The Content Security Policy settings to be applied.
             * 
             * @return $this
             */
            ->add($csp)
            
               /**
             * Adds security headers to the response.
             *
             * This method is responsible for adding various security-related HTTP headers
             * to the response to enhance the security of the application.
             *
             * @param \Cake\Http\Response $securityHeaders The security headers to be added to the response.
             * @return \Cake\Http\Response The updated response with security headers applied.
             */
            ->add($securityHeaders);


        return $middlewareQueue;
    }

    /**
     * Register application container services.
     *
     * @param \Cake\Core\ContainerInterface $container The Container to update.
     * @return void
     * @link https://book.cakephp.org/4/en/development/dependency-injection.html#dependency-injection
     */
    public function services(ContainerInterface $container): void
    {
    }

    /**
     * Bootstrapping for CLI application.
     *
     * That is when running commands.
     *
     * @return void
     */
    protected function bootstrapCli(): void
    {
        $this->addOptionalPlugin('Bake');

        $this->addPlugin('Migrations');

        // Load more plugins here
    }
}
