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.user.server.logic;
018
019import com.echothree.model.control.order.server.control.OrderControl;
020import com.echothree.model.control.search.server.control.SearchControl;
021import com.echothree.model.control.user.server.control.UserControl;
022import com.echothree.model.control.workrequirement.server.logic.WorkRequirementLogic;
023import com.echothree.model.data.party.common.pk.PartyPK;
024import com.echothree.model.data.user.common.pk.UserVisitPK;
025import com.echothree.model.data.user.server.entity.UserVisit;
026import com.echothree.model.data.user.server.factory.UserVisitFactory;
027import com.echothree.util.server.control.BaseLogic;
028import com.echothree.util.server.persistence.EntityPermission;
029import com.echothree.util.server.persistence.Session;
030import javax.enterprise.context.ApplicationScoped;
031import javax.enterprise.inject.spi.CDI;
032
033@ApplicationScoped
034public class UserVisitLogic
035        extends BaseLogic {
036
037    protected UserVisitLogic() {
038        super();
039    }
040
041    public static UserVisitLogic getInstance() {
042        return CDI.current().select(UserVisitLogic.class).get();
043    }
044    
045    private void cleanupUserVisitDependencies(final UserVisit userVisit, final Long endTime, final PartyPK cleanedUpBy) {
046        var orderControl = Session.getModelController(OrderControl.class);
047        var searchControl = Session.getModelController(SearchControl.class);
048
049        orderControl.deleteOrderUserVisitsByUserVisit(userVisit);
050
051        // Clear any searches since they may contain results that should not be accessible by another user.
052        searchControl.removeUserVisitSearchesByUserVisit(userVisit);
053
054        WorkRequirementLogic.getInstance().endWorkTimesByUserVisit(userVisit, endTime, cleanedUpBy);
055    }
056    
057    public void invalidateUserVisit(UserVisit userVisit, PartyPK invalidatedBy) {
058        if(userVisit != null) {
059            var userControl = Session.getModelController(UserControl.class);
060            var userSession = UserSessionLogic.getInstance().deleteUserSessionByUserVisit(userVisit);
061            
062            cleanupUserVisitDependencies(userVisit, null, invalidatedBy);
063            
064            if(userVisit.getRetainUntilTime() == null) {
065                if(userSession != null) {
066                    userControl.removeUserSession(userSession);
067                }
068                
069                userControl.removeUserVisit(userVisit);
070            } else {
071                userControl.deleteUserVisit(userVisit);
072            }
073        }
074    }
075    
076    public void invalidateUserVisit(UserVisitPK userVisitPK, PartyPK invalidatedBy) {
077        var userControl = Session.getModelController(UserControl.class);
078        
079        invalidateUserVisit(userControl.getUserVisitByPKForUpdate(userVisitPK), invalidatedBy);
080    }
081    
082    public void invalidateAbandonedUserVisits(Long abandonedTime, PartyPK invalidatedBy) {
083        var userControl = Session.getModelController(UserControl.class);
084        
085        for(var userVisit: userControl.getAbandonedUserVisits(abandonedTime)) {
086            userVisit = UserVisitFactory.getInstance().getEntityFromPK(EntityPermission.READ_WRITE, userVisit.getPrimaryKey());
087            
088            invalidateUserVisit(userVisit, invalidatedBy);
089        }
090    }
091    
092    public void removeInvalidatedUserVisits() {
093        var userControl = Session.getModelController(UserControl.class);
094        
095        for(var userVisit: userControl.getInvalidatedUserVisits()) {
096            userVisit = UserVisitFactory.getInstance().getEntityFromPK(EntityPermission.READ_WRITE, userVisit.getPrimaryKey());
097            
098            userControl.removeUserVisit(userVisit);
099        }
100    }
101    
102    /** Disassociate any Party from the specified UserVisit by deleting the UserSession, keep all the Preferred* properties.
103     */
104    public void disassociatePartyFromUserVisit(final UserVisit userVisit, final Long endTime, final PartyPK disassociatedBy) {
105        var userControl = Session.getModelController(UserControl.class);
106        var userSession = userControl.getUserSessionByUserVisitForUpdate(userVisit);
107        
108        if(userSession != null) {
109            // If there wasn't a specific UserSession and Party assocaited with the UserVisit,
110            // then we shouldn't need to perform this step.
111            cleanupUserVisitDependencies(userVisit, endTime, disassociatedBy);
112
113            userControl.deleteUserSession(userSession);
114        }
115    }
116    
117    /** Remove any Party's specific connection to the UserVisit and its UserKey. Clearing the connection in the UserKey is one step beyond
118     * what's provided by disassociatePartyFromUserVisit(...).
119     */
120    public void logout(final UserVisitPK userVisitPK, final Long endTime, final PartyPK loggedOutBy) {
121        var userControl = Session.getModelController(UserControl.class);
122        var userVisit = userControl.getUserVisitByPK(userVisitPK);
123        var userKey = userVisit.getUserKey();
124        var userKeyDetailValue = userControl.getUserKeyDetailValueByPKForUpdate(userKey.getLastDetail().getPrimaryKey());
125
126        if(userKeyDetailValue.getPartyPK() != null || userKeyDetailValue.getPartyRelationshipPK() != null) {
127            userKeyDetailValue.setPartyPK(null);
128            userKeyDetailValue.setPartyRelationshipPK(null);
129            userControl.updateUserKeyFromValue(userKeyDetailValue);
130        }
131
132        disassociatePartyFromUserVisit(userVisit, endTime, loggedOutBy);
133
134        // TODO: Create audit trail
135    }
136    
137}