using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using LHDN.CRM.Models.ViewModels;
using LHDN.CRM.BC.Common;
using System.Transactions;
using System.Data;

namespace LHDN.CRM.BC.Services
{
    public class WorkflowService : ServiceBase<Models.ViewModels.CaseViewModel>
    {
        private Repositories.WorkflowRepository repo = new Repositories.WorkflowRepository();
        private Repositories.UserRepository user = new Repositories.UserRepository();
        private UserProfileService userSVC = new UserProfileService();
        public WorkflowAssignToUnitIDViewModel GetWorkFlowAssignToByKategoryId(WorkflowAssignToUnitIDViewModel KategoryId)
        {
            return repo.GetWorkflowAssignToValue(KategoryId);
        }

        public List<WorkflowUserViewModel> GetWorkFlowAllUserOrUnit(WorkflowUserViewModel data)
        {
            List<WorkflowUserViewModel> workflowUser = new List<WorkflowUserViewModel>();
            workflowUser = repo.GetWorkFlowAllUserOrUnit(data);
            if (workflowUser.Count == 0)
            {
                WorkflowUserViewModel tempworkflowUser = new WorkflowUserViewModel();
                tempworkflowUser.DisplayName = "No assigned manager";
                tempworkflowUser.ID = null;
                workflowUser.Add(tempworkflowUser);
            }

            return workflowUser;
        }

