<commit_before>﻿using System;
using System.Collections.Generic;
using System.Linq;
using AutoMapper;
using SignInCheckIn.Services.Interfaces;
using Crossroads.Utilities.Services.Interfaces;
using MinistryPlatform.Translation.Models.DTO;
using MinistryPlatform.Translation.Repositories.Interfaces;
using SignInCheckIn.Models.DTO;

namespace SignInCheckIn.Services
{
    public class SignInLogic : ISignInLogic
    {
        private readonly IEventRepository _eventRepository;
        private readonly IApplicationConfiguration _applicationConfiguration;
        private readonly IGroupRepository _groupRepository;
        private readonly IRoomRepository _roomRepository;
        private readonly IParticipantRepository _participantRepository;
        private readonly IChildSigninRepository _childSigninRepository;

        private readonly int _defaultEarlyCheckinPeriod;
        private readonly int _defaultLateCheckinPeriod;

        public SignInLogic(IEventRepository eventRepository, IApplicationConfiguration applicationConfiguration, IConfigRepository configRepository,
            IGroupRepository groupRepository, IRoomRepository roomRepository, IParticipantRepository participantRepository, IChildSigninRepository childSigninRepository)
        {
            _eventRepository = eventRepository;
            _applicationConfiguration = applicationConfiguration;
            _groupRepository = groupRepository;
            _roomRepository = roomRepository;
            _participantRepository = participantRepository;
            _childSigninRepository = childSigninRepository;

            _defaultEarlyCheckinPeriod = int.Parse(configRepository.GetMpConfigByKey("DefaultEarlyCheckIn").Value);
            _defaultLateCheckinPeriod = int.Parse(configRepository.GetMpConfigByKey("DefaultLateCheckIn").Value);
        }

        public List<ParticipantDto> SignInParticipants(ParticipantEventMapDto participantEventMapDto, List<MpEventDto> currentEvents)
        //public List<MpEventParticipantDto> SignInParticipants(ParticipantEventMapDto participantEventMapDto, List<MpEventDto> currentEvents)
        {
            var mpEventParticipantList = new List<MpEventParticipantDto>();

            // we need to get the events on a per person basis, when doing this - the previous check for duplicate signins needs to 
            // be pulling all events on the day
            foreach (var participant in participantEventMapDto.Participants.Where(r => r.DuplicateSignIn == false && r.Selected == true))
            {
                mpEventParticipantList.AddRange(SignInParticipant(participant, participantEventMapDto, currentEvents));
            }

            var mappedParticipants = mpEventParticipantList.Select(Mapper.Map<ParticipantDto>).ToList();
            return mappedParticipants;
            //return mpEventParticipantList;
        }

        public List<MpEventParticipantDto> SignInParticipant(ParticipantDto participant, ParticipantEventMapDto participantEventMapDto, List<MpEventDto> currentEvents)
        {
            var mpEventParticipantList = new List<MpEventParticipantDto>();

            // if a participant is under 3, by axiom, they do not get signed into adventure club
            var participantAge = System.DateTime.Now - participant.DateOfBirth;
            var underThreeSignIn = (participantAge.Days / 365 < 3) ? true : false;

            var adventureClubSignIn = (participantEventMapDto.ServicesAttended == 2);

            // get the events they can sign into
            var eligibleEvents = EvaluateSignInEvents(participantEventMapDto.CurrentEvent.EventSiteId, adventureClubSignIn, underThreeSignIn, currentEvents);

            // // TODO: Add check here to make sure there's a group assigned for the event room, otherwise set an error --
            // this actually needs to be run after the attempt to sign in, so we can post-mortem why they couldn't sign in
            var eventRooms = GetSignInEventRooms(participant.GroupId.GetValueOrDefault(), eligibleEvents.Select(r => r.EventId).ToList());

            // the first condition is for non-ac signins, second is for ac signins 
            // we will need to set error messages on them as part of a sub-function
            if (eligibleEvents.Any(r => r.ParentEventId != null))
            {
                mpEventParticipantList.AddRange(AssignParticipantToRoomsWithAc(eventRooms, eligibleEvents, participant));
            }
            else
            {
                mpEventParticipantList.AddRange(AssignParticipantToRoomsNonAc(eventRooms, eligibleEvents, participant));
            }

            SyncInvalidSignins(mpEventParticipantList, participant);

            AuditSigninIssues(participantEventMapDto, mpEventParticipantList, eligibleEvents);

            // save the participant if they are selected and have a valid room assignment - moved down here so that we
            // don't sign in multiple kids to a single room over capacity -- also, we want to make sure that 
            // we are using this logic correctly - getting a rock vs. no sign in, so we may still need the
            // mp event participants to be created
            if (participant.Selected == true && participant.AssignedRoomId != null && mpEventParticipantList.Any())
            {
                mpEventParticipantList = _childSigninRepository.CreateEventParticipants(mpEventParticipantList);
            }

            return mpEventParticipantList;
        } 

