# This file is the entry point to configure your own services.
# Files in the packages/ subdirectory configure your dependencies.

# Put parameters here that don't need to change on each machine where the app is deployed
# https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration
parameters:
    tenant.base_domain: '%env(TENANT_BASE_DOMAIN)%'
    app.url: '%env(APP_URL)%'

services:
    # default configuration for services in this file
    _defaults:
        autowire: true      # Automatically injects dependencies in your services.
        autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
        bind:
            # Bind activation tokens cache pool (7-day TTL)
            Psr\Cache\CacheItemPoolInterface $activationTokensCache: '@activation_tokens.cache'
            # Bind users cache pool (no TTL - persistent data)
            Psr\Cache\CacheItemPoolInterface $usersCache: '@users.cache'
            # Bind refresh tokens cache pool (7-day TTL)
            Psr\Cache\CacheItemPoolInterface $refreshTokensCache: '@refresh_tokens.cache'
            # Bind password reset tokens cache pool (1-hour TTL)
            Psr\Cache\CacheItemPoolInterface $passwordResetTokensCache: '@password_reset_tokens.cache'
            # Bind sessions cache pool (7-day TTL)
            Psr\Cache\CacheItemPoolInterface $sessionsCache: '@sessions.cache'
            # Bind student guardians cache pool (no TTL - persistent data)
            Psr\Cache\CacheItemPoolInterface $studentGuardiansCache: '@student_guardians.cache'
            # Bind paginated queries cache pool (1h TTL, tag-aware)
            Symfony\Contracts\Cache\TagAwareCacheInterface $paginatedQueriesCache: '@paginated_queries.cache'
            # Bind named message buses
            Symfony\Component\Messenger\MessageBusInterface $eventBus: '@event.bus'
            Symfony\Component\Messenger\MessageBusInterface $commandBus: '@command.bus'
            Symfony\Component\Messenger\MessageBusInterface $queryBus: '@query.bus'

    # makes classes in src/ available to be used as services
    # this creates a service per class whose id is the fully-qualified class name
    App\:
        resource: '../src/'
        exclude:
            - '../src/DependencyInjection/'
            - '../src/Entity/'
            - '../src/Kernel.php'
            # Exclude Domain layers - they should be pure PHP with no framework deps
            - '../src/*/Domain/'

    # Domain services need to be registered explicitly to avoid framework coupling
    # Example: App\Administration\Application\Command\:
    #     resource: '../src/Administration/Application/Command/'

    # Tenant services
    App\Shared\Infrastructure\Tenant\TenantResolver:
        arguments:
            $baseDomain: '%tenant.base_domain%'

    App\Shared\Infrastructure\Persistence\Doctrine\TenantAwareConnection:
        alias: doctrine.dbal.default_connection

    App\Shared\Infrastructure\Tenant\TenantDatabaseSwitcher:
        alias: doctrine.dbal.default_connection

    # TenantRegistry est configuré par environnement :
    # - dev: config/packages/dev/tenant.yaml (tenants de test)
    # - prod: à configurer via admin ou env vars

    App\Shared\Infrastructure\Tenant\Command\CreateTenantDatabaseCommand:
        arguments:
            $masterDatabaseUrl: '%env(DATABASE_URL)%'

    App\Shared\Infrastructure\Tenant\Command\TenantMigrateCommand:
        arguments:
            $projectDir: '%kernel.project_dir%'

    # Administration services
    # Bind Repository interfaces to their implementations
    App\Administration\Domain\Repository\ActivationTokenRepository:
        alias: App\Administration\Infrastructure\Persistence\Redis\RedisActivationTokenRepository

    App\Administration\Infrastructure\Persistence\Cache\CachedUserRepository:
        arguments:
            $inner: '@App\Administration\Infrastructure\Persistence\Doctrine\DoctrineUserRepository'

    App\Administration\Domain\Repository\UserRepository:
        alias: App\Administration\Infrastructure\Persistence\Cache\CachedUserRepository

    App\Administration\Infrastructure\Console\MigrateUsersToPostgresCommand:
        arguments:
            $source: '@App\Administration\Infrastructure\Persistence\Cache\CacheUserRepository'
            $target: '@App\Administration\Infrastructure\Persistence\Doctrine\DoctrineUserRepository'

    App\Administration\Application\Port\PasswordHasher:
        alias: App\Administration\Infrastructure\Security\SymfonyPasswordHasher

    # Clock interface binding
    App\Shared\Domain\Clock:
        alias: App\Shared\Infrastructure\Clock\SystemClock

    # Domain policies (need explicit registration as Domain is excluded from autowiring)
    App\Administration\Domain\Policy\ConsentementParentalPolicy:
        autowire: true

    # Email handlers
    App\Administration\Infrastructure\Messaging\SendActivationConfirmationHandler:
        arguments:
            $appUrl: '%app.url%'

    App\Administration\Infrastructure\Messaging\SendPasswordResetEmailHandler:
        arguments:
            $appUrl: '%app.url%'

    App\Administration\Infrastructure\Messaging\SendPasswordResetConfirmationHandler:
        arguments:
            $appUrl: '%app.url%'

    App\Shared\Infrastructure\Tenant\TenantUrlBuilder:
        arguments:
            $appUrl: '%app.url%'
            $baseDomain: '%tenant.base_domain%'

    # Audit Logger Service (writes to append-only audit_log table)
    App\Shared\Application\Port\AuditLogger:
        alias: App\Shared\Infrastructure\Audit\AuditLogger

    App\Shared\Infrastructure\Audit\AuditLogger:
        arguments:
            $connection: '@doctrine.dbal.master_connection'
            $appSecret: '%env(APP_SECRET)%'

    App\Shared\Infrastructure\Audit\AuditLogRepository:
        arguments:
            $connection: '@doctrine.dbal.master_connection'

    App\Shared\Infrastructure\Console\ArchiveAuditLogsCommand:
        arguments:
            $connection: '@doctrine.dbal.master_connection'

    App\Shared\Infrastructure\Console\ReviewFailedMessagesCommand:
        arguments:
            $connection: '@doctrine.dbal.master_connection'

    # Audit log handlers (use AuditLogger to write to database)
    App\Shared\Infrastructure\Audit\Handler\AuditAuthenticationHandler:
        arguments:
            $appSecret: '%env(APP_SECRET)%'

    # JWT Authentication
    App\Administration\Infrastructure\Security\JwtPayloadEnricher:
        tags:
            - { name: kernel.event_listener, event: lexik_jwt_authentication.on_jwt_created, method: onJWTCreated }

    App\Administration\Infrastructure\Security\DatabaseUserProvider:
        arguments:
            $userRepository: '@App\Administration\Domain\Repository\UserRepository'

    App\SuperAdmin\Infrastructure\Persistence\Doctrine\DoctrineSuperAdminRepository:
        arguments:
            $connection: '@doctrine.dbal.master_connection'

    App\SuperAdmin\Infrastructure\Persistence\Doctrine\DoctrineEstablishmentRepository:
        arguments:
            $connection: '@doctrine.dbal.master_connection'

    App\SuperAdmin\Application\Query\GetEstablishmentsMetrics\GetEstablishmentsMetricsHandler:
        arguments:
            $connection: '@doctrine.dbal.master_connection'

    # Refresh Token Repository
    App\Administration\Domain\Repository\RefreshTokenRepository:
        alias: App\Administration\Infrastructure\Persistence\Redis\RedisRefreshTokenRepository

    # Password Reset Token Repository
    App\Administration\Domain\Repository\PasswordResetTokenRepository:
        alias: App\Administration\Infrastructure\Persistence\Redis\RedisPasswordResetTokenRepository

    # Session Repository
    App\Administration\Domain\Repository\SessionRepository:
        alias: App\Administration\Infrastructure\Persistence\Redis\RedisSessionRepository

    # Class Repository (Story 2.1 - Gestion des classes)
    App\Administration\Domain\Repository\ClassRepository:
        alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrineClassRepository

    # Subject Repository (Story 2.2 - Gestion des matières)
    App\Administration\Domain\Repository\SubjectRepository:
        alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrineSubjectRepository

    # Period Configuration Repository (Story 2.3 - Gestion des périodes)
    App\Administration\Domain\Repository\PeriodConfigurationRepository:
        alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrinePeriodConfigurationRepository

    # Grading Configuration Repository (Story 2.4 - Mode de notation)
    App\Administration\Domain\Repository\GradingConfigurationRepository:
        alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrineGradingConfigurationRepository

    # Class Assignment (Story 3.0 - Affectation élèves aux classes)
    App\Administration\Domain\Repository\ClassAssignmentRepository:
        alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrineClassAssignmentRepository

    # Teacher Assignment (Story 2.8 - Affectation enseignants)
    App\Administration\Domain\Repository\TeacherAssignmentRepository:
        alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrineTeacherAssignmentRepository

    App\Administration\Application\Port\TeacherAssignmentChecker:
        alias: App\Administration\Infrastructure\Service\RepositoryTeacherAssignmentChecker

    # Teacher Replacement Repository (Story 2.9 - Remplaçants temporaires)
    App\Scolarite\Domain\Repository\TeacherReplacementRepository:
        alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineTeacherReplacementRepository

    # Homework (Story 5.1 - Création de devoirs)
    App\Scolarite\Domain\Repository\HomeworkRepository:
        alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineHomeworkRepository

    App\Scolarite\Domain\Repository\HomeworkRuleExceptionRepository:
        alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineHomeworkRuleExceptionRepository

    App\Scolarite\Domain\Repository\HomeworkAttachmentRepository:
        alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineHomeworkAttachmentRepository

    # Homework Submissions (Story 5.10 - Rendu de devoir par l'élève)
    App\Scolarite\Domain\Repository\HomeworkSubmissionRepository:
        alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineHomeworkSubmissionRepository

    App\Scolarite\Domain\Repository\SubmissionAttachmentRepository:
        alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineSubmissionAttachmentRepository

    App\Scolarite\Application\Port\ClassStudentsReader:
        alias: App\Scolarite\Infrastructure\Service\DoctrineClassStudentsReader

    App\Scolarite\Domain\Service\DueDateValidator:
        autowire: true

    App\Scolarite\Domain\Service\HomeworkDuplicator:
        autowire: true

    App\Scolarite\Application\Port\HtmlSanitizer:
        alias: App\Scolarite\Infrastructure\Service\HomeworkHtmlSanitizer

    App\Scolarite\Infrastructure\Service\HomeworkHtmlSanitizer:
        arguments:
            $homeworkSanitizer: '@html_sanitizer.sanitizer.homework_sanitizer'

    App\Scolarite\Application\Port\FileStorage:
        alias: App\Scolarite\Infrastructure\Storage\LocalFileStorage

    App\Scolarite\Infrastructure\Storage\LocalFileStorage:
        arguments:
            $storagePath: '%kernel.project_dir%/var/storage'

    # Schedule (Story 4.1 - Emploi du temps)
    App\Scolarite\Domain\Repository\ScheduleSlotRepository:
        alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineScheduleSlotRepository

    App\Scolarite\Domain\Repository\ScheduleExceptionRepository:
        alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineScheduleExceptionRepository

    App\Scolarite\Domain\Service\ScheduleConflictDetector:
        autowire: true

    App\Scolarite\Application\Port\EnseignantAffectationChecker:
        alias: App\Scolarite\Infrastructure\Service\CurrentYearEnseignantAffectationChecker

    App\Scolarite\Domain\Repository\EvaluationRepository:
        alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineEvaluationRepository

    App\Scolarite\Domain\Repository\GradeRepository:
        alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineGradeRepository

    App\Scolarite\Domain\Repository\AppreciationTemplateRepository:
        alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineAppreciationTemplateRepository

    App\Scolarite\Application\Port\EvaluationGradesChecker:
        alias: App\Scolarite\Infrastructure\Service\NoGradesEvaluationGradesChecker

    # Competency repositories (Story 6.5 - Mode compétences)
    App\Scolarite\Domain\Repository\CompetencyFrameworkRepository:
        alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineCompetencyFrameworkRepository

    App\Scolarite\Domain\Repository\CompetencyRepository:
        alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineCompetencyRepository

    App\Scolarite\Domain\Repository\CompetencyEvaluationRepository:
        alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineCompetencyEvaluationRepository

    App\Scolarite\Domain\Repository\StudentCompetencyResultRepository:
        alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineStudentCompetencyResultRepository

    App\Scolarite\Domain\Repository\CustomCompetencyLevelRepository:
        alias: App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineCustomCompetencyLevelRepository

    # Averages (Story 6.3 - Calcul automatique des moyennes)
    App\Scolarite\Domain\Service\AverageCalculator:
        autowire: true

    App\Scolarite\Application\Service\RecalculerMoyennesService:
        autowire: true

    App\Scolarite\Application\Port\PeriodFinder:
        alias: App\Scolarite\Infrastructure\Service\DoctrinePeriodFinder

    App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineEvaluationStatisticsRepository:
        autowire: true

    App\Scolarite\Infrastructure\Cache\CachingEvaluationStatisticsRepository:
        arguments:
            $inner: '@App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineEvaluationStatisticsRepository'
            $cache: '@student_averages.cache'

    App\Scolarite\Domain\Repository\EvaluationStatisticsRepository:
        alias: App\Scolarite\Infrastructure\Cache\CachingEvaluationStatisticsRepository

    App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineStudentAverageRepository:
        autowire: true

    App\Scolarite\Infrastructure\Cache\CachingStudentAverageRepository:
        arguments:
            $inner: '@App\Scolarite\Infrastructure\Persistence\Doctrine\DoctrineStudentAverageRepository'
            $cache: '@student_averages.cache'

    App\Scolarite\Domain\Repository\StudentAverageRepository:
        alias: App\Scolarite\Infrastructure\Cache\CachingStudentAverageRepository

    # Super Admin Repositories (Story 2.10 - Multi-établissements)
    App\SuperAdmin\Domain\Repository\SuperAdminRepository:
        alias: App\SuperAdmin\Infrastructure\Persistence\Doctrine\DoctrineSuperAdminRepository

    App\SuperAdmin\Domain\Repository\EstablishmentRepository:
        alias: App\SuperAdmin\Infrastructure\Persistence\Doctrine\DoctrineEstablishmentRepository

    # School Calendar Repository (Story 2.11 - Calendrier scolaire)
    App\Administration\Domain\Model\SchoolCalendar\SchoolCalendarRepository:
        alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrineSchoolCalendarRepository

    App\Administration\Application\Port\OfficialCalendarProvider:
        alias: App\Administration\Infrastructure\Service\JsonOfficialCalendarProvider

    App\Administration\Infrastructure\Service\JsonOfficialCalendarProvider:
        arguments:
            $dataDirectory: '%kernel.project_dir%/var/data/calendar'

    # School Branding (Story 2.13 - Personnalisation visuelle)
    App\Administration\Domain\Model\SchoolBranding\ContrastValidator:
        autowire: true

    App\Administration\Domain\Repository\SchoolBrandingRepository:
        alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrineSchoolBrandingRepository

    # Homework Rules Repository
    App\Administration\Domain\Repository\HomeworkRulesRepository:
        alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrineHomeworkRulesRepository

    App\Administration\Domain\Repository\HomeworkRulesHistoryRepository:
        alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrineHomeworkRulesHistoryRepository

    App\Administration\Application\Port\LogoStorage:
        alias: App\Administration\Infrastructure\Storage\LocalLogoStorage

    App\Administration\Infrastructure\Storage\LocalLogoStorage:
        arguments:
            $uploadDir: '%kernel.project_dir%/public/uploads'
            $publicPath: '/uploads'

    App\Administration\Application\Port\ImageProcessor:
        alias: App\Administration\Infrastructure\Storage\ImagickImageProcessor

    # Import Batch Repository (Story 3.1 - Import élèves via CSV)
    App\Administration\Domain\Repository\ImportBatchRepository:
        alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrineImportBatchRepository

    # Saved Column Mapping Repository (Story 3.1 - T3.3 Réutilisation des mappings)
    App\Administration\Domain\Repository\SavedColumnMappingRepository:
        alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrineSavedColumnMappingRepository

    # Teacher Import Batch Repository (Story 3.2 - Import enseignants via CSV)
    App\Administration\Domain\Repository\TeacherImportBatchRepository:
        alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrineTeacherImportBatchRepository

    # Saved Teacher Column Mapping Repository (Story 3.2 - Réutilisation des mappings enseignants)
    App\Administration\Domain\Repository\SavedTeacherColumnMappingRepository:
        alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrineSavedTeacherColumnMappingRepository

    # Parent Invitation Repository (Story 3.3 - Invitation parents)
    App\Administration\Domain\Repository\ParentInvitationRepository:
        alias: App\Administration\Infrastructure\Persistence\Doctrine\DoctrineParentInvitationRepository

    # Student Guardian Repository (Story 2.7 - Liaison parents-enfants)
    App\Administration\Infrastructure\Persistence\Cache\CacheStudentGuardianRepository:
        arguments:
            $inner: '@App\Administration\Infrastructure\Persistence\Doctrine\DoctrineStudentGuardianRepository'

    App\Administration\Domain\Repository\StudentGuardianRepository:
        alias: App\Administration\Infrastructure\Persistence\Cache\CacheStudentGuardianRepository

    # Paginated Read Model Ports
    App\Administration\Application\Port\PaginatedUsersReader:
        alias: App\Administration\Infrastructure\ReadModel\DbalPaginatedUsersReader

    App\Administration\Application\Port\PaginatedClassesReader:
        alias: App\Administration\Infrastructure\ReadModel\DbalPaginatedClassesReader

    App\Administration\Application\Port\PaginatedSubjectsReader:
        alias: App\Administration\Infrastructure\ReadModel\DbalPaginatedSubjectsReader

    App\Administration\Application\Port\PaginatedAssignmentsReader:
        alias: App\Administration\Infrastructure\ReadModel\DbalPaginatedAssignmentsReader

    App\Administration\Application\Port\PaginatedParentInvitationsReader:
        alias: App\Administration\Infrastructure\ReadModel\DbalPaginatedParentInvitationsReader

    App\Administration\Application\Port\PaginatedStudentImageRightsReader:
        alias: App\Administration\Infrastructure\ReadModel\DbalPaginatedStudentImageRightsReader

    # GradeExistenceChecker (stub until Notes module exists)
    App\Administration\Application\Port\GradeExistenceChecker:
        alias: App\Administration\Infrastructure\Service\NoOpGradeExistenceChecker

    # ActiveRoleStore (session-scoped cache for active role switching)
    App\Administration\Application\Port\ActiveRoleStore:
        alias: App\Administration\Infrastructure\Service\CacheActiveRoleStore

    # GeoLocation Service (null implementation - no geolocation)
    App\Administration\Application\Port\GeoLocationService:
        alias: App\Administration\Infrastructure\Service\NullGeoLocationService

    # Password Reset Processor with rate limiters
    App\Administration\Infrastructure\Api\Processor\RequestPasswordResetProcessor:
        arguments:
            $passwordResetByEmailLimiter: '@limiter.password_reset_by_email'
            $passwordResetByIpLimiter: '@limiter.password_reset_by_ip'

    # Parent Activation Processor with rate limiter
    App\Administration\Infrastructure\Api\Processor\ActivateParentInvitationProcessor:
        arguments:
            $parentActivationByIpLimiter: '@limiter.parent_activation_by_ip'

    # Login handlers
    App\Administration\Infrastructure\Security\LoginSuccessHandler:
        tags:
            - { name: kernel.event_listener, event: lexik_jwt_authentication.on_authentication_success, method: onAuthenticationSuccess }

    App\Administration\Infrastructure\Security\LoginFailureHandler:
        tags:
            - { name: security.authentication_failure_handler, firewall: api_login }

    # Rate Limiter (délai Fibonacci + CAPTCHA + blocage IP)
    App\Shared\Infrastructure\RateLimit\LoginRateLimiter:
        arguments:
            $cache: '@cache.rate_limiter'

    App\Shared\Infrastructure\RateLimit\LoginRateLimiterInterface:
        alias: App\Shared\Infrastructure\RateLimit\LoginRateLimiter

    # Rate Limit Listener (vérifie le rate limit AVANT authentification)
    App\Shared\Infrastructure\RateLimit\LoginRateLimitListener:
        arguments:
            $rateLimiterCache: '@cache.rate_limiter'

    # Turnstile CAPTCHA Validator
    # failOpen: true en dev (ne pas bloquer si API down), false en prod (sécurité)
    App\Shared\Infrastructure\Captcha\TurnstileValidator:
        arguments:
            $secretKey: '%env(TURNSTILE_SECRET_KEY)%'
            $failOpen: '%env(bool:default::TURNSTILE_FAIL_OPEN)%'

    App\Shared\Infrastructure\Captcha\TurnstileValidatorInterface:
        alias: App\Shared\Infrastructure\Captcha\TurnstileValidator

    # =============================================================================
    # Monitoring & Observability (Story 1.8)
    # =============================================================================

    # Prometheus CollectorRegistry - uses Redis for persistence between requests
    Prometheus\Storage\Redis:
        factory: ['App\Shared\Infrastructure\Monitoring\PrometheusStorageFactory', 'createRedisStorage']
        arguments:
            $redisUrl: '%env(REDIS_URL)%'

    Prometheus\CollectorRegistry:
        arguments:
            $storageAdapter: '@Prometheus\Storage\Redis'

    # Sentry/GlitchTip PII scrubber callback
    App\Shared\Infrastructure\Monitoring\SentryBeforeSendCallback: ~

    # Infrastructure Health Checker - shared service for health checks (DRY)
    App\Shared\Infrastructure\Monitoring\InfrastructureHealthChecker:
        arguments:
            $connection: '@doctrine.dbal.master_connection'
            $redisUrl: '%env(REDIS_URL)%'

    # Interface alias for InfrastructureHealthChecker (allows testing with stubs)
    App\Shared\Infrastructure\Monitoring\InfrastructureHealthCheckerInterface:
        alias: App\Shared\Infrastructure\Monitoring\InfrastructureHealthChecker

    # Health Check Controller - uses shared InfrastructureHealthChecker
    App\Shared\Infrastructure\Monitoring\HealthCheckController: ~

    # Metrics Controller - restricted to internal networks in production
    App\Shared\Infrastructure\Monitoring\MetricsController:
        arguments:
            $appEnv: '%kernel.environment%'

    # Health Metrics Collector - exposes health_check_status gauge
    App\Shared\Infrastructure\Monitoring\HealthMetricsCollector: ~

    # Interface alias for HealthMetricsCollector (allows testing with stubs)
    App\Shared\Infrastructure\Monitoring\HealthMetricsCollectorInterface:
        alias: App\Shared\Infrastructure\Monitoring\HealthMetricsCollector

    # Sentry context enricher - adds tenant/user/correlation_id to error reports
    # Explicitly registered to ensure HubInterface dependency is resolved
    App\Shared\Infrastructure\Monitoring\SentryContextEnricher:
        arguments:
            $sentryHub: '@Sentry\State\HubInterface'

    # Monolog processors for structured logging
    App\Shared\Infrastructure\Monitoring\CorrelationIdLogProcessor:
        tags:
            - { name: monolog.processor }

    App\Shared\Infrastructure\Monitoring\PiiScrubberLogProcessor:
        tags:
            - { name: monolog.processor }

    # =============================================================================
    # Messenger & Async (Story 2.5b)
    # =============================================================================

    # Fibonacci retry strategy for async transport
    App\Shared\Infrastructure\Messenger\FibonacciRetryStrategy: ~

    # Dead-letter alert: sends admin email when message exhausts all retries
    App\Shared\Infrastructure\Messenger\DeadLetterAlertHandler:
        arguments:
            $adminEmail: '%env(ADMIN_ALERT_EMAIL)%'
        tags:
            - { name: kernel.event_listener, event: Symfony\Component\Messenger\Event\WorkerMessageFailedEvent }

    # Messenger metrics middleware (handled/failed counters)
    App\Shared\Infrastructure\Messenger\MessengerMetricsMiddleware: ~

    # Messenger queue metrics collector (messages waiting gauge)
    App\Shared\Infrastructure\Monitoring\MessengerMetricsCollector: ~

# =============================================================================
# Test environment overrides
# =============================================================================
when@test:
    services:
        # Use null rate limiter in test environment to avoid IP blocking during E2E tests
        App\Shared\Infrastructure\RateLimit\LoginRateLimiterInterface:
            alias: App\Shared\Infrastructure\RateLimit\NullLoginRateLimiter

        App\Shared\Infrastructure\RateLimit\NullLoginRateLimiter:
            autowire: true