        public List<ChangeHistoryUpdateViewModel> UpdateWorkflow(WorkflowUpdateViewModel objworkflow, out string strCaseRefNo, out string strActRefNo, out DataTable dTblReturnResult, ref WorkflowUserViewModel assignedUser, DateTime now)
        {
            List<ChangeHistoryUpdateViewModel> lst = new List<ChangeHistoryUpdateViewModel>();
            // Retrieve orginal records for audit and change history 
            WorkflowActionViewModel OriWorkflowData = new WorkflowActionViewModel();
            strCaseRefNo = string.Empty;
            strActRefNo = string.Empty;
            dTblReturnResult = new DataTable();
            long lgWFStatusID = 0;

            OriWorkflowData = repo.GetExistingWorkflowRecord(objworkflow.Workflow.WorkflowID);

            if (OriWorkflowData == null)
            {
                // Check for update conflict
                if (objworkflow.Workflow.WorkflowID != 0)
                {
                    //TODO Update conflict
                    throw new UpdateConflictException();
                }

                //foreach (ChangeHistoryUpdateViewModel changeHistoryItem in objworkflow.WorkflowChangeHistory)
                //{
                //    //Get object original value
                //    string oriValue = string.Empty;
                //    if (changeHistoryItem.FieldName.IndexOf('_') > 0)
                //    {
                //        string[] arr = changeHistoryItem.FieldName.Split('_');
                //        var obj = Utilities.GetPropertyValue(OriWorkflowData, arr[0]);

                //        for (int j = 1; j < arr.Length; j++)
                //        {
                //            obj = Utilities.GetPropertyValue(obj, arr[j]);
                //        }
                //        oriValue = obj == null ? "" : obj.ToString();
                //    }
                //    else
                //    {
                //        oriValue = "";
                //    }
                //    changeHistoryItem.OldValue = oriValue;
                //    changeHistoryItem.IsChanged = !(changeHistoryItem.OldValue == changeHistoryItem.NewValue);
                //}

                if (objworkflow.Workflow.DecisionID == (int)Constant.WorkFlow.WorkflowDecision.Assign)
                {
                    objworkflow.Workflow.WFStatusID = (int)Constant.WorkFlow.WorkflowStatus.PendingAction;
                    assignedUser = objworkflow.Workflow.AssignTo;
                    objworkflow.Workflow.Remarks = WorkflowSystemAutoRemark(objworkflow.Workflow.DecisionID, assignedUser.DisplayName);
                }
                else if (objworkflow.Workflow.DecisionID == (int)Constant.WorkFlow.WorkflowDecision.Reassign)
                {
                    objworkflow.Workflow.WFStatusID = (int)Constant.WorkFlow.WorkflowStatus.PendingAction;
                    assignedUser = objworkflow.Workflow.AssignTo;
                    objworkflow.Workflow.Remarks = WorkflowSystemAutoRemark(objworkflow.Workflow.DecisionID, assignedUser.DisplayName);
                }

                // Add Assign To Old and new value to Change History Table
                if (objworkflow.Workflow.AssignTo.AssignToType == 20 || objworkflow.Workflow.AssignTo.AssignToType == 21)
                {
                    var strWorkflowOldAssignName = "";
                    var strWorkflowNewAssignName = objworkflow.Workflow.AssignTo.DisplayName;
                    if (objworkflow.Workflow.ActivityID > 0)
                    {
                        objworkflow.WorkflowChangeHistory.Add(BindChangeHistWorkflow(objworkflow.Workflow.ActivityID, "AssignTo", "Assign To", strWorkflowOldAssignName, strWorkflowNewAssignName));
                    }
                    else
                    {
                        objworkflow.WorkflowChangeHistory.Add(BindChangeHistWorkflow(objworkflow.Workflow.CaseID, "AssignTo", "Assign To", strWorkflowOldAssignName, strWorkflowNewAssignName));
                    }

                }

                // Add Workflow Status Old and new value to Change History Table
                var strWorkflowOldStatus = Utilities.GetEnumDescription(Constant.WorkFlow.WorkflowStatus.Open);
                var strWorkflowNewStatus = Utilities.GetEnumDescription(Constant.WorkFlow.WorkflowStatus.PendingAction);
                if (objworkflow.Workflow.ActivityID > 0)
                {
                    objworkflow.WorkflowChangeHistory.Add(BindChangeHistWorkflow(objworkflow.Workflow.ActivityID, "WorkflowStatus", "Workflow Status", strWorkflowOldStatus.ToString(), strWorkflowNewStatus));

                }
                else
                {
                    objworkflow.WorkflowChangeHistory.Add(BindChangeHistWorkflow(objworkflow.Workflow.CaseID, "WorkflowStatus", "Workflow Status", strWorkflowOldStatus.ToString(), strWorkflowNewStatus));

                }

            }
            else
            {
                if (objworkflow.Workflow.WorkflowID != OriWorkflowData.WorkflowID)
                {
                    //TODO Update conflict
                    throw new UpdateConflictException();
                }

                foreach (ChangeHistoryUpdateViewModel changeHistoryItem in objworkflow.WorkflowChangeHistory)
                {
                    //Get object original value
                    string oriValue = string.Empty;
                    if (changeHistoryItem.FieldName.IndexOf('_') > 0)
                    {
                        string[] arr = changeHistoryItem.FieldName.Split('_');
                        var obj = Utilities.GetPropertyValue(OriWorkflowData, arr[0]);

                        for (int j = 1; j < arr.Length; j++)
                        {
                            obj = Utilities.GetPropertyValue(obj, arr[j]);
                        }
                        oriValue = obj == null ? "" : obj.ToString();
                    }
                    else
                    {
                        var obj = Utilities.GetPropertyValue(OriWorkflowData, changeHistoryItem.FieldName);
                        oriValue = obj == null ? "" : obj.ToString();
                    }
                    changeHistoryItem.OldValue = oriValue;
                    changeHistoryItem.IsChanged = !(changeHistoryItem.OldValue == changeHistoryItem.NewValue);
                }

                // New workflow change history
                if (objworkflow.Workflow.DecisionID == (int)Constant.WorkFlow.WorkflowDecision.Assign)
                {
                    objworkflow.Workflow.WFStatusID = (int)Constant.WorkFlow.WorkflowStatus.PendingAction;
                    OriWorkflowData.WFStatusID = objworkflow.Workflow.WFStatusID;
                    OriWorkflowData.DecisionID = objworkflow.Workflow.DecisionID;
                    assignedUser = objworkflow.Workflow.AssignTo;

                    // Add Assign To Old and new value to Change History Table
                    var strWorkflowOldAssignName = OriWorkflowData.AssignToName;
                    var strWorkflowNewAssignName = assignedUser.DisplayName;

                    // Add Workflow Status Old and new value to Change History Table
                    var strWorkflowOldStatus = AdminService.LookupParamSys.Where(x => x.LookupParamSysID == OriWorkflowData.WFStatusID).FirstOrDefault();
                    var strWorkflowNewStatus = Utilities.GetEnumDescription(Constant.WorkFlow.WorkflowStatus.PendingAction);

                    if (objworkflow.Workflow.ActivityID > 0)
                    {
                        objworkflow.WorkflowChangeHistory.Add(BindChangeHistWorkflow(OriWorkflowData.ActivityID, "AssignTo", "Assign To", strWorkflowOldAssignName, strWorkflowNewAssignName));
                        objworkflow.WorkflowChangeHistory.Add(BindChangeHistWorkflow(OriWorkflowData.ActivityID, "WorkflowStatus", "Workflow Status", strWorkflowOldStatus.ParamDesc, strWorkflowNewStatus));
                    }
                    else
                    {
                        objworkflow.WorkflowChangeHistory.Add(BindChangeHistWorkflow(OriWorkflowData.CaseID, "AssignTo", "Assign To", strWorkflowOldAssignName, strWorkflowNewAssignName));
                        objworkflow.WorkflowChangeHistory.Add(BindChangeHistWorkflow(OriWorkflowData.CaseID, "WorkflowStatus", "Workflow Status", strWorkflowOldStatus.ParamDesc, strWorkflowNewStatus));

                    }
                }
                else if (objworkflow.Workflow.DecisionID == (int)Constant.WorkFlow.WorkflowDecision.Recall)
                {
                    objworkflow.Workflow.WFStatusID = (int)Constant.WorkFlow.WorkflowStatus.PendingAction;
                    assignedUser.ID = objworkflow.Workflow.AssignBy;
                    assignedUser.DisplayName = objworkflow.Workflow.AssignByName;
                    assignedUser.Email = objworkflow.Workflow.AssignByEmail;
                    assignedUser.AssignToType = (int)Constant.WorkFlow.AssignToType.User;
                    OriWorkflowData.WFStatusID = objworkflow.Workflow.WFStatusID;
                    OriWorkflowData.DecisionID = objworkflow.Workflow.DecisionID;

                    // Add Assign To Old and new value to Change History Table
                    var strWorkflowOldAssignName = OriWorkflowData.AssignToName;
                    var strWorkflowNewAssignName = assignedUser.DisplayName;

                    // Add Workflow Status Old and new value to Change History Table
                    var strWorkflowOldStatus = AdminService.LookupParamSys.Where(x => x.LookupParamSysID == OriWorkflowData.WFStatusID).FirstOrDefault();
                    var strWorkflowNewStatus = Utilities.GetEnumDescription(Constant.WorkFlow.WorkflowStatus.PendingAction);

                    if (objworkflow.Workflow.ActivityID > 0)
                    {
                        objworkflow.WorkflowChangeHistory.Add(BindChangeHistWorkflow(OriWorkflowData.ActivityID, "AssignTo", "Assign To", strWorkflowOldAssignName, strWorkflowNewAssignName));
                        objworkflow.WorkflowChangeHistory.Add(BindChangeHistWorkflow(OriWorkflowData.ActivityID, "WorkflowStatus", "Workflow Status", strWorkflowOldStatus.ParamDesc, strWorkflowNewStatus));
                    }
                    else
                    {
                        objworkflow.WorkflowChangeHistory.Add(BindChangeHistWorkflow(OriWorkflowData.CaseID, "AssignTo", "Assign To", strWorkflowOldAssignName, strWorkflowNewAssignName));
                        objworkflow.WorkflowChangeHistory.Add(BindChangeHistWorkflow(OriWorkflowData.CaseID, "WorkflowStatus", "Workflow Status", strWorkflowOldStatus.ParamDesc, strWorkflowNewStatus));
                    }
                }
                else if (objworkflow.Workflow.DecisionID == (int)Constant.WorkFlow.WorkflowDecision.Return)
                {
                    objworkflow.Workflow.WFStatusID = (int)Constant.WorkFlow.WorkflowStatus.PendingAction;
                    OriWorkflowData.WFStatusID = objworkflow.Workflow.WFStatusID;
                    OriWorkflowData.DecisionID = objworkflow.Workflow.DecisionID;

                    assignedUser.AssignToType = (int)Constant.WorkFlow.AssignToType.User;
                    assignedUser.ID = OriWorkflowData.AssignBy;
                    assignedUser.DisplayName = OriWorkflowData.AssignByName;
                    var userdata = user.GetUserEmailByLoginID(assignedUser.ID);
                    if (userdata != null)
                    {
                        assignedUser.Email = userdata.Email;
                    }
                    else
                    {
                        assignedUser.Email = "";
                    }

                    // Add Assign To Old and new value to Change History Table
                    var strWorkflowOldAssignName = OriWorkflowData.AssignToName;
                    var strWorkflowNewAssignName = assignedUser.DisplayName;

                    // Add Workflow Status Old and new value to Change History Table
                    var strWorkflowOldStatus = AdminService.LookupParamSys.Where(x => x.LookupParamSysID == OriWorkflowData.WFStatusID).FirstOrDefault();
                    var strWorkflowNewStatus = Utilities.GetEnumDescription(Constant.WorkFlow.WorkflowStatus.PendingAction);

                    if (objworkflow.Workflow.ActivityID > 0)
                    {
                        objworkflow.WorkflowChangeHistory.Add(BindChangeHistWorkflow(OriWorkflowData.ActivityID, "AssignTo", "Assign To", strWorkflowOldAssignName, strWorkflowNewAssignName));
                        objworkflow.WorkflowChangeHistory.Add(BindChangeHistWorkflow(OriWorkflowData.ActivityID, "WorkflowStatus", "Workflow Status", strWorkflowOldStatus.ParamDesc, strWorkflowNewStatus));

                    }
                    else
                    {
                        objworkflow.WorkflowChangeHistory.Add(BindChangeHistWorkflow(OriWorkflowData.CaseID, "AssignTo", "Assign To", strWorkflowOldAssignName, strWorkflowNewAssignName));
                        objworkflow.WorkflowChangeHistory.Add(BindChangeHistWorkflow(OriWorkflowData.CaseID, "WorkflowStatus", "Workflow Status", strWorkflowOldStatus.ParamDesc, strWorkflowNewStatus));
                    }
                }

                // Construct workflow remarks according to decision status
                objworkflow.Workflow.Remarks = WorkflowSystemAutoRemark(objworkflow.Workflow.DecisionID, assignedUser.DisplayName);
            }

            if (assignedUser.Email == null)
            {
                assignedUser.Email = "";
            }

            if (assignedUser.ManagerFunctionRoleID == null)
            {
                assignedUser.ManagerFunctionRoleID = "";
            }

            // TODO : Transaction Scope
            using (var scope = new TransactionScope(TransactionScopeOption.Required))
            {
                if (OriWorkflowData != null)
                {
                    if (OriWorkflowData.CaseID != 0 || OriWorkflowData.ActivityID != 0)
                    {
                        OriWorkflowData.AssignBy = objworkflow.Workflow.AssignBy;
                        OriWorkflowData.AssignByName = objworkflow.Workflow.AssignByName;
                        repo.InsertWorkflowHistory(OriWorkflowData, now, objworkflow.Workflow.TableType);
                    }
                }

                DataTable dTblResult = repo.InsertWorkflow(objworkflow, assignedUser, now, objworkflow.Workflow.TableType);
                if (dTblResult != null && dTblResult.Rows.Count > 0)
                {
                    if (objworkflow.Workflow.TableType == 2)
                    {
                        strCaseRefNo = dTblResult.Rows[0]["CaseRefNo"].ToString();  //2-->Is For Case Workflow
                    }
                    else
                    {
                        strActRefNo = dTblResult.Rows[0]["ActRefNo"].ToString();   //3-->For Activity Workflow
                    }

                    lgWFStatusID = Convert.ToInt64(dTblResult.Rows[0]["WFStatusID"]);
                    dTblReturnResult = dTblResult;  // Return select data after workflow insert successfully

                }

                lst = InsertChangeHistoryDetail(objworkflow.WorkflowChangeHistory, objworkflow.Workflow.LastUpdatedByName, objworkflow.Workflow.LastUpdatedBy, now, objworkflow.Workflow.TableType);
                if (objworkflow.Workflow.ActivityID > 0)
                {
                    repo.UpdateActivityOrCaseForWorkflowAssign(objworkflow.Workflow, lgWFStatusID, assignedUser, (int)Constant.TableType.Activity, now);
                }
                else
                {
                    repo.UpdateActivityOrCaseForWorkflowAssign(objworkflow.Workflow, lgWFStatusID, assignedUser, (int)Constant.TableType.Case, now);
                }
                scope.Complete();
            }

            return lst;
        }

