<?php

namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Ferienpass\CoreBundle\ApplicationSystem\FirstComeApplicationSystem;
use Ferienpass\CoreBundle\ApplicationSystem\LotApplicationSystem;
use Ferienpass\CoreBundle\Controller\StorageFileController;
use Ferienpass\CoreBundle\Entity\User;
use Ferienpass\CoreBundle\EventListener\HttpCache\UserContextAwarePrivateResponseListener;
use Ferienpass\CoreBundle\Filesystem\PublicUri\StoragePublicUriProvider;
use Ferienpass\CoreBundle\Payments\Provider\PaymentProviderChain;
use Ferienpass\CoreBundle\Payments\Provider\PaymentProviderInterface;
use Ferienpass\CoreBundle\Payments\Provider\PmPaymentProvider;
use Ferienpass\CoreBundle\Security\ContaoBackendUser;
use Ferienpass\CoreBundle\Security\ContaoUserProvider;
use Ferienpass\CoreBundle\Security\UserChecker;
use Ferienpass\CoreBundle\Twig\NullSafeMarkdownRuntime;
use League\CommonMark\CommonMarkConverter;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;
use Twig\Extra\Markdown\LeagueMarkdown;
use Twig\Extra\Markdown\MarkdownRuntime;

return function(ContainerConfigurator $container): void {
    $services = $container->services()
        ->defaults()
        ->autoconfigure()
        ->autowire()
        ->bind('$messagesLogger', service('monolog.logger.ferienpass_messages'))
        ->bind('$notificationsLogger', service('monolog.logger.ferienpass_notifications'))
        ->bind('$webhookLogger', service('monolog.logger.ferienpass_webhook'))
    ;

    $services
        ->load('Ferienpass\\CoreBundle\\', '../src/')
        ->exclude([
            '../src/{DependencyInjection,Entity}',
            '../src/**/PdfExportConfig.php',
            // Registered explicitly below under `contao.listener.make_response_private`
            // (replaces Contao's stock listener). Excluding it from the FQCN
            // autoload prevents double registration of the #[AsEventListener]
            // handlers — otherwise every response would run the downgrade twice.
            '../src/EventListener/HttpCache/UserContextAwarePrivateResponseListener.php',
        ])
    ;

    // Override Contao's MakeResponsePrivateListener with our
    // user-context-aware variant. The override preserves the original service
    // id so other listeners wired to it (priorities, aliases) keep working.
    //
    // Safety model: per-user pages set `_cached_for_user_context = false` on
    // the request (UserAccountPage does this unconditionally;
    // CmsPageCacheOptOutListener propagates the tl_page DCA checkbox).
    // Read the docblock on UserContextAwarePrivateResponseListener before
    // changing anything here — getting the opt-out wrong previously caused
    // a cross-user data leak on /account.
    $services->set('contao.listener.make_response_private', UserContextAwarePrivateResponseListener::class)
        ->autoconfigure()
        ->arg('$scopeMatcher', service('contao.routing.scope_matcher'))
    ;

    // Tags autoconfigure
    $services
        ->instanceof(AbstractTypeExtension::class)
        ->tag('form.type_extension')
    ;
    $services
        ->instanceof(VoterInterface::class)
        ->tag('security.voter')
    ;

    $services->get(FirstComeApplicationSystem::class)
        ->tag('ferienpass.application_system')
    ;
    $services->get(LotApplicationSystem::class)
        ->tag('ferienpass.application_system')
    ;

    $services->get(UserChecker::class)
        ->tag('security.user_checker.contao_backend', ['priority' => 10])
        ->tag('security.user_checker.contao_frontend', ['priority' => 10])
        ->tag('security.user_checker.ferienpass_admin', ['priority' => 10])
        ->tag('security.user_checker.api', ['priority' => 10])
    ;

    $services->get(UserChecker\FrontendUserChecker::class)
        ->tag('security.user_checker.contao_frontend', ['priority' => 5])
    ;
    $services->get(UserChecker\AdminUserChecker::class)
        ->tag('security.user_checker.ferienpass_admin', ['priority' => 5])
    ;

    $services->set('ferienpass.security.contao_backend_user_provider', ContaoUserProvider::class)
        ->args([
            service('contao.framework'),
            ContaoBackendUser::class,
            service('doctrine'),
            User::class,
            'email'
        ])
    ;

    $services->set(StorageFileController::class)
        ->args([service_locator([
            'media' => service('contao.filesystem.virtual.media'),
            'attachments' => service('contao.filesystem.virtual.attachments'),
            'logos' => service('contao.filesystem.virtual.logos'),
            'export' => service('contao.filesystem.virtual.export'),
        ])]);

    $services->get(StoragePublicUriProvider::class)
        ->tag('contao.public_file_uri_provider')
    ;

    $services->instanceof(PaymentProviderInterface::class)
        ->tag('ferienpass.payment_provider')
    ;

    $services->get(PmPaymentProvider::class)->tag('ferienpass.payment_provider');

    $services->alias(PaymentProviderInterface::class, PaymentProviderChain::class);

    // Harden the `markdown_to_html` Twig filter (twig/markdown-extra).
    //
    // The filter is declared `is_safe=html`, so its output is NOT escaped by
    // Twig. By default the runtime (service `twig.markdown.default`) uses a
    // stock League CommonMark converter with `html_input=allow` and
    // `allow_unsafe_links=true`, which lets host-authored markdown inject raw
    // <script>/<iframe>/javascript: payloads — stored XSS on every call site
    // (offer/host descriptions, consents, emails).
    //
    // Override the converter ONCE here so every `markdown_to_html` call is safe
    // by default: strip embedded HTML and forbid unsafe links. The
    // `soft_break` renderer option mirrors config/packages/twig_extra.yaml.
    $services->set('twig.markdown.league_common_mark_converter', CommonMarkConverter::class)
        ->args([[
            'html_input' => 'strip',
            'allow_unsafe_links' => false,
            'renderer' => [
                'soft_break' => "<br />\n",
            ],
        ]]);

    $services->set('twig.markdown.default', LeagueMarkdown::class)
        ->args([service('twig.markdown.league_common_mark_converter')]);

    // Make `markdown_to_html` null-safe everywhere. The stock runtime types its
    // argument `string`, so a null value (e.g. a consent with no text) throws a
    // TypeError mid-render — a 500 on the whole page/component. Replace the
    // runtime with a null-coalescing subclass, matching the graceful behaviour
    // of the per-template `{% if %}` guards. The declared class stays
    // MarkdownRuntime so Twig's class-keyed runtime map (RuntimeLoaderPass)
    // still resolves `markdown_to_html` to this service.
    $services->set('twig.runtime.markdown', MarkdownRuntime::class)
        ->factory([NullSafeMarkdownRuntime::class, 'fromConverter'])
        ->args([service('twig.markdown.default')])
        ->tag('twig.runtime');

//    $services->set('ferienpass.security.contao_frontend_user_provider', ContaoUserProvider::class)
//        ->args([
//            service('contao.framework'),
//            ContaoFrontendUser::class,
//            service('doctrine'),
//            User::class,
//            'email'
//        ])
//    ;
};
