001// --------------------------------------------------------------------------------
002// Copyright 2002-2025 Echo Three, LLC
003//
004// Licensed under the Apache License, Version 2.0 (the "License");
005// you may not use this file except in compliance with the License.
006// You may obtain a copy of the License at
007//
008//     http://www.apache.org/licenses/LICENSE-2.0
009//
010// Unless required by applicable law or agreed to in writing, software
011// distributed under the License is distributed on an "AS IS" BASIS,
012// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013// See the License for the specific language governing permissions and
014// limitations under the License.
015// --------------------------------------------------------------------------------
016
017package com.echothree.model.control.subscription.server.logic;
018
019import com.echothree.model.control.chain.common.ChainConstants;
020import com.echothree.model.control.chain.server.control.ChainControl;
021import com.echothree.model.control.chain.server.logic.BaseChainLogic;
022import com.echothree.model.control.subscription.server.control.SubscriptionControl;
023import com.echothree.model.data.chain.server.entity.Chain;
024import com.echothree.model.data.chain.server.entity.ChainInstance;
025import com.echothree.model.data.subscription.server.entity.Subscription;
026import com.echothree.util.common.persistence.BasePK;
027import com.echothree.util.server.message.ExecutionErrorAccumulator;
028import com.echothree.util.server.persistence.Session;
029import javax.enterprise.context.ApplicationScoped;
030import javax.enterprise.inject.spi.CDI;
031
032@ApplicationScoped
033public class SubscriptionChainLogic
034        extends BaseChainLogic {
035
036    protected SubscriptionChainLogic() {
037        super();
038    }
039
040    public static SubscriptionChainLogic getInstance() {
041        return CDI.current().select(SubscriptionChainLogic.class).get();
042    }
043    
044    /** createChainInstance is different than the version in BaseChainLogic, it requires a Subscription parameter. Subscriptions
045     * are allowed to override the Chain being used in each SubscriptionType, so this takes that into account when choosing a
046     * Chain. If there is no SubscriptionTypeChain, then we fall back to the default for the ChainType, following the normal
047     * rules that take OfferChainTypes into account as well. If the chainTypeName == RENEWAL, then we immediately fall back to the
048     * default for the ChainType.
049     * 
050     * TODO: RENEWAL should really be based on the amount of time remaining in the Subscription, and not just fall back to the
051     * default. getSubscriptionTypeChainsBySubscriptionTypeAndChainType(...) could order by the remaining time, and then search
052     * for the one that's the best fit.
053     */
054    protected ChainInstance createChainInstance(final ExecutionErrorAccumulator eea, final String chainKindName, final String chainTypeName,
055            final Subscription subscription, final BasePK createdBy) {
056        var subscriptionControl = Session.getModelController(SubscriptionControl.class);
057        var subscriptionType = subscription.getLastDetail().getSubscriptionType();
058        var chainType = getChainTypeByName(eea, chainKindName, chainTypeName);
059        var party = subscription.getLastDetail().getParty();
060        var subscriptionTypeChains = chainTypeName.equals(ChainConstants.ChainType_RENEWAL) ? null
061                : subscriptionControl.getSubscriptionTypeChainsBySubscriptionTypeAndChainType(subscriptionType, chainType);
062        Chain chain;
063        ChainInstance chainInstance = null;
064        
065        if(subscriptionTypeChains == null || subscriptionTypeChains.isEmpty()) {
066            chain = getChain(eea, chainType, party);
067        } else {
068            chain = subscriptionTypeChains.iterator().next().getChain();
069        }
070        
071        if(chain != null) {
072            chainInstance = createChainInstance(eea, chainType, party, createdBy);
073        }
074        
075        return chainInstance;
076    }
077    
078    protected ChainInstance createSubscriptionChainInstance(final ExecutionErrorAccumulator eea, final String chainTypeName, final Subscription subscription,
079            final BasePK createdBy) {
080        var chainInstance = createChainInstance(eea, ChainConstants.ChainKind_SUBSCRIPTION, chainTypeName, subscription, createdBy);
081        
082        if(chainInstance != null) {
083            var chainControl = Session.getModelController(ChainControl.class);
084            var chainType = chainInstance.getLastDetail().getChain().getLastDetail().getChainType();
085        
086            chainControl.createChainInstanceEntityRole(chainInstance, chainControl.getChainEntityRoleTypeByName(chainType,
087                    ChainConstants.ChainEntityRoleType_SUBSCRIPTION), subscription.getPrimaryKey(), createdBy);
088        }
089        
090        return chainInstance;
091    }
092    
093    public ChainInstance createSubscriptionInitialChainInstance(final ExecutionErrorAccumulator eea, final Subscription subscription,
094            final BasePK createdBy) {
095        return createSubscriptionChainInstance(eea, ChainConstants.ChainType_INITIAL, subscription, createdBy);
096    }
097    
098    public ChainInstance createSubscriptionExpirationWarningChainInstance(final ExecutionErrorAccumulator eea, final Subscription subscription,
099            final BasePK createdBy) {
100        return createSubscriptionChainInstance(eea, ChainConstants.ChainType_EXPIRATION_WARNING, subscription, createdBy);
101    }
102    
103    /** TODO: Consider the RemainingTime in the SubscriptionDetail when choosing the Chain to use.
104     */
105    public ChainInstance createSubscriptionRenewalChainInstance(final ExecutionErrorAccumulator eea, final Subscription subscription,
106            final BasePK createdBy) {
107        return createSubscriptionChainInstance(eea, ChainConstants.ChainType_RENEWAL, subscription, createdBy);
108    }
109    
110    public ChainInstance createSubscriptionExpirationChainInstance(final ExecutionErrorAccumulator eea, final Subscription subscription,
111            final BasePK createdBy) {
112        return createSubscriptionChainInstance(eea, ChainConstants.ChainType_EXPIRATION, subscription, createdBy);
113    }
114    
115}