public async Task <InvokeResult> HandleDeviceExceptionAsync(DeviceException exception, EntityHeader org, EntityHeader user)
        {
            if (exception == null)
            {
                throw new ArgumentNullException(nameof(exception));
            }
            if (org == null)
            {
                throw new ArgumentNullException(nameof(org));
            }
            if (user == null)
            {
                throw new ArgumentNullException(nameof(user));
            }

            Console.ForegroundColor = ConsoleColor.Magenta;
            Console.WriteLine("Handling Device Exception.");

            var repo = await _repoManager.GetDeviceRepositoryWithSecretsAsync(exception.DeviceRepositoryId, org, user);

            var device = await _deviceManager.GetDeviceByDeviceIdAsync(repo, exception.DeviceUniqueId, org, user);

            var deviceConfig = await _deviceConfigManager.GetDeviceConfigurationAsync(device.DeviceConfiguration.Id, org, user);

            var deviceErrorCode = deviceConfig.ErrorCodes.FirstOrDefault(err => err.Key == exception.ErrorCode);

            if (deviceErrorCode == null)
            {
                return(InvokeResult.FromError($"Could not find error code [{exception.ErrorCode}] on device configuration [{deviceConfig.Name}] for device [{device.Name}]"));
            }

            if (!EntityHeader.IsNullOrEmpty(deviceErrorCode.ServiceTicketTemplate))
            {
                Console.WriteLine("Generating service ticket (every occurence.");
                var request = new CreateServiceTicketRequest()
                {
                    TemplateId = deviceErrorCode.ServiceTicketTemplate.Id,
                    DeviceId   = device.DeviceId,
                    Details    = exception.Details,
                    DontCreateIfOpenForDevice = !deviceErrorCode.TriggerOnEachOccurrence,
                    RepoId = repo.Id
                };

                var result = await CreateServiceTicketAsync(request, org, user);

                if (!result.Successful)
                {
                    return(result.ToInvokeResult());
                }
            }
            else
            {
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.WriteLine("No service ticket, skipping.");
            }

            var deviceError = device.Errors.Where(err => err.DeviceErrorCode == exception.ErrorCode).FirstOrDefault();

            if (deviceError == null)
            {
                deviceError = new DeviceError()
                {
                    Count           = 1,
                    DeviceErrorCode = exception.ErrorCode,
                    FirstSeen       = DateTime.UtcNow.ToJSONString(),
                    LastDetails     = exception.Details,
                    Timestamp       = DateTime.UtcNow.ToJSONString(),
                    Expires         = deviceErrorCode.AutoexpireTimespan.Value.AddTimeSpan(deviceErrorCode.AutoexpireTimespanQuantity.Value)
                };

                device.Errors.Add(deviceError);
            }

            if (!EntityHeader.IsNullOrEmpty(deviceErrorCode.DistroList))
            {
                if (deviceError == null || String.IsNullOrEmpty(deviceError.NextNotification) || (deviceError.NextNotification.ToDateTime().ToUniversalTime() < DateTime.UtcNow))
                {
                    var result = await _distroManager.GetListAsync(deviceErrorCode.DistroList.Id, org, user);

                    var subject = String.IsNullOrEmpty(deviceErrorCode.EmailSubject) ? deviceErrorCode.Name : deviceErrorCode.EmailSubject.Replace("[DEVICEID]", device.DeviceId).Replace("[DEVICENAME]", device.Name);

                    foreach (var notificationUser in result.AppUsers)
                    {
                        var appUser = await _userManager.FindByIdAsync(notificationUser.Id);

                        if (deviceErrorCode.SendEmail)
                        {
                            var body = $"The error code [{deviceErrorCode.Key}] was detected on the device {device.Name}<br>{deviceErrorCode.Description}<br>{exception.Details}";
                            if (exception.AdditionalDetails.Any())
                            {
                                body += "<br>";
                                body += "<b>Additional Details:<br /><b>";
                                body += "<ul>";
                                foreach (var detail in exception.AdditionalDetails)
                                {
                                    body += $"<li>{detail}</li>";
                                }

                                body += "</ul>";
                            }
                            await _emailSender.SendAsync(appUser.Email, subject, body);
                        }

                        if (deviceErrorCode.SendSMS)
                        {
                            var body = $"Device {device.Name} generated error code [${deviceErrorCode.Key}] {deviceErrorCode.Description} {exception.Details}";
                            await _smsSender.SendAsync(appUser.PhoneNumber, body);
                        }
                    }

                    if (EntityHeader.IsNullOrEmpty(deviceErrorCode.NotificationIntervalTimeSpan))
                    {
                        deviceError.NextNotification = null;
                    }
                    else if (deviceErrorCode.NotificationIntervalQuantity.HasValue && deviceErrorCode.NotificationIntervalTimeSpan.Value != TimeSpanIntervals.NotApplicable)
                    {
                        deviceError.NextNotification = deviceErrorCode.NotificationIntervalTimeSpan.Value.AddTimeSpan(deviceErrorCode.NotificationIntervalQuantity.Value);
                    }
                    else
                    {
                        deviceError.NextNotification = null;
                        Logger.AddCustomEvent(Core.PlatformSupport.LogLevel.Error, "ServiceTicketManager__HandleDeviceExceptionAsync", "Invalid quantity on NotificationIntervalQuantity", device.DeviceId.ToKVP("deviceId"), exception.ErrorCode.ToKVP("errorCode"));
                    }
                }
                else
                {
                    if (String.IsNullOrEmpty(deviceError.NextNotification))
                    {
                        Console.WriteLine("NExt Notification is null....");
                    }
                    else
                    {
                        Console.WriteLine("When to send next notification: " + deviceError.NextNotification);
                    }
                }
            }
            else
            {
                Console.WriteLine("No distro, skipping.");
            }

            Console.ResetColor();

            await _deviceManager.UpdateDeviceAsync(repo, device, org, user);

            return(InvokeResult.Success);
        }
 public Task <InvokeResult <string> > CreateTicketAsync([FromBody] CreateServiceTicketRequest request)
 {
     return(_ticketCreator.CreateServiceTicketAsync(request, OrgEntityHeader, UserEntityHeader));
 }
        public async Task <InvokeResult <string> > CreateServiceTicketAsync(CreateServiceTicketRequest createServiceTicketRequest, EntityHeader org, EntityHeader user)
        {
            if (createServiceTicketRequest == null)
            {
                throw new ArgumentNullException(nameof(createServiceTicketRequest));
            }
            if (String.IsNullOrEmpty(createServiceTicketRequest.RepoId))
            {
                throw new ArgumentNullException(createServiceTicketRequest.RepoId);
            }
            if (String.IsNullOrEmpty(createServiceTicketRequest.DeviceId) &&
                String.IsNullOrEmpty(createServiceTicketRequest.DeviceUniqueId))
            {
                throw new ArgumentNullException(nameof(createServiceTicketRequest.DeviceId) + " and " + nameof(createServiceTicketRequest.DeviceUniqueId));
            }
            if (String.IsNullOrEmpty(createServiceTicketRequest.TemplateId) &&
                String.IsNullOrEmpty(createServiceTicketRequest.TemplateKey))
            {
                throw new ArgumentNullException(nameof(createServiceTicketRequest.TemplateId) + " and " + nameof(createServiceTicketRequest.TemplateKey));
            }

            ServiceTicketTemplate template;

            if (String.IsNullOrEmpty(createServiceTicketRequest.TemplateKey))
            {
                template = await _templateRepo.GetServiceTicketTemplateAsync(createServiceTicketRequest.TemplateId);

                if (template == null)
                {
                    throw new NullReferenceException($"Could not load ticket template for {createServiceTicketRequest.TemplateId}");
                }
            }
            else
            {
                template = await _templateRepo.GetServiceTicketTemplateByKeyAsync(org.Id, createServiceTicketRequest.TemplateKey);

                if (template == null)
                {
                    throw new NullReferenceException($"Could not load ticket template for {createServiceTicketRequest.TemplateKey}");
                }
            }

            org ??= template.OwnerOrganization;
            user ??= template.DefaultContact ?? template.CreatedBy;

            if (org == null)
            {
                throw new NullReferenceException(nameof(org));
            }
            if (user == null)
            {
                throw new NullReferenceException(nameof(user));
            }

            var repo = await _repoManager.GetDeviceRepositoryWithSecretsAsync(createServiceTicketRequest.RepoId, org, user);

            if (repo == null)
            {
                throw new InvalidOperationException($"Could not find repository for id {createServiceTicketRequest.RepoId}");
            }
            if (org != null && template.OwnerOrganization != org)
            {
                throw new InvalidOperationException("Template, org mismatch.");
            }

            Console.WriteLine("+++1");

            Device device = null;

            if (!String.IsNullOrEmpty(createServiceTicketRequest.DeviceId))
            {
                device = await _deviceManager.GetDeviceByDeviceIdAsync(repo, createServiceTicketRequest.DeviceId, template.OwnerOrganization, user ?? template.DefaultContact);

                if (device == null)
                {
                    device = await _deviceManager.GetDeviceByIdAsync(repo, createServiceTicketRequest.DeviceId, template.OwnerOrganization, user ?? template.DefaultContact);

                    if (device == null) // still null
                    {
                        throw new ArgumentNullException($"Could not find device with device id {createServiceTicketRequest.DeviceId}.");
                    }
                }
            }
            else if (!String.IsNullOrEmpty(createServiceTicketRequest.DeviceUniqueId))
            {
                device = await _deviceManager.GetDeviceByIdAsync(repo, createServiceTicketRequest.DeviceUniqueId, template.OwnerOrganization, user ?? template.DefaultContact);

                if (device == null)
                {
                    throw new ArgumentNullException($"Could not find device with device id {createServiceTicketRequest.DeviceUniqueId}.");
                }
            }
            else
            {
                throw new ArgumentNullException("Must supply either DeviceId or DeviceUniqueId to create a service ticket.");
            }

            if (org != null && device.OwnerOrganization != org)
            {
                throw new InvalidOperationException("Device, org mismatch.");
            }

            Console.WriteLine("+++2");

            var stateSet = await _ticketStatusRepo.GetTicketStatusDefinitionAsync(template.StatusType.Id);

            var defaultState = stateSet.Items.Where(st => st.IsDefault).First();

            var assignedToUser = device.AssignedUser;

            if (assignedToUser == null)
            {
                assignedToUser = repo.AssignedUser;
            }

            if (assignedToUser == null)
            {
                assignedToUser = template.DefaultContact;
            }

            var currentTimeStamp = DateTime.UtcNow.ToJSONString();

            EntityHeader <ServiceBoard> boardEH = null;

            Console.WriteLine("+++3");

            var ticketId = Guid.NewGuid().ToString();

            if (!String.IsNullOrEmpty(createServiceTicketRequest.BoardId))
            {
                var board = await _serviceBoardRepo.GetServiceBoardAsync(createServiceTicketRequest.BoardId);

                boardEH = new EntityHeader <ServiceBoard>()
                {
                    Id = board.Id, Text = board.Name
                };
                var ticketNumber = await _serviceBoardRepo.GetNextTicketNumber(createServiceTicketRequest.BoardId);

                ticketId = $"{board.BoardAbbreviation}-{ticketNumber}";

                if (assignedToUser == null && !EntityHeader.IsNullOrEmpty(board.PrimaryContact))
                {
                    assignedToUser = board.PrimaryContact;
                }
            }
            else if (!EntityHeader.IsNullOrEmpty(repo.ServiceBoard))
            {
                boardEH = new EntityHeader <ServiceBoard>()
                {
                    Id = repo.ServiceBoard.Id, Text = repo.ServiceBoard.Text
                };

                var board = await _serviceBoardRepo.GetServiceBoardAsync(repo.ServiceBoard.Id);

                var ticketNumber = await _serviceBoardRepo.GetNextTicketNumber(repo.ServiceBoard.Id);

                ticketId = $"{board.BoardAbbreviation}-{ticketNumber}";
            }

            Console.WriteLine("+++4");

            string dueDate = null;

            if (!EntityHeader.IsNullOrEmpty(template.TimeToCompleteTimeSpan) &&
                template.TimeToCompleteTimeSpan.Value != TimeSpanIntervals.NotApplicable &&
                template.TimeToCompleteQuantity.HasValue)
            {
                TimeSpan ts = TimeSpan.Zero;
                switch (template.TimeToCompleteTimeSpan.Value)
                {
                case TimeSpanIntervals.Minutes: ts = TimeSpan.FromMinutes(template.TimeToCompleteQuantity.Value); break;

                case TimeSpanIntervals.Hours: ts = TimeSpan.FromHours(template.TimeToCompleteQuantity.Value); break;

                case TimeSpanIntervals.Days: ts = TimeSpan.FromDays(template.TimeToCompleteQuantity.Value); break;
                }

                dueDate = DateTime.UtcNow.Add(ts).ToJSONString();
            }

            string statusDueDate = null;

            if (EntityHeader.IsNullOrEmpty(defaultState.TimeAllowedInStatusTimeSpan) &&
                defaultState.TimeAllowedInStatusTimeSpan.Value != TimeSpanIntervals.NotApplicable &&
                defaultState.TimeAllowedInStatusQuantity.HasValue)
            {
                TimeSpan ts = TimeSpan.Zero;
                switch (defaultState.TimeAllowedInStatusTimeSpan.Value)
                {
                case TimeSpanIntervals.Minutes: ts = TimeSpan.FromMinutes(defaultState.TimeAllowedInStatusQuantity.Value); break;

                case TimeSpanIntervals.Hours: ts = TimeSpan.FromHours(defaultState.TimeAllowedInStatusQuantity.Value); break;

                case TimeSpanIntervals.Days: ts = TimeSpan.FromDays(defaultState.TimeAllowedInStatusQuantity.Value); break;
                }

                statusDueDate = DateTime.UtcNow.Add(ts).ToJSONString();
            }

            Console.WriteLine("+++5");

            var ticket = new ServiceTicket()
            {
                Key             = template.Key,
                TicketId        = ticketId,
                DeviceRepo      = EntityHeader.Create(repo.Id, repo.Name),
                CreationDate    = currentTimeStamp,
                LastUpdatedDate = currentTimeStamp,
                DueDate         = dueDate,
                Name            = $"{template.Name} ({device.DeviceId})",
                Address         = device.Address,
                IsClosed        = false,
                Description     = template.Description,
                Subject         = String.IsNullOrEmpty(createServiceTicketRequest.Subject) ? $"{template.Name} ({device.DeviceId})" : createServiceTicketRequest.Subject,
                AssignedTo      = assignedToUser,
                Template        = new EntityHeader <ServiceTicketTemplate>()
                {
                    Id = template.Id, Text = template.Name
                },
                ServiceBoard = boardEH,
                Device       = new EntityHeader <IoT.DeviceManagement.Core.Models.Device>()
                {
                    Id = device.Id, Text = device.Name
                },
                Status               = EntityHeader.Create(defaultState.Key, defaultState.Name),
                StatusDate           = DateTime.UtcNow.ToJSONString(),
                OwnerOrganization    = template.OwnerOrganization,
                HoursEstimate        = template.HoursEstimate,
                CostEstimate         = template.CostEstimate,
                SkillLevel           = template.SkillLevel,
                Urgency              = template.Urgency,
                Tools                = template.Tools,
                PartsKits            = template.PartsKits,
                ServiceParts         = template.ServiceParts,
                Instructions         = template.Instructions,
                StatusType           = template.StatusType,
                StatusDueDate        = statusDueDate,
                Resources            = template.Resources,
                TroubleshootingSteps = template.TroubleshootingSteps,
                CreatedBy            = user,
                LastUpdatedBy        = user
            };

            ticket.StatusType.Value = stateSet;

            ticket.History.Add(new ServiceTicketStatusHistory()
            {
                AddedBy   = user,
                DateStamp = DateTime.UtcNow.ToJSONString(),
                Status    = ticket.Status.Text,
                Note      = $"Created service ticket with {defaultState.Name} status."
            });

            Console.WriteLine("+++6");

            ticket.Notes.Add(new ServiceTicketNote()
            {
                AddedBy   = user,
                DateStamp = currentTimeStamp,
                Note      = assignedToUser != null ? $"Service ticket created and assigned to {assignedToUser.Text}." : "Service ticket created and not assigned to technician."
            });

            if (!String.IsNullOrEmpty(createServiceTicketRequest.Details))
            {
                ticket.Notes.Add(new ServiceTicketNote()
                {
                    Id        = Guid.NewGuid().ToString(),
                    Note      = createServiceTicketRequest.Details,
                    AddedBy   = ticket.CreatedBy,
                    DateStamp = DateTime.UtcNow.ToJSONString()
                });
            }

            Console.WriteLine("+++7");


            await _repo.AddServiceTicketAsync(ticket);

            Console.WriteLine("+++8");

            await SendTicketNotificationAsync(ticket);

            Console.WriteLine("+++9");

            return(InvokeResult <string> .Create(ticket.TicketId));
        }