        public List<ChangeHistoryUpdateViewModel> ReassignWorkflow(ref WorkflowUpdateViewModel objworkflow, DateTime now)
        {
            List<ChangeHistoryUpdateViewModel> lstWorkflow = new List<ChangeHistoryUpdateViewModel>();
            int intDecisionID = objworkflow.Workflow.DecisionID.Value;
            int intWFStatusID = (int)Constant.WorkFlow.WorkflowStatus.PendingAction;
            List<WorkflowUserViewModel> lstAssignedUser = new List<WorkflowUserViewModel>();

            // Check for update conflict

            // Change History
            foreach (WorkflowActionViewModel reassignWF in objworkflow.WorkFlowList)
            {
                //reassignWF.WorkflowID = objworkflow.Workflow.WorkflowID;
                reassignWF.AssignTo = objworkflow.Workflow.AssignTo;
                reassignWF.AssignToTypeID = objworkflow.Workflow.AssignToTypeID;
                reassignWF.AssignToName = objworkflow.Workflow.AssignToName;
                reassignWF.AssignToEmail = objworkflow.Workflow.AssignToEmail;
                reassignWF.WFStatusID = objworkflow.Workflow.WFStatusID;
                reassignWF.StartDate = now;
                reassignWF.AssignBy = objworkflow.Workflow.AssignBy;
                reassignWF.AssignByName = objworkflow.Workflow.AssignByName;
                reassignWF.CreatedBy = objworkflow.Workflow.CreatedBy;
                reassignWF.LastUpdatedBy = objworkflow.Workflow.LastUpdatedBy;
                // Workflow Remarks
                objworkflow.Workflow.Remarks = WorkflowSystemAutoRemark(intDecisionID, objworkflow.Workflow.AssignTo.DisplayName);
                objworkflow.Workflow.CreatedBy = objworkflow.Workflow.LastUpdatedBy = objworkflow.Workflow.AssignBy;
                objworkflow.Workflow.AssignToTypeID = objworkflow.Workflow.UserType;
                objworkflow.Workflow.WFStatusID = intWFStatusID;
                DataTable dTbl = new DataTable();
                // Transaction scope
                using (var scope = new TransactionScope(TransactionScopeOption.Required))
                {
                    try
                    {
                        int intResult = 0;
                        // Insert workflow
                        intResult = repo.InsertWFForReassignment(objworkflow, reassignWF.WorkflowID, reassignWF.IsNewCase, now);
                        // Update case/ activity                    
                        dTbl = repo.UpdateTableWorkflowAssign(objworkflow, intWFStatusID, reassignWF.WorkflowID, reassignWF.IsNewCase, now);
                        if (reassignWF.IsNewCase==0)
                        {
                            // Insert workflow history
                            intResult = repo.MoveWFHistoryForReassignment(objworkflow, reassignWF.WorkflowID, now);
                        }

                        scope.Complete();
                    }
                    catch (Exception ex)
                    {
                        ErrorService svcError = new ErrorService();
                        svcError.LogError((reassignWF.IsNewCase==1?"Case ID : ": "Workflow ID : " + reassignWF.WorkflowID.ToStringSafe()+ "     - ") 
                                    + ex.Message + " " + ex.StackTrace, userSVC.UserInfo.LoginID);
                        continue;
                    }
                }

                if (dTbl != null && dTbl.Rows.Count > 0)
                {
                    reassignWF.CaseID = Convert.ToInt64(dTbl.Rows[0]["CaseID"]);
                    reassignWF.CaseRefNo = dTbl.Rows[0]["CaseRefNo"].ToStringSafe();
                }
            }

            return lstWorkflow;
        }

