<?php declare(strict_types=1); 
 
namespace Shopware\Core\Framework; 
 
use Shopware\Core\Checkout\Cart\Price\Struct\CartPrice; 
use Shopware\Core\Defaults; 
use Shopware\Core\Framework\Api\Context\AdminApiSource; 
use Shopware\Core\Framework\Api\Context\ContextSource; 
use Shopware\Core\Framework\Api\Context\SystemSource; 
use Shopware\Core\Framework\DataAbstractionLayer\Pricing\CashRoundingConfig; 
use Shopware\Core\Framework\Struct\StateAwareTrait; 
use Shopware\Core\Framework\Struct\Struct; 
 
class Context extends Struct 
{ 
    use StateAwareTrait; 
 
    public const SYSTEM_SCOPE = 'system'; 
    public const USER_SCOPE = 'user'; 
    public const CRUD_API_SCOPE = 'crud'; 
 
    /** 
     * @deprecated tag:v6.5.0 - Use `\Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria::STATE_ELASTICSEARCH_AWARE` on Criteria instead 
     */ 
    public const STATE_ELASTICSEARCH_AWARE = 'elasticsearchAware'; 
    public const SKIP_TRIGGER_FLOW = 'skipTriggerFlow'; 
 
    /** 
     * @var non-empty-array<string> 
     * 
     * @deprecated tag:v6.5.0 prop will be natively typed as `array` in future versions 
     */ 
    protected $languageIdChain; 
 
    /** 
     * @var string 
     * 
     * @deprecated tag:v6.5.0 prop will be natively typed as `string` in future versions 
     */ 
    protected $versionId; 
 
    /** 
     * @var string 
     * 
     * @deprecated tag:v6.5.0 prop will be natively typed as `string` in future versions 
     */ 
    protected $currencyId; 
 
    /** 
     * @var float 
     * 
     * @deprecated tag:v6.5.0 prop will be natively typed as `float` in future versions 
     */ 
    protected $currencyFactor; 
 
    /** 
     * @var string 
     * 
     * @deprecated tag:v6.5.0 prop will be natively typed as `string` in future versions 
     */ 
    protected $scope = self::USER_SCOPE; 
 
    /** 
     * @var array<string> 
     * 
     * @deprecated tag:v6.5.0 prop will be natively typed as `array` in future versions 
     */ 
    protected $ruleIds; 
 
    /** 
     * @var ContextSource 
     * 
     * @deprecated tag:v6.5.0 prop will be natively typed as `ContextSource` in future versions 
     */ 
    protected $source; 
 
    /** 
     * @var bool 
     * 
     * @deprecated tag:v6.5.0 prop will be natively typed as `bool` in future versions 
     */ 
    protected $considerInheritance; 
 
    /** 
     * @see CartPrice::TAX_STATE_GROSS, CartPrice::TAX_STATE_NET, CartPrice::TAX_STATE_FREE 
     * 
     * @var string 
     * 
     * @deprecated tag:v6.5.0 prop will be natively typed as `string` in future versions 
     */ 
    protected $taxState = CartPrice::TAX_STATE_GROSS; 
 
    /** 
     * @var CashRoundingConfig 
     * 
     * @deprecated tag:v6.5.0 prop will be natively typed as `CashRoundingConfig` in future versions 
     */ 
    protected $rounding; 
 
    /** 
     * @param array<string> $languageIdChain 
     * @param array<string> $ruleIds 
     */ 
    public function __construct( 
        ContextSource $source, 
        array $ruleIds = [], 
        string $currencyId = Defaults::CURRENCY, 
        array $languageIdChain = [Defaults::LANGUAGE_SYSTEM], 
        string $versionId = Defaults::LIVE_VERSION, 
        float $currencyFactor = 1.0, 
        bool $considerInheritance = false, 
        string $taxState = CartPrice::TAX_STATE_GROSS, 
        ?CashRoundingConfig $rounding = null 
    ) { 
        $this->source = $source; 
 
        if ($source instanceof SystemSource) { 
            $this->scope = self::SYSTEM_SCOPE; 
        } 
 
        $this->ruleIds = $ruleIds; 
        $this->currencyId = $currencyId; 
 
        $this->versionId = $versionId; 
        $this->currencyFactor = $currencyFactor; 
 
        if (empty($languageIdChain)) { 
            throw new \InvalidArgumentException('Argument languageIdChain must not be empty'); 
        } 
 
        /** @var non-empty-array<string> $chain */ 
        $chain = array_keys(array_flip(array_filter($languageIdChain))); 
        $this->languageIdChain = $chain; 
        $this->considerInheritance = $considerInheritance; 
        $this->taxState = $taxState; 
        $this->rounding = $rounding ?? new CashRoundingConfig(2, 0.01, true); 
    } 
 
