using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using MTWorkHR.Application.Identity;
using MTWorkHR.Application.Mapper;
using MTWorkHR.Application.Models;
using MTWorkHR.Core.Global;
using MTWorkHR.Core.IRepositories;
using MTWorkHR.Core.UnitOfWork;
using MTWorkHR.Infrastructure.Entities;
using MTWorkHR.Application.Services.Interfaces;
using MTWorkHR.Core.Email;
using MTWorkHR.Core.Entities;
using MTWorkHR.Infrastructure.UnitOfWorks;
using MTWorkHR.Core.Entities.User;
using System.Linq.Dynamic.Core;
using Microsoft.AspNetCore.Http;

namespace MTWorkHR.Application.Services
{
    public class OrderRequestService : BaseService<OrderRequest, OrderRequestDto, OrderRequestDto>, IOrderRequestService
    {
        private readonly IUnitOfWork _unitOfWork;
        private readonly IMailSender _emailSender;
        private readonly ApplicationUserManager _userManager;
        private readonly GlobalInfo _globalInfo;
        private readonly IUserService _userService;

        public OrderRequestService(IUnitOfWork unitOfWork, IMailSender emailSender, ApplicationUserManager userManager, GlobalInfo globalInfo, IUserService userService) : base(unitOfWork)
        {
            _unitOfWork = unitOfWork;
            _emailSender = emailSender;
            _userManager = userManager;
            _globalInfo = globalInfo;
            _userService = userService;
        }


        public override async Task<OrderRequestDto> GetById(long id)
        {
            var entity = await _unitOfWork.OrderRequest.GetByIdWithAllChildren(id);
            var response = MapperObject.Mapper.Map<OrderRequestDto>(entity);
            response.Employee = await _userService.GetUserWithAttachmentById(entity.RequestingEmployeeId);
            return response;
        }
      
