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