    /** 
     * @internal 
     */ 
    public static function createDefaultContext(?ContextSource $source = null): self 
    { 
        $source = $source ?? new SystemSource(); 
 
        return new self($source); 
    } 
 
    public function getSource(): ContextSource 
    { 
        return $this->source; 
    } 
 
    public function getVersionId(): string 
    { 
        return $this->versionId; 
    } 
 
    public function getLanguageId(): string 
    { 
        return $this->languageIdChain[0]; 
    } 
 
    public function getCurrencyId(): string 
    { 
        return $this->currencyId; 
    } 
 
    public function getCurrencyFactor(): float 
    { 
        return $this->currencyFactor; 
    } 
 
    /** 
     * @return array<string> 
     */ 
    public function getRuleIds(): array 
    { 
        return $this->ruleIds; 
    } 
 
    /** 
     * @return non-empty-array<string> 
     */ 
    public function getLanguageIdChain(): array 
    { 
        return $this->languageIdChain; 
    } 
 
    public function createWithVersionId(string $versionId): self 
    { 
        $context = new self( 
            $this->source, 
            $this->ruleIds, 
            $this->currencyId, 
            $this->languageIdChain, 
            $versionId, 
            $this->currencyFactor, 
            $this->considerInheritance, 
            $this->taxState, 
            $this->rounding 
        ); 
        $context->scope = $this->scope; 
 
        foreach ($this->getExtensions() as $key => $extension) { 
            $context->addExtension($key, $extension); 
        } 
 
        return $context; 
    } 
 
    /** 
     * @return mixed the return value of the provided callback function 
     */ 
    public function scope(string $scope, callable $callback) 
    { 
        $currentScope = $this->getScope(); 
        $this->scope = $scope; 
 
        try { 
            $result = $callback($this); 
        } finally { 
            $this->scope = $currentScope; 
        } 
 
        return $result; 
    } 
 
    public function getScope(): string 
    { 
        return $this->scope; 
    } 
 
    public function considerInheritance(): bool 
    { 
        return $this->considerInheritance; 
    } 
 
    public function setConsiderInheritance(bool $considerInheritance): void 
    { 
        $this->considerInheritance = $considerInheritance; 
    } 
 
    public function getTaxState(): string 
    { 
        return $this->taxState; 
    } 
 
    public function setTaxState(string $taxState): void 
    { 
        $this->taxState = $taxState; 
    } 
 
    public function isAllowed(string $privilege): bool 
    { 
        if ($this->source instanceof AdminApiSource) { 
            return $this->source->isAllowed($privilege); 
        } 
 
        return true; 
    } 
 
    /** 
     * @param array<string> $ruleIds 
     */ 
    public function setRuleIds(array $ruleIds): void 
    { 
        $this->ruleIds = array_filter(array_values($ruleIds)); 
    } 
 
    /** 
     * @return mixed 
     */ 
    public function enableInheritance(callable $function) 
    { 
        $previous = $this->considerInheritance; 
        $this->considerInheritance = true; 
        $result = $function($this); 
        $this->considerInheritance = $previous; 
 
        return $result; 
    } 
 
    /** 
     * @return mixed 
     */ 
    public function disableInheritance(callable $function) 
    { 
        $previous = $this->considerInheritance; 
        $this->considerInheritance = false; 
        $result = $function($this); 
        $this->considerInheritance = $previous; 
 
        return $result; 
    } 
 
    public function getApiAlias(): string 
    { 
        return 'context'; 
    } 
 
    public function getRounding(): CashRoundingConfig 
    { 
        return $this->rounding; 
    } 
 
    public function setRounding(CashRoundingConfig $rounding): void 
    { 
        $this->rounding = $rounding; 
    } 
}