using System.Text.RegularExpressions;

namespace Fast.Web.Controllers
{
    [Authorize]
    [ApiController]
    [Route("api/[controller]")]
    public class NotificationController : ODataController
    {
        private readonly MyDbContext db;
        private readonly AuthorizationHelper authHelper;

        public NotificationController(MyDbContext context)
        {
            db = context;
            authHelper = new AuthorizationHelper(Main.IsAuthenticationEnabled);
        }

        [Permission("Notifications", PermissionType.View)]
        [Route("/odata/GetNotificationItems")]
        public IActionResult GetItems(ODataQueryOptions<NotificationListItem> queryOptions, bool isExport)
        {
            queryOptions = Util.GetQueryOptionsWithConvertedDates(queryOptions);
            var itemsQueryable = GetItemsInternal();
            var items = (queryOptions.ApplyTo(itemsQueryable) as IEnumerable<NotificationListItem>)?.ToList();

            if (isExport)
                return File(Util.Excel.GetExportFileStream(items), "application/octet-stream");
            else
                return Ok(items);
        }

        [Permission("Notifications", PermissionType.View)]
        [Route("[action]")]
        public IQueryable<NotificationListItem> GetItemsInternal()
        {
            IQueryable<NotificationListItem>? itemsQueryable = null;

            itemsQueryable = (
                from q in db.Notifications
                select new NotificationListItem
                {
                    NotificationId = q.Id,
                    NotificationName = q.Name,
                    FromAddress = q.FromAddress ?? "",
                    ToAddresses = q.ToAddresses ?? "",
                    FromName = q.FromName ?? "",
                    Subject = q.Subject ?? ""
                }
            ).AsNoTracking();

            return itemsQueryable;
        }

        [Permission("Notifications", PermissionType.View)]
        [Route("[action]")]
        public async Task<IActionResult> GetRequiredData()
        {
            var hasModifyPermission = await authHelper.IsAuthorizedAsync(User, "Notifications", PermissionType.Modify);

            var result = new { hasModifyPermission };
            return Ok(result);
        }

        [Permission("Notifications", PermissionType.View)]
        [Route("[action]")]
        public async Task<IActionResult> GetDetail(int id)
        {
            var detail = await (
                from q in db.Notifications
                where q.Id == id
                select new NotificationDetail
                {
                    NotificationId = q.Id,
                    FromName = q.FromName ?? "",
                    FromAddress = q.FromAddress ?? "",
                    ToAddresses = q.ToAddresses ?? "",
                    Subject = q.Subject ?? "",
                    Body = q.Body ?? ""
                }
            ).AsNoTracking().FirstAsync();

            return Ok(detail);
        }

        [Permission("Notifications", PermissionType.Modify)]
        [Route("[action]")]
        public async Task<IActionResult> SaveDetail(NotificationDetail detail)
        {
            int resultId = 0;
            await db.Database.CreateExecutionStrategy().Execute(async () =>
            {
                using var dbContextTransaction = await db.Database.BeginTransactionAsync();
                Notification? dbItem = null;

                dbItem = await (
                    from q in db.Notifications
                    where q.Id == detail.NotificationId
                    select q
                ).FirstOrDefaultAsync();


                if (dbItem == null) //if the item does not exist then add it
                {
                    dbItem = new Notification();
                    db.Notifications.Add(dbItem);
                }
                else
                {
                    //remove existing items so that they get completely re-inserted
                }

                var d = detail;
                dbItem.FromName = d.FromName;
                dbItem.FromAddress = GetValidatedEmails(d.FromAddress);
                dbItem.ToAddresses = GetValidatedEmails(d.ToAddresses);
                dbItem.Subject = d.Subject;
                dbItem.Body = d.Body;

                await db.SaveChangesAsync();
                resultId = dbItem.Id;

                await dbContextTransaction.CommitAsync();
            });

            return Ok(resultId);
        }

        [Permission("Notifications", PermissionType.View)]
        private static string GetValidatedEmails(string emails)
        {
            string validatedEmailsResult;
            bool hasInvalidEmail = false;

            try
            {
                List<string> validEmails = new();

                var toAddressesList = emails.Split(",").ToList();

                foreach (string address in toAddressesList)
                {
                    if (!string.IsNullOrWhiteSpace(address))
                    {
                        if (address.Contains('@'))
                        {
                            string validEmail = Regex.Replace(address, @"\s", "");
                            validEmails.Add(validEmail);
                        }
                        else
                        {
                            hasInvalidEmail = true;
                        }
                    }
                }

                if (hasInvalidEmail)
                    throw new Exception();

                validatedEmailsResult = string.Join(", ", validEmails);
            }
            catch (Exception)
            {
                throw new Exception("There are one or more invalid email addresses");
            }

            return validatedEmailsResult;
        }

    }
}
