services:

    # ── Ultimate Cart dedicated log file ─────────────────────────────────
    # Shopware's production logging uses Monolog's "fingers_crossed"
    # handler on the default channel: records for a request are
    # buffered and only written to prod.log if something in that SAME
    # request reaches ERROR level — debug/info/warning records (most
    # of what this plugin logs) are silently dropped in production
    # otherwise.
    #
    # This builds a plain Monolog\Logger service directly (NOT via a
    # Resources/config/packages/*.yaml "monolog:" block — Shopware
    # plugins do not auto-load those the way the main app's
    # config/packages/ does; an earlier attempt at that broke
    # cache:clear with "non-existent service
    # monolog.logger.sellops_ultimate_cart"). A plain service
    # definition here only depends on services.yaml itself, which IS
    # always auto-loaded for every plugin, and on the Monolog classes
    # Shopware core already requires.
    #
    # Every LoggerInterface argument below now points at this service
    # instead of the generic "logger", so plugin logs always reach
    # their own file regardless of environment.
    #
    # Log file: var/log/sellops-ultimate-cart-<environment>.log
    Sellops\UltimateCart\Logger\CartRotatingFileHandler:
        class: Monolog\Handler\RotatingFileHandler
        arguments:
            - '%kernel.logs_dir%/sellops-ultimate-cart-%kernel.environment%.log'
            - 14

    Sellops\UltimateCart\Logger\UltimateCartLogger:
        class: Monolog\Logger
        public: false
        arguments:
            - 'sellops_ultimate_cart'
        calls:
            - method: pushHandler
              arguments:
                  - '@Sellops\UltimateCart\Logger\CartRotatingFileHandler'

    # ── Progress Milestone entity ────────────────────────────────────────
    Sellops\UltimateCart\Entity\ProgressMilestone\ProgressMilestoneDefinition:
        tags:
            - { name: shopware.entity.definition, entity: sellops_uc_progress_milestone }

    # ── Progress Milestone translation entity ────────────────────────────
    # labelBefore/labelAfter translations — see
    # Migration1784400001CreateProgressMilestoneTranslations and
    # ProgressMilestoneDefinition's TranslationsAssociationField.
    Sellops\UltimateCart\Entity\ProgressMilestone\Translation\ProgressMilestoneTranslationDefinition:
        tags:
            - { name: shopware.entity.definition, entity: sellops_uc_progress_milestone_translation }

    # ── Free Product entity ──────────────────────────────────────────────
    Sellops\UltimateCart\Entity\FreeProduct\FreeProductDefinition:
        tags:
            - { name: shopware.entity.definition, entity: sellops_uc_free_product }

    # ── Cross-Sell Rule entity ───────────────────────────────────────────
    Sellops\UltimateCart\Entity\CrossSellRule\CrossSellRuleDefinition:
        tags:
            - { name: shopware.entity.definition, entity: sellops_uc_cross_sell_rule }

    # ── FreeProductGiftResolver ──────────────────────────────────────────
    Sellops\UltimateCart\Service\FreeProductGiftResolver:
        arguments:
            - '@sellops_uc_free_product.repository'
            - '@sales_channel.product.repository'
            - '@order_line_item.repository'
            - '@Sellops\UltimateCart\Service\VariantOptionLoader'
            - '@Sellops\UltimateCart\Logger\UltimateCartLogger'

    # ── ActiveMilestoneLoader ─────────────────────────────────────────────
    # Single source of truth for "active milestones in scope for this
    # sales channel" — shared by ProgressBarResolver, MilestoneCartProcessor,
    # and MilestoneFixedCodePromotionSync so they can never silently
    # disagree on the starting set of milestones. Shopware auto-creates
    # sellops_uc_progress_milestone.repository from the entity definition
    # tag above; this is the only service that injects it directly.
    Sellops\UltimateCart\Service\ActiveMilestoneLoader:
        arguments:
            - '@sellops_uc_progress_milestone.repository'

    # ── VariantOptionLoader ──────────────────────────────────────────────
    # Shared variant/option data for the gift and cross-sell popups
    Sellops\UltimateCart\Service\VariantOptionLoader:
        arguments:
            - '@product.repository'
            - '@sales_channel.product.repository'
            - '@Sellops\UltimateCart\Logger\UltimateCartLogger'

    # ── PromotionEligibilityChecker ──────────────────────────────────────
    # Single source of truth for "is this promotion eligible right now"
    # and "is this promotion actually applied to the cart" — shared by
    # ProgressBarResolver and MilestoneFixedCodePromotionSync so they
    # can never silently diverge (same rationale as ActiveMilestoneLoader
    # above).
    Sellops\UltimateCart\Service\PromotionEligibilityChecker: ~

    Sellops\UltimateCart\Service\ProgressBarResolver:
        arguments:
            - '@Sellops\UltimateCart\Service\ActiveMilestoneLoader'
            - '@Sellops\UltimateCart\Service\FreeProductGiftResolver'
            - '@promotion.repository'
            - '@media.repository'
            - '@Sellops\UltimateCart\Logger\UltimateCartLogger'
            - '@Sellops\UltimateCart\Service\PromotionEligibilityChecker'

    # ── MilestoneCartProcessor ───────────────────────────────────────────
    # Applies discount_percent, discount_fixed, and free_shipping rewards
    # directly inside the cart calculation pipeline (CartProcessorInterface).
    # Priority -50 runs after product processors (~0) so prices are already
    # calculated when we read them for percentage/absolute discount math.
    # MUST run AFTER CrossSellDiscountCartProcessor (priority -40, runs
    # first) — milestone thresholds are evaluated against the product
    # subtotal, and that subtotal must reflect any cross-sell discount
    # already applied, or the milestone bar could read a pre-discount total.
    Sellops\UltimateCart\Cart\MilestoneCartProcessor:
        arguments:
            - '@Sellops\UltimateCart\Service\ActiveMilestoneLoader'
            - '@product.repository'
            - '@Sellops\UltimateCart\Service\FreeProductGiftResolver'
            - '@Shopware\Core\Checkout\Cart\Price\PercentagePriceCalculator'
            - '@Shopware\Core\Checkout\Cart\Price\AbsolutePriceCalculator'
            - '@Sellops\UltimateCart\Logger\UltimateCartLogger'
        tags:
            - { name: shopware.cart.processor, priority: -50 }

    # ── StorefrontPageSubscriber ─────────────────────────────────────────
    Sellops\UltimateCart\Subscriber\StorefrontPageSubscriber:
        arguments:
            - '@Shopware\Core\System\SystemConfig\SystemConfigService'
            - '@Sellops\UltimateCart\Service\CrossSellResolver'
            - '@Sellops\UltimateCart\Service\ProgressBarResolver'
            - '@Sellops\UltimateCart\Service\FreeProductGiftResolver'
            - '@Sellops\UltimateCart\Service\MilestoneFixedCodePromotionSync'
            - '@media.repository'
            - '@Sellops\UltimateCart\Logger\UltimateCartLogger'
        tags:
            - { name: kernel.event_subscriber }

    # ── CrossSellResolver ────────────────────────────────────────────────
    Sellops\UltimateCart\Service\CrossSellResolver:
        arguments:
            - '@product.repository'
            - '@Shopware\Core\System\SystemConfig\SystemConfigService'
            - '@Sellops\UltimateCart\Service\VariantOptionLoader'
            - '@Sellops\UltimateCart\Service\CrossSellRuleEngine'
            - '@Sellops\UltimateCart\Logger\UltimateCartLogger'

    # ── CrossSellFrequentlyBoughtTogetherService ─────────────────────────
    Sellops\UltimateCart\Service\CrossSellFrequentlyBoughtTogetherService:
        arguments:
            - '@Doctrine\DBAL\Connection'

    # ── CrossSellRuleEngine ──────────────────────────────────────────────
    # Evaluates sellops_uc_cross_sell_rule entries against the CURRENT
    # CART CONTENTS and resolves the mini-cart's "Cross-Sell" offers.
    # sales_channel.product.repository gives cover/media/tax/prices
    # populated the same way CrossSellResolver's own native fallback does.
    Sellops\UltimateCart\Service\CrossSellRuleEngine:
        arguments:
            - '@Doctrine\DBAL\Connection'
            - '@sales_channel.product.repository'
            - '@Sellops\UltimateCart\Service\CrossSellFrequentlyBoughtTogetherService'
            # NOTE: AbstractProductStreamBuilder (the non-deprecated
            # replacement for ProductStreamBuilder::buildFilters(), which is
            # deprecated as of tag:v6.8.0) is NOT registered as an
            # autowireable service on this Shopware installation — swapping
            # to it broke `cache:clear` with a ServiceNotFoundException
            # ("did you mean ProductStreamHydrator / ProductStreamBuilder /
            # ProductStreamIndexer?"). Reverted to the concrete
            # ProductStreamBuilder for now; revisit when upgrading to a
            # Shopware version where the abstract class is confirmed
            # container-registered.
            - '@Shopware\Core\Content\ProductStream\Service\ProductStreamBuilder'
            - '@Sellops\UltimateCart\Logger\UltimateCartLogger'
            - '@sellops_uc_cross_sell_rule.repository'

    # ── CrossSellRulesAdminController ────────────────────────────────────
    Sellops\UltimateCart\Controller\Admin\CrossSellRulesAdminController:
        public: true
        arguments:
            - '@Doctrine\DBAL\Connection'
            - '@sellops_uc_cross_sell_rule.repository'
        calls:
            - method: setContainer
              arguments:
                  - '@service_container'

    # ── CrossSellDiscountCartProcessor ───────────────────────────────────
    # Applies the cross-sell rule builder's discount to the REAL price
    # of matching product line items — without this, the discounted
    # price shown in the mini-cart's Cross-Sell offer card was purely
    # cosmetic and the customer was charged full price. Re-evaluates
    # CrossSellRuleEngine server-side (same call CrossSellResolver
    # makes for display) rather than trusting any client-supplied
    # discount value. Priority -40 — runs after ProductCartProcessor
    # (~5000) has already priced items, and BEFORE MilestoneCartProcessor
    # (priority -50): milestone thresholds are evaluated against the
    # product subtotal, so the cross-sell discount must already be baked
    # into line item prices by the time that subtotal is read, or the
    # milestone bar would (dis)qualify against a stale, pre-discount total.
    Sellops\UltimateCart\Cart\CrossSellDiscountCartProcessor:
        arguments:
            - '@Doctrine\DBAL\Connection'
            - '@sales_channel.product.repository'
            - '@Sellops\UltimateCart\Service\CrossSellRuleEngine'
            - '@Shopware\Core\Checkout\Cart\Price\QuantityPriceCalculator'
            - '@Sellops\UltimateCart\Logger\UltimateCartLogger'
        tags:
            - { name: shopware.cart.processor, priority: -40 }

    # ── MilestoneFixedCodePromotionSync ──────────────────────────────────
    # Auto-redeems / auto-removes a Fixed-code Promotion (one shared code,
    # not per-customer) when a milestone's threshold is crossed. Triggered
    # by CartChangedSubscriber below — see that class + this service's own
    # docblock for why it can't live inside MilestoneCartProcessor.
    Sellops\UltimateCart\Service\MilestoneFixedCodePromotionSync:
        arguments:
            - '@Sellops\UltimateCart\Service\ActiveMilestoneLoader'
            - '@promotion.repository'
            - '@Shopware\Core\Checkout\Cart\SalesChannel\CartService'
            - '@Sellops\UltimateCart\Logger\UltimateCartLogger'
            - '@Sellops\UltimateCart\Service\PromotionEligibilityChecker'

    # ── MilestoneFreeGiftSync ─────────────────────────────────────────────
    # Auto-adds a non-variant free-product gift the moment its milestone
    # is reached (no "Add" click needed). Triggered by CartChangedSubscriber
    # below — see that class + this service's own docblock for why it
    # can't live inside MilestoneCartProcessor, and why removal needs no
    # matching call here.
    Sellops\UltimateCart\Service\MilestoneFreeGiftSync:
        arguments:
            - '@Sellops\UltimateCart\Service\FreeProductGiftResolver'
            - '@Shopware\Core\Checkout\Cart\SalesChannel\CartService'
            - '@Sellops\UltimateCart\Logger\UltimateCartLogger'

    # ── CartChangedSubscriber ────────────────────────────────────────────
    Sellops\UltimateCart\Subscriber\CartChangedSubscriber:
        arguments:
            - '@Sellops\UltimateCart\Service\MilestoneFixedCodePromotionSync'
            - '@Sellops\UltimateCart\Service\MilestoneFreeGiftSync'
            - '@Sellops\UltimateCart\Logger\UltimateCartLogger'
        tags:
            - { name: kernel.event_subscriber }

    # ── ProductStickyCartSubscriber ──────────────────────────────────────
    Sellops\UltimateCart\Subscriber\ProductStickyCartSubscriber:
        arguments:
            - '@Shopware\Core\System\SystemConfig\SystemConfigService'
            - '@Sellops\UltimateCart\Logger\UltimateCartLogger'
        tags:
            - { name: kernel.event_subscriber }