        public string WorkflowSystemAutoRemark(int? DecisionID, string username)
        {
            var remark = string.Empty;
            if (DecisionID == (int)Constant.WorkFlow.WorkflowDecision.Assign)
            {
                remark = string.Format("{0}: {1} {2} ", Constant.WorkFlow.WorkflowDecision.Assign, " Pending Action By ", username);
            }
            else if (DecisionID == (int)Constant.WorkFlow.WorkflowDecision.Recall)
            {
                remark = string.Format("{0}: {1} {2} ", Constant.WorkFlow.WorkflowDecision.Recall, " Pending Action By ", username);
            }
            else if (DecisionID == (int)Constant.WorkFlow.WorkflowDecision.Return)
            {
                remark = string.Format("{0}: {1} {2} ", Constant.WorkFlow.WorkflowDecision.Return, " Pending Action By ", username);
            }
            else if (DecisionID == (int)Constant.WorkFlow.WorkflowDecision.Close)
            {
                remark = string.Format("{0}: {1} {2} ", Constant.WorkFlow.WorkflowDecision.Close, " Closed By ", username);
            }
            else if (DecisionID == (int)Constant.WorkFlow.WorkflowDecision.Reassign)
            {
                remark = string.Format("{0}: {1} {2} ", Constant.WorkFlow.WorkflowDecision.Reassign, " Reassign By ", username);
            }

            return remark;
        }

