<commit_before>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Countersoft.Foundation.Commons.Extensions;
using Countersoft.Gemini.Commons;
using Countersoft.Gemini.Commons.Dto;
using Countersoft.Gemini.Commons.Entity;
using Countersoft.Gemini.Commons.Permissions;
using Countersoft.Gemini.Commons.System;
using Countersoft.Gemini.Contracts;
using Countersoft.Gemini.Contracts.Caching;
using Countersoft.Gemini.Contracts.Business;
using Countersoft.Gemini.Infrastructure.Helpers;
using Countersoft.Gemini.Infrastructure.Managers;
using Countersoft.Gemini.Infrastructure.TimerJobs;
using Countersoft.Gemini.Mailer;
using Microsoft.Practices.Unity;
using Countersoft.Gemini.Extensibility.Apps;
using Countersoft.Gemini;
using Countersoft.Gemini.Commons.Entity.Security;

namespace EmailAlerts
{
    [AppType(AppTypeEnum.Timer), 
    AppGuid("840D6D86-C74A-43D2-8D3A-3CF9985DB1D4"),
    AppName("Email Notification Engine"), 
    AppDescription("Sends email notifications to users")]
    public class EmailNotificationEngine : TimerJob
    {
        private List<AlertTemplate> _templates;

        private List<IssueTypeDto> _types;

        private List<PermissionSetDto> _permissionSets;

        private List<Organization> _organizations;

        private IssueManager _issueManager;

        public override bool Run(IssueManager issueManager)
        {
            if (!issueManager.UserContext.Config.EmailAlertsEnabled) return true;

            issueManager.UserContext.User.Entity = new User(issueManager.UserContext.User.Entity);

            issueManager.UserContext.User.Entity.Language = "en-US";

            _issueManager = issueManager;

            _templates = GeminiApp.Container.Resolve<IAlertTemplates>().FindWhere(c => c.AlertType != AlertTemplateType.Breeze).ToList();

            _types = new MetaManager(issueManager).TypeGetAll();

            _permissionSets = new PermissionSetManager(issueManager).GetAll();

            _organizations = new OrganizationManager(issueManager).GetAll();

            ProcessAppNavCardAlerts();

            ProcessWatcherAlerts();
            
            return true;
        }

        private List<UserDto> GetCardSubscribers(NavigationCard card, NavigationCardsManager cardManager, UserManager userManager, UserDto owner)
        {
            if (!owner.Entity.Active) return new List<UserDto>();

            Dictionary<int, UserDto> subscribers = new Dictionary<int, UserDto>();

            subscribers.Add(owner.Entity.Id, owner);

            foreach (var user in card.CardData.Subscription.Users)
            {
                var userDto = userManager.Get(user);

                if(user != owner.Entity.Id && userDto != null && userDto.Entity.Active) subscribers.Add(user, userDto);
            }

            var groupUsers = cardManager.GetUsersFromGroups(card, card.CardData.Subscription.Groups);

            foreach (var user in groupUsers)
            {
                if (!subscribers.ContainsKey(user.Entity.Id)) subscribers.Add(user.Entity.Id, user);
            }
            
            return new List<UserDto>(subscribers.Values);
        }