        // parameters determine the behavior of what events we get back
        public List<MpEventDto> EvaluateSignInEvents(int siteId, bool adventureClubSignIn, bool underThree, List<MpEventDto> currentEvents)
        {
            List<MpEventDto> eligibleEvents = new List<MpEventDto>();

            // return only a single event
            if (adventureClubSignIn == false)
            {
                eligibleEvents.Add(currentEvents.First(r => r.ParentEventId == null));
                return eligibleEvents;
            }

            if (underThree == true)
            {
                var serviceEventSet = currentEvents.Where(r => r.ParentEventId == null).ToList();

                // we need to get first two event services, and no matching ac events
                for (int i = 0; i < 2; i++)
                {
                    eligibleEvents.Add(serviceEventSet[i]);
                }

                return eligibleEvents;
            }

            if (adventureClubSignIn == true)
            {
                var serviceEventSet = currentEvents.Where(r => r.ParentEventId == null).ToList();

                // we need to get first two event services, and then the matching ac events - if there's
                // a combination of 1, 1AC, 2, 3, 3AC, 3 and 3 AC are not considered for checkin
                for(int i = 0; i < 2; i++)
                {
                    eligibleEvents.Add(serviceEventSet[i]);

                    if (currentEvents.Any(r => r.ParentEventId == serviceEventSet[i].EventId))
                    {
                        eligibleEvents.Add(currentEvents.First(r => r.ParentEventId == serviceEventSet[i].EventId));
                    }
                }

                return eligibleEvents;
            }

            return null;
        }

        public List<MpEventRoomDto> GetSignInEventRooms(int groupId, List<int> eventIds)
        {
            // return all event rooms which match up to groups on the events
            var eventGroups = _eventRepository.GetEventGroupsByGroupIdAndEventIds(groupId, eventIds);
            var eventRoomIds = eventGroups.Select(r => r.RoomReservationId.GetValueOrDefault()).ToList();
            var eventRooms = _roomRepository.GetEventRoomsByEventRoomIds(eventRoomIds).Where(r => r.AllowSignIn).ToList();

            return eventRooms;
        }

