001// --------------------------------------------------------------------------------
002// Copyright 2002-2026 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.control.user.chain.server.command;
018
019import com.echothree.control.user.chain.common.form.CreateChainActionForm;
020import com.echothree.model.control.chain.common.ChainConstants;
021import com.echothree.model.control.chain.server.control.ChainControl;
022import com.echothree.model.control.letter.server.control.LetterControl;
023import com.echothree.model.control.party.common.PartyTypes;
024import com.echothree.model.control.security.common.SecurityRoleGroups;
025import com.echothree.model.control.security.common.SecurityRoles;
026import com.echothree.model.control.uom.common.UomConstants;
027import com.echothree.model.control.uom.server.control.UomControl;
028import com.echothree.model.control.uom.server.util.Conversion;
029import com.echothree.model.data.chain.server.entity.Chain;
030import com.echothree.model.data.chain.server.entity.ChainAction;
031import com.echothree.model.data.chain.server.entity.ChainActionSet;
032import com.echothree.model.data.chain.server.entity.ChainActionType;
033import com.echothree.model.data.chain.server.entity.ChainType;
034import com.echothree.model.data.letter.server.entity.Letter;
035import com.echothree.model.data.party.common.pk.PartyPK;
036import com.echothree.model.data.user.common.pk.UserVisitPK;
037import com.echothree.util.common.message.ExecutionErrors;
038import com.echothree.util.common.validation.FieldDefinition;
039import com.echothree.util.common.validation.FieldType;
040import com.echothree.util.common.command.BaseResult;
041import com.echothree.util.common.form.ValidationResult;
042import com.echothree.util.server.control.BaseSimpleCommand;
043import com.echothree.util.server.control.CommandSecurityDefinition;
044import com.echothree.util.server.control.PartyTypeDefinition;
045import com.echothree.util.server.control.SecurityRoleDefinition;
046import com.echothree.util.server.persistence.Session;
047import com.echothree.util.server.validation.Validator;
048import java.util.List;
049import javax.enterprise.context.Dependent;
050
051@Dependent
052public class CreateChainActionCommand
053        extends BaseSimpleCommand<CreateChainActionForm> {
054    
055    private final static CommandSecurityDefinition COMMAND_SECURITY_DEFINITION;
056    private final static List<FieldDefinition> FORM_FIELD_DEFINITIONS;
057    private final static List<FieldDefinition> letterFormFieldDefinitions;
058    private final static List<FieldDefinition> surveyFormFieldDefinitions;
059    private final static List<FieldDefinition> chainActionSetFormFieldDefinitions;
060    
061    static {
062        COMMAND_SECURITY_DEFINITION = new CommandSecurityDefinition(List.of(
063                new PartyTypeDefinition(PartyTypes.UTILITY.name(), null),
064                new PartyTypeDefinition(PartyTypes.EMPLOYEE.name(), List.of(
065                    new SecurityRoleDefinition(SecurityRoleGroups.ChainAction.name(), SecurityRoles.Create.name())
066                    ))
067                ));
068
069        FORM_FIELD_DEFINITIONS = List.of(
070                new FieldDefinition("ChainKindName", FieldType.ENTITY_NAME, true, null, null),
071                new FieldDefinition("ChainTypeName", FieldType.ENTITY_NAME, true, null, null),
072                new FieldDefinition("ChainName", FieldType.ENTITY_NAME, true, null, null),
073                new FieldDefinition("ChainActionSetName", FieldType.ENTITY_NAME, true, null, null),
074                new FieldDefinition("ChainActionName", FieldType.ENTITY_NAME, true, null, null),
075                new FieldDefinition("ChainActionTypeName", FieldType.ENTITY_NAME, true, null, null),
076                new FieldDefinition("SortOrder", FieldType.SIGNED_INTEGER, true, null, null),
077                new FieldDefinition("Description", FieldType.STRING, false, 1L, 132L)
078                );
079        
080        letterFormFieldDefinitions = List.of(
081                new FieldDefinition("LetterName", FieldType.ENTITY_NAME, true, null, null)
082                );
083        
084        surveyFormFieldDefinitions = List.of(
085                new FieldDefinition("SurveyName", FieldType.ENTITY_NAME, true, null, null)
086                );
087        
088        chainActionSetFormFieldDefinitions = List.of(
089                new FieldDefinition("NextChainActionSetName", FieldType.ENTITY_NAME, true, null, null),
090                new FieldDefinition("DelayTime", FieldType.UNSIGNED_LONG, true, null, null),
091                new FieldDefinition("DelayTimeUnitOfMeasureTypeName", FieldType.ENTITY_NAME, true, null, null)
092                );
093    }
094    
095    /** Creates a new instance of CreateChainActionCommand */
096    public CreateChainActionCommand() {
097        super(COMMAND_SECURITY_DEFINITION, FORM_FIELD_DEFINITIONS, false);
098    }
099    
100    @Override
101    protected ValidationResult validate() {
102        var validator = new Validator(this);
103        var validationResult = validator.validate(form, FORM_FIELD_DEFINITIONS);
104        
105        if(!validationResult.getHasErrors()) {
106            var chainActionTypeName = form.getChainActionTypeName();
107            
108            if(chainActionTypeName.equals(ChainConstants.ChainActionType_LETTER)) {
109                validationResult = validator.validate(form, letterFormFieldDefinitions);
110            } else if(chainActionTypeName.equals(ChainConstants.ChainActionType_SURVEY)) {
111                validationResult = validator.validate(form, surveyFormFieldDefinitions);
112            } else if(chainActionTypeName.equals(ChainConstants.ChainActionType_CHAIN_ACTION_SET)) {
113                validationResult = validator.validate(form, chainActionSetFormFieldDefinitions);
114            }
115        }
116        
117        return validationResult;
118    }
119    
120    private abstract class BaseChainActionType {
121
122        ChainControl chainControl;
123        ChainActionType chainActionType;
124
125        public BaseChainActionType(ChainControl chainControl, String chainActionTypeName) {
126            this.chainControl = chainControl;
127            chainActionType = chainControl.getChainActionTypeByName(chainActionTypeName);
128
129            if(chainActionType == null) {
130                addExecutionError(ExecutionErrors.UnknownChainActionTypeName.name(), chainActionTypeName);
131            }
132        }
133
134        public abstract void execute(ChainAction chainAction, PartyPK partyPK);
135    }
136    
137    private abstract class LetterComponentChainActionType
138            extends BaseChainActionType {
139
140        protected LetterControl letterControl = Session.getModelController(LetterControl.class);
141
142        public LetterComponentChainActionType(ChainControl chainControl, String chainActionTypeName) {
143            super(chainControl, chainActionTypeName);
144        }
145    }
146    
147    private class LetterChainActionType
148        extends LetterComponentChainActionType {
149        private Letter letter = null;
150        
151        public LetterChainActionType(ChainControl chainControl, ChainType chainType) {
152            super(chainControl, ChainConstants.ChainActionType_LETTER);
153            
154            if(!hasExecutionErrors()) {
155                var letterName = form.getLetterName();
156                
157                letter = letterControl.getLetterByName(chainType, letterName);
158                
159                if(letter == null) {
160                    addExecutionError(ExecutionErrors.UnknownLetterName.name(), letterName);
161                }
162            }
163        }
164        
165        @Override
166        public void execute(ChainAction chainAction, PartyPK partyPK) {
167            chainControl.createChainActionLetter(chainAction, letter, partyPK);
168        }
169    }
170    
171    private class ChainActionSetChainActionType
172        extends BaseChainActionType {
173        ChainActionSet nextChainActionSet = null;
174        Long delayTime = null;
175        
176        public ChainActionSetChainActionType(ChainControl chainControl, Chain chain) {
177            super(chainControl, ChainConstants.ChainActionType_CHAIN_ACTION_SET);
178            
179            if(!hasExecutionErrors()) {
180                var nextChainActionSetName = form.getNextChainActionSetName();
181                
182                nextChainActionSet = chainControl.getChainActionSetByName(chain, nextChainActionSetName);
183                
184                if(nextChainActionSet != null) {
185                    var uomControl = Session.getModelController(UomControl.class);
186                    var timeUnitOfMeasureKind = uomControl.getUnitOfMeasureKindByUnitOfMeasureKindUseTypeUsingNames(UomConstants.UnitOfMeasureKindUseType_TIME);
187
188                    if(timeUnitOfMeasureKind != null) {
189                        var delayTimeUnitOfMeasureTypeName = form.getDelayTimeUnitOfMeasureTypeName();
190                        var delayTimeUnitOfMeasureType = uomControl.getUnitOfMeasureTypeByName(timeUnitOfMeasureKind, delayTimeUnitOfMeasureTypeName);
191
192                        if(delayTimeUnitOfMeasureType != null) {
193                            delayTime = new Conversion(uomControl, delayTimeUnitOfMeasureType, Long.valueOf(form.getDelayTime())).convertToLowestUnitOfMeasureType().getQuantity();
194                        } else {
195                            addExecutionError(ExecutionErrors.UnknownRetainUserVisitsTimeUnitOfMeasureTypeName.name(), delayTimeUnitOfMeasureTypeName);
196                        }
197                    } else {
198                        addExecutionError(ExecutionErrors.UnknownTimeUnitOfMeasureKind.name());
199                    }
200                } else {
201                    addExecutionError(ExecutionErrors.UnknownNextChainActionSetName.name(), nextChainActionSetName);
202                }
203            }
204        }
205        
206        @Override
207        public void execute(ChainAction chainAction, PartyPK partyPK) {
208            chainControl.createChainActionChainActionSet(chainAction, nextChainActionSet, delayTime, partyPK);
209        }
210    }
211    
212    @Override
213    protected BaseResult execute() {
214        var chainControl = Session.getModelController(ChainControl.class);
215        var chainKindName = form.getChainKindName();
216        var chainKind = chainControl.getChainKindByName(chainKindName);
217
218        if(chainKind != null) {
219            var chainTypeName = form.getChainTypeName();
220            var chainType = chainControl.getChainTypeByName(chainKind, chainTypeName);
221
222            if(chainType != null) {
223                var chainName = form.getChainName();
224                var chain = chainControl.getChainByName(chainType, chainName);
225
226                if(chain != null) {
227                    var chainActionSetName = form.getChainActionSetName();
228                    var chainActionSet = chainControl.getChainActionSetByName(chain, chainActionSetName);
229
230                    if(chainActionSet != null) {
231                        var chainActionName = form.getChainActionName();
232                        var chainAction = chainControl.getChainActionByName(chainActionSet, chainActionName);
233
234                        if(chainAction == null) {
235                            var chainActionTypeName = form.getChainActionTypeName();
236                            var chainActionType = chainControl.getChainActionTypeByName(chainActionTypeName);
237
238                            if(chainActionType != null) {
239                                BaseChainActionType baseChainActionType = null;
240                                
241                                if(chainActionTypeName.equals(ChainConstants.ChainActionType_LETTER)) {
242                                    baseChainActionType = new LetterChainActionType(chainControl, chainType);
243                                } else if(chainActionTypeName.equals(ChainConstants.ChainActionType_SURVEY)) {
244                                    // TODO
245                                } else if(chainActionTypeName.equals(ChainConstants.ChainActionType_CHAIN_ACTION_SET)) {
246                                    baseChainActionType = new ChainActionSetChainActionType(chainControl, chain);
247                                }
248                                
249                                if(!hasExecutionErrors()) {
250                                    var partyPK = getPartyPK();
251                                    var sortOrder = Integer.valueOf(form.getSortOrder());
252                                    var description = form.getDescription();
253
254                                    chainAction = chainControl.createChainAction(chainActionSet, chainActionName, chainActionType, sortOrder, partyPK);
255
256                                            if(baseChainActionType != null) { // TODO: Remove test once all Types are implemented.
257                                                baseChainActionType.execute(chainAction, partyPK);
258                                            }
259
260                                    if(description != null) {
261                                        chainControl.createChainActionDescription(chainAction, getPreferredLanguage(), description, partyPK);
262                                    }
263                                }
264                            } else {
265                                    addExecutionError(ExecutionErrors.UnknownChainActionTypeName.name(), chainActionTypeName);
266                            }
267                        } else {
268                                addExecutionError(ExecutionErrors.DuplicateChainActionName.name(), chainKindName, chainTypeName, chainName, chainActionSetName, chainActionName);
269                        }
270                    } else {
271                            addExecutionError(ExecutionErrors.UnknownChainActionSetName.name(), chainKindName, chainTypeName, chainName, chainActionSetName);
272                    }
273                } else {
274                        addExecutionError(ExecutionErrors.UnknownChainName.name(), chainKindName, chainTypeName, chainName);
275                }
276            } else {
277                addExecutionError(ExecutionErrors.UnknownChainTypeName.name(), chainKindName, chainTypeName);
278            }
279        } else {
280            addExecutionError(ExecutionErrors.UnknownChainKindName.name(), chainKindName);
281        }
282
283        return null;
284    }
285    
286}