        public List<ChangeHistoryUpdateViewModel> InsertChangeHistoryDetail(List<ChangeHistoryUpdateViewModel> model, string strUserName, string strUserID, DateTime dtUpdated, int TableType)
        {
            int intReturnValue = 0;
            List<ChangeHistoryUpdateViewModel> lst = model.Where(x => x.IsChanged).ToList();
            intReturnValue = repo.InsertChangeHistoryDetail(lst, strUserName, strUserID, dtUpdated, TableType);

            return lst;
        }

        private ChangeHistoryUpdateViewModel BindChangeHistWorkflow(long caseID, string strFieldName, string strDisplayName, string strOldValue, string strNewValue)
        {
            ChangeHistoryUpdateViewModel changeHistVM = new ChangeHistoryUpdateViewModel();
            changeHistVM.ID = caseID;
            changeHistVM.FieldName = strFieldName;
            changeHistVM.DisplayName = strDisplayName;
            changeHistVM.OldValue = strOldValue;
            changeHistVM.NewValue = strNewValue;
            changeHistVM.IsChanged = !(changeHistVM.OldValue == changeHistVM.NewValue);

            return changeHistVM;
        }

        public WorkflowAssignToUnitIDViewModel GetCaseAssignDefaultUnitByUnitID(int UnitID)
        {
            return repo.GetCaseAssignDefaultUnitByUnitID(UnitID);
        }
    }
}