        // set assignments for non-ac rooms here -- this is not picking up when a partici
        public List<MpEventParticipantDto> AssignParticipantToRoomsNonAc(List<MpEventRoomDto> eventRoomDtos, List<MpEventDto> eventDtos, ParticipantDto participant)
        {
            var mpEventParticipantRecords = new List<MpEventParticipantDto>();

            // sort the events in ascending time - want to start with the first service event for this
            eventDtos = eventDtos.OrderBy(r => r.EventStartDate).ToList();

            foreach (var serviceEvent in eventDtos)
            {
                // need to make sure that there is only a single event room returned on these...
                var eventRoom = eventRoomDtos.FirstOrDefault(r => r.EventId == serviceEvent.EventId);
                if (eventRoom == null)
                {
                    continue;
                }

                if (eventRoom.Capacity > (eventRoom.SignedIn + eventRoom.CheckedIn))
                {
                    // consider just passing down the event room?
                    var mpEventParticipantDto = TranslateParticipantDtoToMpEventParticipantDto(participant, eventRoom.EventId, eventRoom.RoomId);
                    mpEventParticipantRecords.Add(mpEventParticipantDto);
                }
                else
                {
                    var bumpedRoom = ProcessBumpingRules(serviceEvent.EventId, eventRoom.EventRoomId.GetValueOrDefault());

                    if (bumpedRoom != null)
                    {
                        var mpEventParticipantDto = TranslateParticipantDtoToMpEventParticipantDto(participant, serviceEvent.EventId, bumpedRoom.RoomId);
                        mpEventParticipantRecords.Add(mpEventParticipantDto);
                    }
                    // if they have no room assignment, add an unsignedin participant so that their label gets printed appropriately
                    else
                    {
                        var mpEventParticipantDto = TranslateParticipantDtoToMpEventParticipantDto(participant, serviceEvent.EventId, null);
                        mpEventParticipantRecords.Add(mpEventParticipantDto);
                    }
                }
            }
<commit_msg>DE2974 - Sign In More Than Capacity<commit_after>﻿using System;
using System.Collections.Generic;
using System.Linq;
using AutoMapper;
using SignInCheckIn.Services.Interfaces;
using Crossroads.Utilities.Services.Interfaces;
using MinistryPlatform.Translation.Models.DTO;
using MinistryPlatform.Translation.Repositories.Interfaces;
using SignInCheckIn.Models.DTO;

namespace SignInCheckIn.Services
{
    public class SignInLogic : ISignInLogic
    {
        private readonly IEventRepository _eventRepository;
        private readonly IApplicationConfiguration _applicationConfiguration;
        private readonly IGroupRepository _groupRepository;
        private readonly IRoomRepository _roomRepository;
        private readonly IParticipantRepository _participantRepository;
        private readonly IChildSigninRepository _childSigninRepository;

        private readonly int _defaultEarlyCheckinPeriod;
        private readonly int _defaultLateCheckinPeriod;

        public SignInLogic(IEventRepository eventRepository, IApplicationConfiguration applicationConfiguration, IConfigRepository configRepository,
            IGroupRepository groupRepository, IRoomRepository roomRepository, IParticipantRepository participantRepository, IChildSigninRepository childSigninRepository)
        {
            _eventRepository = eventRepository;
            _applicationConfiguration = applicationConfiguration;
            _groupRepository = groupRepository;
            _roomRepository = roomRepository;
            _participantRepository = participantRepository;
            _childSigninRepository = childSigninRepository;

            _defaultEarlyCheckinPeriod = int.Parse(configRepository.GetMpConfigByKey("DefaultEarlyCheckIn").Value);
            _defaultLateCheckinPeriod = int.Parse(configRepository.GetMpConfigByKey("DefaultLateCheckIn").Value);
        }

        public List<ParticipantDto> SignInParticipants(ParticipantEventMapDto participantEventMapDto, List<MpEventDto> currentEvents)
        //public List<MpEventParticipantDto> SignInParticipants(ParticipantEventMapDto participantEventMapDto, List<MpEventDto> currentEvents)
        {
            var mpEventParticipantList = new List<MpEventParticipantDto>();

            // we need to get the events on a per person basis, when doing this - the previous check for duplicate signins needs to 
            // be pulling all events on the day
            foreach (var participant in participantEventMapDto.Participants.Where(r => r.DuplicateSignIn == false && r.Selected == true))
            {
                mpEventParticipantList.AddRange(SignInParticipant(participant, participantEventMapDto, currentEvents));
            }

            var mappedParticipants = mpEventParticipantList.Select(Mapper.Map<ParticipantDto>).ToList();
            return mappedParticipants;
            //return mpEventParticipantList;
        }

        public List<MpEventParticipantDto> SignInParticipant(ParticipantDto participant, ParticipantEventMapDto participantEventMapDto, List<MpEventDto> currentEvents)
        {
            var mpEventParticipantList = new List<MpEventParticipantDto>();

            // if a participant is under 3, by axiom, they do not get signed into adventure club
            var participantAge = System.DateTime.Now - participant.DateOfBirth;
            var underThreeSignIn = (participantAge.Days / 365 < 3) ? true : false;

            var adventureClubSignIn = (participantEventMapDto.ServicesAttended == 2);

            // get the events they can sign into
            var eligibleEvents = EvaluateSignInEvents(participantEventMapDto.CurrentEvent.EventSiteId, adventureClubSignIn, underThreeSignIn, currentEvents);

            // // TODO: Add check here to make sure there's a group assigned for the event room, otherwise set an error --
            // this actually needs to be run after the attempt to sign in, so we can post-mortem why they couldn't sign in
            var eventRooms = GetSignInEventRooms(participant.GroupId.GetValueOrDefault(), eligibleEvents.Select(r => r.EventId).ToList());

            // the first condition is for non-ac signins, second is for ac signins 
            // we will need to set error messages on them as part of a sub-function
            if (eligibleEvents.Any(r => r.ParentEventId != null))
            {
                mpEventParticipantList.AddRange(AssignParticipantToRoomsWithAc(eventRooms, eligibleEvents, participant));
            }
            else
            {
                mpEventParticipantList.AddRange(AssignParticipantToRoomsNonAc(eventRooms, eligibleEvents, participant));
            }

            SyncInvalidSignins(mpEventParticipantList, participant);

            AuditSigninIssues(participantEventMapDto, mpEventParticipantList, eligibleEvents);

            // save the participant if they are selected and have a valid room assignment - moved down here so that we
            // don't sign in multiple kids to a single room over capacity -- also, we want to make sure that 
            // we are using this logic correctly - getting a rock vs. no sign in, so we may still need the
            // mp event participants to be created
            if (participant.Selected == true && participant.AssignedRoomId != null && mpEventParticipantList.Any())
            {
                mpEventParticipantList = _childSigninRepository.CreateEventParticipants(mpEventParticipantList);
            }

            return mpEventParticipantList;
        } 

        // parameters determine the behavior of what events we get back
        public List<MpEventDto> EvaluateSignInEvents(int siteId, bool adventureClubSignIn, bool underThree, List<MpEventDto> currentEvents)
        {
            List<MpEventDto> eligibleEvents = new List<MpEventDto>();

            // return only a single event
            if (adventureClubSignIn == false)
            {
                eligibleEvents.Add(currentEvents.First(r => r.ParentEventId == null));
                return eligibleEvents;
            }

            if (underThree == true)
            {
                var serviceEventSet = currentEvents.Where(r => r.ParentEventId == null).ToList();

                // we need to get first two event services, and no matching ac events
                for (int i = 0; i < 2; i++)
                {
                    eligibleEvents.Add(serviceEventSet[i]);
                }

                return eligibleEvents;
            }

            if (adventureClubSignIn == true)
            {
                var serviceEventSet = currentEvents.Where(r => r.ParentEventId == null).ToList();

                // we need to get first two event services, and then the matching ac events - if there's
                // a combination of 1, 1AC, 2, 3, 3AC, 3 and 3 AC are not considered for checkin
                for(int i = 0; i < 2; i++)
                {
                    eligibleEvents.Add(serviceEventSet[i]);

                    if (currentEvents.Any(r => r.ParentEventId == serviceEventSet[i].EventId))
                    {
                        eligibleEvents.Add(currentEvents.First(r => r.ParentEventId == serviceEventSet[i].EventId));
                    }
                }

                return eligibleEvents;
            }

            return null;
        }

        public List<MpEventRoomDto> GetSignInEventRooms(int groupId, List<int> eventIds)
        {
            // return all event rooms which match up to groups on the events
            var eventGroups = _eventRepository.GetEventGroupsByGroupIdAndEventIds(groupId, eventIds);
            var eventRoomIds = eventGroups.Select(r => r.RoomReservationId.GetValueOrDefault()).ToList();
            var eventRooms = _roomRepository.GetEventRoomsByEventRoomIds(eventRoomIds).Where(r => r.AllowSignIn).ToList();

            return eventRooms;
        }

        // set assignments for non-ac rooms here -- this is not picking up when a partici
        public List<MpEventParticipantDto> AssignParticipantToRoomsNonAc(List<MpEventRoomDto> eventRoomDtos, List<MpEventDto> eventDtos, ParticipantDto participant)
        {
            var mpEventParticipantRecords = new List<MpEventParticipantDto>();

            // sort the events in ascending time - want to start with the first service event for this
            eventDtos = eventDtos.OrderBy(r => r.EventStartDate).ToList();

            foreach (var serviceEvent in eventDtos)
            {
                // need to make sure that there is only a single event room returned on these...
                var eventRoom = eventRoomDtos.FirstOrDefault(r => r.EventId == serviceEvent.EventId);
                if (eventRoom == null)
                {
                    var mpEventParticipantDto = TranslateParticipantDtoToMpEventParticipantDto(participant, serviceEvent.EventId, null);
                    mpEventParticipantRecords.Add(mpEventParticipantDto);
                    continue;
                }

                if (eventRoom.Capacity > (eventRoom.SignedIn + eventRoom.CheckedIn))
                {
                    // consider just passing down the event room?
                    var mpEventParticipantDto = TranslateParticipantDtoToMpEventParticipantDto(participant, eventRoom.EventId, eventRoom.RoomId);
                    mpEventParticipantRecords.Add(mpEventParticipantDto);
                }
                else
                {
                    var bumpedRoom = ProcessBumpingRules(serviceEvent.EventId, eventRoom.EventRoomId.GetValueOrDefault());

                    if (bumpedRoom != null)
                    {
                        var mpEventParticipantDto = TranslateParticipantDtoToMpEventParticipantDto(participant, serviceEvent.EventId, bumpedRoom.RoomId);
                        mpEventParticipantRecords.Add(mpEventParticipantDto);
                    }
                    // if they have no room assignment, add an unsignedin participant so that their label gets printed appropriately
                    else
                    {
                        var mpEventParticipantDto = TranslateParticipantDtoToMpEventParticipantDto(participant, serviceEvent.EventId, null);
                        mpEventParticipantRecords.Add(mpEventParticipantDto);
                    }
                }
            }

{'fraction_non_alphanumeric': 0.06595129599686413, 'fraction_numerical': 0.0018619236611298936, 'mean_word_length': 2.354149548069022, 'pattern_counts': {'":': 0, '<': 47, '<?xml version=': 0, '>': 67, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '929660', 'n_tokens_mistral': 5152, 'n_tokens_neox': 4838, 'n_words': 1319}