/// <summary>
            /// Validates the parent object.
            /// </summary>
            public WatchbillValidator()
            {
                RuleFor(x => x.Title).NotEmpty().Length(1, 50);

                RuleFor(x => x.CreatedBy).NotEmpty();
                RuleFor(x => x.CurrentState).NotEmpty();
                RuleFor(x => x.Command).NotEmpty();
                RuleFor(x => x.LastStateChange).NotEmpty();
                RuleFor(x => x.LastStateChangedBy).NotEmpty();
                RuleFor(x => x.EligibilityGroup).NotEmpty();

                RuleFor(x => x.WatchShifts).SetCollectionValidator(new WatchShift.WatchShiftValidator());
                RuleFor(x => x.InputRequirements).SetCollectionValidator(new WatchInputRequirement.WatchInputRequirementValidator());
                RuleFor(x => x.Range).Must(x => x.Start <= x.End);

#pragma warning disable CS0618 // Type or member is obsolete
                Custom(watchbill =>
                {
                    var shiftsByType = watchbill.WatchShifts.GroupBy(x => x.ShiftType);

                    List <string> errorElements = new List <string>();

                    //Make sure that none of the shifts overlap.
                    foreach (var group in shiftsByType)
                    {
                        var shifts = group.ToList();
                        foreach (var shift in shifts)
                        {
                            var shiftRange = new Itenso.TimePeriod.TimeRange(shift.Range.Start, shift.Range.End, false);
                            foreach (var otherShift in shifts.Where(x => x.Id != shift.Id))
                            {
                                var otherShiftRange = new Itenso.TimePeriod.TimeRange(otherShift.Range.Start, otherShift.Range.End, false);
                                if (shiftRange.OverlapsWith(otherShiftRange))
                                {
                                    errorElements.Add($"{group.Key} shifts: {String.Join(" ; ", otherShiftRange.ToString())}");
                                }
                            }
                        }
                    }

                    var watchbillTimeRange = new Itenso.TimePeriod.TimeRange(watchbill.Range.Start, watchbill.Range.End, true);

                    if (errorElements.Any())
                    {
                        string str = $"One or more shifts with the same type overlap:  {String.Join(" | ", errorElements)}";
                        return(new FluentValidation.Results.ValidationFailure(nameof(watchbill.WatchShifts), str));
                    }

                    return(null);
                });
#pragma warning restore CS0618 // Type or member is obsolete
            }
        /// <summary>
        /// Checks if alerts have been sent for upcoming watch assignments, and sends them if they haven't.
        /// </summary>
        private static void SendWatchAlerts()
        {
            using (var session = DataProvider.GetSession())
            {
                using (var transaction = session.BeginTransaction())
                {
                    try
                    {
                        var assignments = session.QueryOver <WatchAssignment>().Where(x => x.NumberOfAlertsSent != 2).List();

                        var hourRange = new Itenso.TimePeriod.TimeRange(DateTime.UtcNow.AddHours(-1), DateTime.UtcNow);
                        var dayRange  = new Itenso.TimePeriod.TimeRange(DateTime.UtcNow.AddDays(-1), DateTime.UtcNow);

                        foreach (var assignment in assignments)
                        {
                            if (assignment.NumberOfAlertsSent == 0)
                            {
                                if (dayRange.IntersectsWith(new Itenso.TimePeriod.TimeRange(assignment.WatchShift.Range.Start)))
                                {
                                    var model = new Email.Models.UpcomingWatchEmailModel
                                    {
                                        WatchAssignment = assignment
                                    };

                                    var addresses = assignment.PersonAssigned.EmailAddresses
                                                    .Where(x => x.IsPreferred)
                                                    .Select(x => new System.Net.Mail.MailAddress(x.Address, assignment.PersonAssigned.ToString()));

                                    Email.EmailInterface.CCEmailMessage
                                    .CreateDefault()
                                    .To(addresses)
                                    .CC(Email.EmailInterface.CCEmailMessage.DeveloperAddress)
                                    .Subject("Upcoming Watch")
                                    .HTMLAlternateViewUsingTemplateFromEmbedded("CommandCentral.Email.Templates.UpcomingWatch_HTML.html", model)
                                    .SendWithRetryAndFailure(TimeSpan.FromSeconds(1));

                                    assignment.NumberOfAlertsSent++;

                                    session.Update(assignment);
                                }
                            }
                            else if (assignment.NumberOfAlertsSent == 1)
                            {
                                if (hourRange.IntersectsWith(new Itenso.TimePeriod.TimeRange(assignment.WatchShift.Range.Start)))
                                {
                                    var model = new Email.Models.UpcomingWatchEmailModel
                                    {
                                        WatchAssignment = assignment
                                    };

                                    var addresses = assignment.PersonAssigned.EmailAddresses
                                                    .Where(x => x.IsPreferred)
                                                    .Select(x => new System.Net.Mail.MailAddress(x.Address, assignment.PersonAssigned.ToString()));

                                    Email.EmailInterface.CCEmailMessage
                                    .CreateDefault()
                                    .To(addresses)
                                    .CC(Email.EmailInterface.CCEmailMessage.DeveloperAddress)
                                    .Subject("Upcoming Watch")
                                    .HTMLAlternateViewUsingTemplateFromEmbedded("CommandCentral.Email.Templates.UpcomingWatch_HTML.html", model)
                                    .SendWithRetryAndFailure(TimeSpan.FromSeconds(1));

                                    assignment.NumberOfAlertsSent++;

                                    session.Update(assignment);
                                }
                            }
                            else
                            {
                                throw new NotImplementedException("How the f**k did we get here?");
                            }
                        }

                        transaction.Commit();
                    }
                    catch
                    {
                        transaction.Rollback();
                        throw;
                    }
                }
            }
        }