/// <summary>
        /// Lists Active Reports from context. Each Report must not be Pending or Deleted. Each Report's Industry must not be Deleted.
        /// /// </summary>
        /// <param name="sort">The property to sort the Reports by. Can be left null for default sorting by ID.</param>
        /// <param name="search">Searches the Report List by Name and Summary. Can be left null to display all Reports.</param>
        /// <param name="author">Filters the Report List by matching the author's Email address.</param>
        /// <param name="industry">Filters the Report List by Industry.</param>
        /// <param name="tag">Filters the Report List by Tag.</param>
        /// <returns>ICollection of Report Models</returns>
        public async Task <ICollection <ReportModel> > GetReports(string sort, string search, string author, string industry, string tag)
        {
            var reports = await _context.Reports
                          .Where(r => !r.IsDeleted)
                          .Where(r => !r.IsPending)
                          .Include(r => r.Industry)
                          .Where(r => !r.Industry.IsDeleted)
                          .Include(r => r.Author)
                          .Include(r => r.Downloads)
                          .Include(r => r.ReportTags)
                          .ThenInclude(rt => rt.Tag)
                          .Select(r => ReportMapper.MapModelFromEntity(r))
                          .ToListAsync();

            //Sort Reports
            reports = SortReports(sort, reports).ToList();

            //Search Reports
            reports = SearchReports(search, reports).ToList();

            //Filter Reports
            if (author != null)
            {
                reports = reports.Where(r => r.Author.ToLower().Contains(author.ToLower())).ToList();
            }
            if (industry != null)
            {
                reports = reports.Where(r => r.Industry.ToLower().Contains(industry.ToLower())).ToList();
            }
            if (tag != null)
            {
                reports = reports.Where(r => string.Join(' ', r.Tags).ToLower().Contains(tag.ToLower())).ToList();
            }
            return(reports);
        }
        /// <summary>
        /// Gets all the Downloaded Reports for a User.
        /// </summary>
        /// <param name="userId">The ID of the target User.</param>
        /// <param name="search">Searches the Reports by Title and Summary.</param>
        /// <returns>ICollection of Report Models.</returns>
        public async Task <ICollection <ReportModel> > GetDownloadedReports(int userId, string search)
        {
            var user = await _context.Users.FirstOrDefaultAsync(u => u.Id == userId);

            ValidateUserExists(user);

            var reports = await _context.DownloadedReports
                          .Include(ur => ur.Report)
                          .ThenInclude(r => r.Author)
                          .Include(ur => ur.Report)
                          .ThenInclude(r => r.Industry)
                          .Include(ur => ur.Report)
                          .ThenInclude(r => r.ReportTags)
                          .ThenInclude(r => r.Tag)
                          .Include(ur => ur.Report)
                          .ThenInclude(r => r.Downloads)
                          .Where(ur => !ur.Report.IsDeleted)
                          .Where(ur => ur.UserId == userId)
                          .Select(ur => ReportMapper.MapModelFromEntity(ur.Report))
                          .ToListAsync();

            reports = SearchReports(search, reports).ToList();

            return(reports);
        }
        /// <summary>
        /// Updates the properties of an existing Report, then moves it to the Pending list.
        /// </summary>
        /// <param name="id">The ID for the target Report.</param>
        /// <param name="title">The new Title of the Report.</param>
        /// <param name="summary">The new Summary of the Report.</param>
        /// <param name="description">The new Description of the Report.</param>
        /// <param name="imgUrl">The path to the new Image to be displayed for the Report.</param>
        /// <param name="industry">The new Industry of the Report.</param>
        /// <param name="tags">The new list of Tags for the Report. New Tags will be created if any do not exist.</param>
        /// <returns>A Report Model on success. </returns>
        public async Task <ReportModel> UpdateReport(int id, string title, string summary, string description, string imgUrl, string industry, string tags)
        {
            //Throw if report with new Title already exists.
            if (await _context.Reports.AnyAsync(r => r.Title == title && r.Id != id))
            {
                throw new ArgumentException($"Report with title {title} already exists.");
            }
            var report = await _context.Reports
                         .Include(r => r.Industry)
                         .Include(r => r.Author)
                         .Include(r => r.ReportTags)
                         .ThenInclude(rt => rt.Tag)
                         .FirstOrDefaultAsync(r => r.Id == id);

            //Throw if ID is invalid.
            ValidateReportExists(report);


            var reportModel = ReportMapper.MapModelFromInput(title, summary, description, imgUrl, null, industry, tags);

            //Map Title
            if (title != null && title != string.Empty)
            {
                report.Title = reportModel.Title;
            }
            //Map Summary
            if (summary != null && summary != string.Empty)
            {
                report.Summary = reportModel.Summary;
            }
            //Map Description
            if (description != null && description != string.Empty)
            {
                report.Description = reportModel.Description;
            }
            //Map Image
            if (imgUrl != null && imgUrl != string.Empty)
            {
                report.ImgUrl = reportModel.ImgUrl;
            }
            //Map Industry
            report.Industry = await _context.Industries.FirstOrDefaultAsync(i => i.Name == reportModel.Industry);

            //Set ModifiedOn
            report.ModifiedOn = DateTime.UtcNow;
            report.ReportTags.Clear();
            //Set Pending
            report.IsPending = true;
            await _context.SaveChangesAsync();

            //Map Tags, Add new Tags to Context if tags are not found.
            await AddTagsToReport(report, reportModel.Tags);

            await _context.SaveChangesAsync();

            //Return Report Model
            reportModel = ReportMapper.MapModelFromEntity(report);
            return(reportModel);
        }
        /// <summary>
        /// Gets a report from the context by its ID.
        /// </summary>
        /// <param name="id">The ID for the target Report.</param>
        /// <returns>On success - a Report Model. Throws ArgumentNullException if ID is invalid.</returns>
        public async Task <ReportModel> GetReport(int id)
        {
            var report = await _context.Reports
                         .Include(r => r.Industry)
                         .Include(r => r.Author)
                         .Include(r => r.ReportTags)
                         .ThenInclude(rt => rt.Tag)
                         .FirstOrDefaultAsync(r => r.Id == id);

            ValidateReportExists(report);
            var reportDTO = ReportMapper.MapModelFromEntity(report);

            return(reportDTO);
        }
        /// <summary>
        /// Lists the 4 Most Downloaded Reports in the context.
        /// </summary>
        /// <returns>ICollection of Report Models</returns>
        public async Task <ICollection <ReportModel> > GetMostDownloadedReports()
        {
            var reports = await _context.Reports
                          .Where(r => !r.IsDeleted)
                          .Where(r => !r.IsPending)
                          .Include(r => r.Industry)
                          .Include(r => r.Author)
                          .Include(r => r.Downloads)
                          .Include(r => r.ReportTags)
                          .ThenInclude(rt => rt.Tag)
                          .OrderByDescending(r => r.Downloads.Count)
                          .Take(4)
                          .Select(r => ReportMapper.MapModelFromEntity(r))
                          .ToListAsync();

            return(reports);
        }
        /// <summary>
        /// Lists all Pending Reports
        /// </summary>
        /// <param name="sort">The property to sort the Reports by. Can be left null for default sorting by ID.</param>
        /// <param name="search">Searches the Report List by Name and Summary. Can be left null to display all Reports.</param>
        /// <returns>ICollection of Report Models</returns>
        public async Task <ICollection <ReportModel> > GetPendingReports(string sort, string search)
        {
            var reports = await _context.Reports
                          .Where(r => r.IsPending)
                          .Include(r => r.Industry)
                          .Include(r => r.Author)
                          .Include(r => r.ReportTags)
                          .ThenInclude(rt => rt.Tag)
                          .Select(r => ReportMapper.MapModelFromEntity(r))
                          .ToListAsync();

            reports = SortReports(sort, reports).ToList();

            reports = SearchReports(search, reports).ToList();

            return(reports);
        }
        /// <summary>
        /// Creates a new Report from given Strings and places it in Pending.
        /// </summary>
        /// <param name="title">The title of the new Report. Between 5 and 100 characters.</param>
        /// <param name="summary">The summary of the new Report. Between 5 and 300 characters.</param>
        /// <param name="description">The description of the new Report. Between 5 and 5000 characters.</param>
        /// <param name="author">The author of the new Report. Automatically generated from Identity and the logged in User.</param>
        /// <param name="imgUrl">The URL for the report's image, which appears on the report's card.</param>
        /// <param name="industry">The Industry under which the new Report will be classified. Has to match an existing Industry in the context.</param>
        /// <param name="tags">The Tags to be added to the new Report. If a tag does not exist, it will be created automatically.</param>
        /// <returns>On success - A Report Model, mapped from the new Report. If the Report already exists - Throws Argument Exception</returns>
        public async Task <ReportModel> CreateReport(string title, string summary, string description, string author, string imgUrl, string industry, string tags)
        {
            var reportModel = ReportMapper.MapModelFromInput(title, summary, description, imgUrl, author, industry, tags);

            // Throw if Report with this title exists.
            if (await _context.Reports
                .Include(r => r.Author)
                .Include(r => r.Industry)
                .Include(r => r.ReportTags)
                .AnyAsync(r => r.Title == title))
            {
                throw new ArgumentException($"Report with title {title} already exists.");
            }

            //Create Report
            var report = new Report()
            {
                Title       = reportModel.Title,
                Description = reportModel.Description,
                Summary     = reportModel.Summary,
                ImgUrl      = reportModel.ImgUrl,
                CreatedOn   = DateTime.UtcNow
            };
            await _context.Reports.AddAsync(report);

            //Map Author, Industry, set Pending
            report.AuthorId   = _context.Users.FirstOrDefault(u => u.NormalizedEmail == reportModel.Author.ToUpper()).Id;
            report.IndustryId = _context.Industries.FirstOrDefault(i => i.Name.ToUpper() == reportModel.Industry.ToUpper()).Id;
            report.IsPending  = true;
            await _context.SaveChangesAsync();

            //Map Tags, Create new Tags if any do not exist.
            await AddTagsToReport(report, reportModel.Tags);

            await _context.SaveChangesAsync();

            //Return Report Model
            reportModel = ReportMapper.MapModelFromEntity(report);
            return(reportModel);
        }