        public  async Task<PagingResultDto<OrderRequestDto>> GetAll(OrderPagingInputDto PagingInputDto)
        {
            var res = await _unitOfWork.OrderRequest.GetAllWithChildrenAsync();
            var query = res.Item1;
            if (_globalInfo.UserType != UserTypeEnum.Business)
            {
                query = query.Where(m => m.RequestingEmployeeId != null && m.RequestingEmployeeId == _globalInfo.UserId);

            }
            if (PagingInputDto.Filter != null)
            {
                var filter = PagingInputDto.Filter;
                query = query.Where(u => u.RequestComments!.Contains(filter) 
                || u.OrderType.NameEn!.Contains(filter)
                || u.OrderType.NameAr!.Contains(filter)
                || u.LeaveType!.NameEn!.Contains(filter)
                || u.LeaveType!.NameAr!.Contains(filter));
            }
            if (PagingInputDto.SearchDate != null)
            {
                query = query.Where(u => u.StartDate.Date <= PagingInputDto.SearchDate.Value.Date && u.EndDate!.Value.Date >= PagingInputDto.SearchDate.Value.Date) ;
            }
            if (PagingInputDto.OrderTypeId != null)
            {
                query = query.Where(u => u.OrderTypeId == PagingInputDto.OrderTypeId);
            }
            if (PagingInputDto.LeaveTypeId != null)
            {
                query = query.Where(u => u.LeaveTypeId == PagingInputDto.LeaveTypeId);
            }
            if (PagingInputDto.StatusId != null)
            {
                query = query.Where(u => (long?)u.OrderStatus == PagingInputDto.StatusId);
            }
            var order = query.OrderBy(PagingInputDto.OrderByField + " " + PagingInputDto.OrderType);

            var page = order.Skip((PagingInputDto.PageNumber * PagingInputDto.PageSize) - PagingInputDto.PageSize).Take(PagingInputDto.PageSize);

            var total = await query.CountAsync();

            var list = MapperObject.Mapper
                .Map<IList<OrderRequestDto>>(await page.ToListAsync());
            foreach (var item in list)
            {
                if (item.RequestingEmployeeId != null)
                {
                    var user = await _userService.GetUserWithAttachmentById(item.RequestingEmployeeId);
                    if (user != null)
                    {
                        item.Employee = user;
                        var attach = user.UserAttachments?.FirstOrDefault(a => a.AttachmentTypeId == 9);
                        if(attach != null)
                        using (var stream = new MemoryStream(attach.Content))
                        {
                            var file = new FormFile(stream, 0, stream.Length, Path.GetFileNameWithoutExtension(attach.FileName), attach.FileName)
                            {
                                Headers = new HeaderDictionary(),
                                ContentType = attach.ContentType,
                            };

                            System.Net.Mime.ContentDisposition cd = new System.Net.Mime.ContentDisposition
                            {
                                FileName = file.FileName
                            };
                            file.ContentDisposition = cd.ToString();
                            item.Employee.ProfileImage = file;
                        }
                    }
                }
            }
            var response = new PagingResultDto<OrderRequestDto>
            {
                Result = list,
                Total = total
            };

            return response;
        }
        public override async Task<OrderRequestDto> Create(OrderRequestDto input)
        {
            var period = DateTime.Now.Year;
            if(string.IsNullOrEmpty( input.RequestingEmployeeId))
            {
                input.RequestingEmployeeId = _globalInfo.UserId;
            }
            var allocation = await _unitOfWork.OrderAllocation.GetUserAllocations(input.RequestingEmployeeId, input.OrderTypeId, input.LeaveTypeId, period);

            if (allocation is null)
            {
                throw new AppException(ExceptionEnum.NoVacationBalance, "You do not have any allocations for this leave type.");
            }
            else
            {
                int daysRequested = (int)(input.EndDate - input.StartDate).TotalDays;
                if (daysRequested > allocation.NumberOfDays)
                {
                    throw new AppException(ExceptionEnum.NoVacationBalance, "You do not have enough days for this request");
                }
            }
            var orderRequest = MapperObject.Mapper.Map<OrderRequest>(input);
            orderRequest = await _unitOfWork.OrderRequest.AddAsync(orderRequest);
            await _unitOfWork.CompleteAsync();

            try
            {
                var requestingEmployee = await _userManager.Users.FirstOrDefaultAsync(x => x.Id == input.RequestingEmployeeId);
                var sendMailResult = await _emailSender.SendEmail(new EmailMessage
                {
                    To = requestingEmployee.Email,
                    Body = $"Your leave request for {input.StartDate:D} to {input.EndDate:D} " +
                    $"has been submitted successfully.",
                    Subject = "Leave Request Submitted",
                    userId = input.RequestingEmployeeId
                });
                if (!sendMailResult)
                {
                    throw new AppException("User created, but could not send the email!");
                }
            }
            catch (Exception ex)
            {
                //// Log or handle error, but don't throw...
            }
            var response = MapperObject.Mapper.Map<OrderRequestDto>(orderRequest);
            if (response.RequestingEmployeeId != null)
            {
                var user = await _userService.GetUserWithAttachmentById(response.RequestingEmployeeId);
                if (user != null)
                {
                    response.Employee = user;
                    var attach = user.UserAttachments?.FirstOrDefault(a => a.AttachmentTypeId == 9);
                    if (attach != null)
                        using (var stream = new MemoryStream(attach.Content))
                        {
                            var file = new FormFile(stream, 0, stream.Length, Path.GetFileNameWithoutExtension(attach.FileName), attach.FileName)
                            {
                                Headers = new HeaderDictionary(),
                                ContentType = attach.ContentType,
                            };

                            System.Net.Mime.ContentDisposition cd = new System.Net.Mime.ContentDisposition
                            {
                                FileName = file.FileName
                            };
                            file.ContentDisposition = cd.ToString();
                            response.Employee.ProfileImage = file;
                        }
                }
            }
            return response;
        }


        public async Task<OrderRequestDto> ChangeStatus(long id, int statusId )
        {
            var orderRequest = await _unitOfWork.OrderRequest.GetByIdAsync(id);
            orderRequest.OrderStatus = (ApprovalStatusEnum)statusId;
            if (orderRequest.OrderStatus == ApprovalStatusEnum.Approved)
            {
                var allocation = await _unitOfWork.OrderAllocation.GetUserAllocations(orderRequest.RequestingEmployeeId, orderRequest.OrderTypeId, orderRequest.LeaveTypeId, DateTime.Now.Year);
                int daysRequested = !orderRequest.EndDate.HasValue ? 1 : (int)(orderRequest.EndDate.Value - orderRequest.StartDate).TotalDays;
                allocation.NumberOfDays -= daysRequested;
            }
            await _unitOfWork.CompleteAsync();
            var response = MapperObject.Mapper.Map<OrderRequestDto>(orderRequest);
            return response;
        }



    }
}