        private void ProcessAppNavCardAlerts()
        {
            var navigationCardsManager = new NavigationCardsManager(_issueManager);

            List<NavigationCard> cards = navigationCardsManager.GetPendingAlertCards();

            LogDebugMessage("Email templates loaded: " + _templates.Count);

            LogDebugMessage("Pending card alerts: " + cards.Count);

            // ? We need to store user id and issue id for every email we send out -- avoids dupes?
            List<string> issuesEmailedToUsers = new List<string>(50);

            List<string> individualIssuesEmailedToUsers = new List<string>(50);

            AlertsTemplateHelper alerts = new AlertsTemplateHelper(_templates, GetUrl(_issueManager));

            UserManager userManager = new UserManager(_issueManager);

            bool refreshCache = false;

            var allOptOuts = navigationCardsManager.GetOptOuts();

            foreach (NavigationCard card in cards)
            {
                List<IssueDto> individualIssues = new List<IssueDto>();

                // Safety checks
                if (!card.UserId.HasValue) continue;

                if (card.CardData.Alerts.Count == 0) continue;

                UserDto recepient = userManager.Get(card.UserId.Value);

                // Safety check
                if (!recepient.Entity.EmailMe || recepient.Entity.Email.IsEmpty()) continue;

                DateTime lastChecked = card.CardData.AlertsLastSent.HasValue ? card.CardData.AlertsLastSent.Value : card.Created;

                DateTime lastCheckedLocal = lastChecked.ToLocal(_issueManager.UserContext.User.TimeZone);

                AlertTypeAppNavAlertsTemplateModel model = new AlertTypeAppNavAlertsTemplateModel();

                model.TheRecipient = recepient;

                model.Version = GeminiVersion.Version;

                model.GeminiUrl = alerts.GeminiUrl;

                List<int> issuesToAlert = new List<int>(card.CardData.Alerts);

                foreach (int issueId in issuesToAlert)
                {
                    IssueDto issue = _issueManager.Get(issueId);
                    
                    // Safety check
                    if (issue == null || issue.Entity.IsNew) continue;

                    // Dupe check
                    string dupeKey = string.Format("{0}-{1}-{2}", recepient.Entity.Id, issueId, card.Id);

                    if (issuesEmailedToUsers.Contains(dupeKey)) continue;

                    var permissionManager = new PermissionsManager(recepient, _types, _permissionSets, _organizations, _issueManager.UserContext.Config.HelpDeskModeGroup, false);

                    if (!permissionManager.CanSeeItem(issue.Project, issue)) continue;

                    foreach (var comment in issue.Comments)
                    {
                        if (!permissionManager.CanSeeComment(issue, comment))
                        {
                            issue.Comments.RemoveAll(c => !permissionManager.CanSeeComment(issue, c));
                            break;
                        }
                    }

                    // Remove the reported by first entry!
                    if(issue.History.Count > 0 && issue.History[issue.History.Count-1].Entity.AttributeChanged == ItemAttributeVisibility.ReportedBy) issue.History.RemoveAt(issue.History.Count -1); 
                    issue.ChangeLog = _issueManager.GetChangeLog(issue, _issueManager.UserContext.User, recepient, lastCheckedLocal);

                    // Populate model for email template<commit_msg>Don't crash the whole app if we can't get an issue.<commit_after>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Countersoft.Foundation.Commons.Extensions;
using Countersoft.Gemini.Commons;
using Countersoft.Gemini.Commons.Dto;
using Countersoft.Gemini.Commons.Entity;
using Countersoft.Gemini.Commons.Permissions;
using Countersoft.Gemini.Commons.System;
using Countersoft.Gemini.Contracts;
using Countersoft.Gemini.Contracts.Caching;
using Countersoft.Gemini.Contracts.Business;
using Countersoft.Gemini.Infrastructure.Helpers;
using Countersoft.Gemini.Infrastructure.Managers;
using Countersoft.Gemini.Infrastructure.TimerJobs;
using Countersoft.Gemini.Mailer;
using Microsoft.Practices.Unity;
using Countersoft.Gemini.Extensibility.Apps;
using Countersoft.Gemini;
using Countersoft.Gemini.Commons.Entity.Security;

namespace EmailAlerts
{
    [AppType(AppTypeEnum.Timer), 
    AppGuid("840D6D86-C74A-43D2-8D3A-3CF9985DB1D4"),
    AppName("Email Notification Engine"), 
    AppDescription("Sends email notifications to users")]
    public class EmailNotificationEngine : TimerJob
    {
        private List<AlertTemplate> _templates;

        private List<IssueTypeDto> _types;

        private List<PermissionSetDto> _permissionSets;

        private List<Organization> _organizations;

        private IssueManager _issueManager;

        public override bool Run(IssueManager issueManager)
        {
            if (!issueManager.UserContext.Config.EmailAlertsEnabled) return true;

            issueManager.UserContext.User.Entity = new User(issueManager.UserContext.User.Entity);

            issueManager.UserContext.User.Entity.Language = "en-US";

            _issueManager = issueManager;

            _templates = GeminiApp.Container.Resolve<IAlertTemplates>().FindWhere(c => c.AlertType != AlertTemplateType.Breeze).ToList();

            _types = new MetaManager(issueManager).TypeGetAll();

            _permissionSets = new PermissionSetManager(issueManager).GetAll();

            _organizations = new OrganizationManager(issueManager).GetAll();

            ProcessAppNavCardAlerts();

            ProcessWatcherAlerts();
            
            return true;
        }

        private List<UserDto> GetCardSubscribers(NavigationCard card, NavigationCardsManager cardManager, UserManager userManager, UserDto owner)
        {
            if (!owner.Entity.Active) return new List<UserDto>();

            Dictionary<int, UserDto> subscribers = new Dictionary<int, UserDto>();

            subscribers.Add(owner.Entity.Id, owner);

            foreach (var user in card.CardData.Subscription.Users)
            {
                var userDto = userManager.Get(user);

                if(user != owner.Entity.Id && userDto != null && userDto.Entity.Active) subscribers.Add(user, userDto);
            }

            var groupUsers = cardManager.GetUsersFromGroups(card, card.CardData.Subscription.Groups);

            foreach (var user in groupUsers)
            {
                if (!subscribers.ContainsKey(user.Entity.Id)) subscribers.Add(user.Entity.Id, user);
            }
            
            return new List<UserDto>(subscribers.Values);
        }

        private void ProcessAppNavCardAlerts()
        {
            var navigationCardsManager = new NavigationCardsManager(_issueManager);

            List<NavigationCard> cards = navigationCardsManager.GetPendingAlertCards();

            LogDebugMessage("Email templates loaded: " + _templates.Count);

            LogDebugMessage("Pending card alerts: " + cards.Count);

            // ? We need to store user id and issue id for every email we send out -- avoids dupes?
            List<string> issuesEmailedToUsers = new List<string>(50);

            List<string> individualIssuesEmailedToUsers = new List<string>(50);

            AlertsTemplateHelper alerts = new AlertsTemplateHelper(_templates, GetUrl(_issueManager));

            UserManager userManager = new UserManager(_issueManager);

            bool refreshCache = false;

            var allOptOuts = navigationCardsManager.GetOptOuts();

            foreach (NavigationCard card in cards)
            {
                List<IssueDto> individualIssues = new List<IssueDto>();

                // Safety checks
                if (!card.UserId.HasValue) continue;

                if (card.CardData.Alerts.Count == 0) continue;

                UserDto recepient = userManager.Get(card.UserId.Value);

                // Safety check
                if (!recepient.Entity.EmailMe || recepient.Entity.Email.IsEmpty()) continue;

                DateTime lastChecked = card.CardData.AlertsLastSent.HasValue ? card.CardData.AlertsLastSent.Value : card.Created;

                DateTime lastCheckedLocal = lastChecked.ToLocal(_issueManager.UserContext.User.TimeZone);

                AlertTypeAppNavAlertsTemplateModel model = new AlertTypeAppNavAlertsTemplateModel();

                model.TheRecipient = recepient;

                model.Version = GeminiVersion.Version;

                model.GeminiUrl = alerts.GeminiUrl;

                List<int> issuesToAlert = new List<int>(card.CardData.Alerts);

                foreach (int issueId in issuesToAlert)
                {
                    IssueDto issue = null;
                    try
                    {
                        issue = _issueManager.Get(issueId);
                    }
                    catch (Exception ex)
                    {
                        LogException(ex);
                    }
                    
                    // Safety check
                    if (issue == null || issue.Entity.IsNew) continue;

                    // Dupe check
                    string dupeKey = string.Format("{0}-{1}-{2}", recepient.Entity.Id, issueId, card.Id);

                    if (issuesEmailedToUsers.Contains(dupeKey)) continue;

                    var permissionManager = new PermissionsManager(recepient, _types, _permissionSets, _organizations, _issueManager.UserContext.Config.HelpDeskModeGroup, false);

                    if (!permissionManager.CanSeeItem(issue.Project, issue)) continue;

                    foreach (var comment in issue.Comments)
                    {
                        if (!permissionManager.CanSeeComment(issue, comment))
                        {
                            issue.Comments.RemoveAll(c => !permissionManager.CanSeeComment(issue, c));
                            break;
                        }
                    }

                    // Remove the reported by first entry!
                    if(issue.History.Count > 0 && issue.History[issue.History.Count-1].Entity.AttributeChanged == ItemAttributeVisibility.ReportedBy) issue.History.RemoveAt(issue.History.Count -1); 
                    issue.ChangeLog = _issueManager.GetChangeLog(issue, _issueManager.UserContext.User, recepient, lastCheckedLocal);

                    // Populate model for email template
{'fraction_non_alphanumeric': 0.08788650418333939, 'fraction_numerical': 0.004510731174972717, 'mean_word_length': 2.293243890752276, 'pattern_counts': {'":': 0, '<': 41, '<?xml version=': 0, '>': 47, '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': '4594479', 'n_tokens_mistral': 3698, 'n_tokens_neox': 3447, 'n_words': 696}