<?php

declare(strict_types=1);

namespace Flowaxy\Infrastructure\Security;

/**
 * Генератор CSP (Content Security Policy) заголовков
 *
 * @package Flowaxy\Infrastructure\Security
 */
final class CspHeaders
{
    private array $directives = [];

    /**
     * Добавить директиву CSP
     *
     * @param string $directive Название директивы (например, 'default-src')
     * @param array<int, string> $sources Источники
     * @return self
     */
    public function addDirective(string $directive, array $sources): self
    {
        $this->directives[$directive] = $sources;
        return $this;
    }

    /**
     * Добавить источники к существующей директиве
     *
     * @param string $directive Название директивы (например, 'script-src')
     * @param array<int, string> $sources Дополнительные источники
     * @return self
     */
    public function addSources(string $directive, array $sources): self
    {
        if (!isset($this->directives[$directive])) {
            $this->directives[$directive] = [];
        }
        $this->directives[$directive] = array_merge($this->directives[$directive], $sources);
        $this->directives[$directive] = array_unique($this->directives[$directive]);
        return $this;
    }

    /**
     * Сгенерировать CSP заголовок
     *
     * @return string
     */
    public function generate(): string
    {
        $parts = [];

        foreach ($this->directives as $directive => $sources) {
            if (!empty($sources)) {
                $parts[] = $directive . ' ' . implode(' ', $sources);
            }
        }

        return implode('; ', $parts);
    }

    /**
     * Установить строгую политику по умолчанию
     *
     * @return self
     */
    public function setStrict(): self
    {
        $this->directives = [
            'default-src' => ["'self'"],
            'script-src' => ["'self'", "'unsafe-inline'"],
            'style-src' => ["'self'", "'unsafe-inline'"],
            'img-src' => ["'self'", 'data:', 'https:'],
            'font-src' => ["'self'"],
            'connect-src' => ["'self'"],
            'frame-ancestors' => ["'none'"],
        ];

        return $this;
    }

    /**
     * Создать CSP с настройками по умолчанию (строгая политика + разрешенные CDN)
     *
     * @return self
     */
    public static function createDefault(): self
    {
        $csp = new self();
        $csp->setStrict();
        // Добавляем разрешенные CDN для скриптов и стилей
        $csp->addSources('script-src', ['https://cdn.jsdelivr.net', 'https://cdnjs.cloudflare.com', 'https://code.jquery.com']);
        $csp->addSources('style-src', ['https://cdn.jsdelivr.net', 'https://cdnjs.cloudflare.com', 'https://fonts.googleapis.com']);
        $csp->addSources('font-src', ['https://cdn.jsdelivr.net', 'https://cdnjs.cloudflare.com', 'https://fonts.gstatic.com', 'data:']);

        return $csp;
    }
}
