using Microsoft.EntityFrameworkCore;
using MTWorkHR.Application.Identity;
using MTWorkHR.Application.Mapper;
using MTWorkHR.Application.Models;
using MTWorkHR.Core.Global;
using MTWorkHR.Core.UnitOfWork;
using MTWorkHR.Application.Services.Interfaces;
using MTWorkHR.Core.Entities;
using System.Linq.Dynamic.Core;
using Microsoft.AspNetCore.Identity;
using MTWorkHR.Infrastructure.Entities;
using System.Linq;
using MTWorkHR.Core.IDto;
using MTWorkHR.Core.Entities.User;
using System.Diagnostics.Contracts;
using Contract = MTWorkHR.Core.Entities.Contract;
using MTWorkHR.Infrastructure.Reports;
using DevExpress.DataProcessing;
namespace MTWorkHR.Application.Services
{
    public class ContractService : BaseService<Contract, ContractDto, ContractDto>, IContractService
    {
        private readonly IUnitOfWork _unitOfWork;
        private readonly IUserService _userService;
        private readonly GlobalInfo _globalInfo;
        private readonly IFileService _fileService;
        private readonly IOrderAllocationService _orderAllocationService;

        public ContractService(IUnitOfWork unitOfWork, IUserService userService, GlobalInfo globalInfo, IOrderAllocationService orderAllocationService, IFileService fileService) : base(unitOfWork)
        {
            _unitOfWork = unitOfWork;
            _userService = userService;
            _globalInfo = globalInfo;
            _orderAllocationService = orderAllocationService;
            _fileService = fileService;
        }

        public override async Task<ContractDto> GetById(long id)
        {
            var entity = await _unitOfWork.Contract.GetByIdWithAllChildren(id);
            var response = MapperObject.Mapper.Map<ContractDto>(entity);
            response.WorkingDays = entity.WorkingDays != null ? entity.WorkingDays.Split(",").ToList() : null;

            return response;
        }
       
        public override async Task<ContractDto> Create(ContractDto input)
        {
            var entity = MapperObject.Mapper.Map<Contract>(input);

            if (entity is null)
            {
                throw new AppException(ExceptionEnum.MapperIssue);
            }
            entity.WorkingDays = input.WorkingDays!=null? string.Join(",", input.WorkingDays): null;
            var team = await _unitOfWork.Contract.AddAsync(entity);
            await _unitOfWork.CompleteAsync();

            var response = MapperObject.Mapper.Map<ContractDto>(team);
            return response;
        }

        //public override async Task<ContractDto> Update(ContractDto input)
        //{
        //    var entity = MapperObject.Mapper.Map<Contract>(input);

        //    if (entity is null)
        //    {
        //        throw new AppException(ExceptionEnum.MapperIssue);
        //    }

        //    var contract = await _unitOfWork.Contract.upd(entity);
        //    await _unitOfWork.CompleteAsync();


        //    var response = Mapper.MapperObject.Mapper.Map<ContractDto>(entity);
        //    return response;
        //}
       

        public async Task<PagingResultDto<ContractDto>> GetAll(ContractPagingInputDto PagingInputDto)
        {
            var res = await _unitOfWork.Contract.GetAllWithChildrenAsync();
            var query = res.Item1;

            if (_globalInfo.UserType != UserTypeEnum.Business)
            {
                // query = query.Where(m => m.ContractUsers != null && m.ContractUsers.Count > 0 && m.ContractUsers.Count(u=> u.AssignedUserId == _globalInfo.UserId) > 0);

            }
            if (PagingInputDto.Filter != null)
            {
                var filter = PagingInputDto.Filter;
                query = query.Where(u => u.JobDescription != null && u.JobDescription.Contains(filter));
            }

            if (PagingInputDto.ContractStatusId != null)
            {
                query = query.Where(u => (int)u.ContractStatusId == PagingInputDto.ContractStatusId);
            }
            if (PagingInputDto.ContractTypeId != null)
            {
                query = query.Where(u => (int)u.ContractTypeId == PagingInputDto.ContractTypeId);
            }

            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<ContractDto>>(await page.ToListAsync());

            foreach (var item in list)
            {
                if (item.UserId != null)
                {
                    var user = await _userService.GetUserWithAttachmentById(item.UserId);
                    if (user != null)
                    {
                        item.EmployeeName = user.FirstName + " " + user.LastName;
                        item.EmployeeEmail = user.Email;
                    }
                }
            }
            var response = new PagingResultDto<ContractDto>
            {
                Result = list,
                Total = total
            };

            return response;
        }

        #region HR data____________________________
        public async Task<PagingResultDto<ContractAllHRDto>> GetAllForHr(ContractPagingInputDto PagingInputDto)
        {
            var res = await _unitOfWork.Contract.GetAllWithChildrenAsync();
            var query = res.Item1;

            if (_globalInfo.UserType != UserTypeEnum.Business)
            {
                // query = query.Where(m => m.ContractUsers != null && m.ContractUsers.Count > 0 && m.ContractUsers.Count(u=> u.AssignedUserId == _globalInfo.UserId) > 0);

            }
            if (PagingInputDto.Filter != null)
            {
                var filter = PagingInputDto.Filter;
                query = query.Where(u => u.JobDescription.Contains(filter));
            }

            if (PagingInputDto.ContractStatusId != null)
            {
                query = query.Where(u => (int)u.ContractStatusId == PagingInputDto.ContractStatusId);
            }
            if (PagingInputDto.ContractTypeId != null)
            {
                query = query.Where(u => (int)u.ContractTypeId == PagingInputDto.ContractTypeId);
            }

            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<ContractAllHRDto>>(await page.ToListAsync());


            var teamUsersList = await _unitOfWork.TeamUser.GetAllWithChildrenAsync();
            var vacationAllocations = await _unitOfWork.OrderAllocation.GetAllAsync();
            foreach (var item in list)
            {
                if (item.UserId != null)
                {
                    var user = await _userService.GetUserWithAttachmentById(item.UserId);
                    if (user != null)
                    {
                        item.EmployeeName = user.FirstName + " " + user.LastName;
                        item.EmployeeEmail = user.Email;
                        var attach = user.UserAttachments?.FirstOrDefault(a => a.AttachmentTypeId == 9);
                        item.ProfileImagePath = attach?.FilePath;
                        //___________Get Teams
                        //var TeamsList = teamUsersList.Item1.Where(t => t.AssignedUserId == item.UserId).Select(t => GlobalInfo.lang == "en" ? t.Team.NameEn : t.Team.NameAr);
                        //item.Teams = TeamsList == null ? "" : string.Join(",", TeamsList);
                        var latestTeam = teamUsersList.Item1.OrderByDescending(a=> a.Id).FirstOrDefault(t => t.AssignedUserId == item.UserId);
                        item.Teams = latestTeam == null ? "" : GlobalInfo.lang == "en" ? latestTeam.Team?.NameEn : latestTeam.Team?.NameAr;
                        //_____________Get vacation balance
                        var remainVacations = vacationAllocations.Item1.FirstOrDefault(t => t.EmployeeId == item.UserId && t.ContractId == item.Id);
                        item.RestVacations = remainVacations == null ? item.VacationDays : remainVacations.NumberOfDays ;
                        item.SpentVacations = item.VacationDays - item.RestVacations;
                    }
                }
            }

            // Filter employees by name if provided
            if (!string.IsNullOrEmpty(PagingInputDto.UserName))
            {
                var filter = PagingInputDto.UserName;

                list = list.Where(u =>
                     (u.EmployeeName != null && u.EmployeeName.ToLower().Contains(filter))
                    || (u.EmployeeEmail != null && u.EmployeeEmail.ToLower().Contains(filter))
                ).ToList();
            }



            var response = new PagingResultDto<ContractAllHRDto>
            {
                Result = list,
                Total = list.Count
            };

            return response;
        }
        public async Task<ContractHRDto> GetByIdHRDetails(long contractId)
        {
            var entity = await _unitOfWork.Contract.GetByIdWithAllChildren(contractId);
            if (entity != null)
            {
                var response = MapperObject.Mapper.Map<ContractHRDto>(entity);
                response.WorkingDays = entity.WorkingDays != null ? entity.WorkingDays.Split(",").ToList() : null;
                //--------------------
                var user = await _userService.GetUserWithAttachmentById(entity.UserId);
                response.EmployeeName = user.FirstName + " " + user.LastName;
                response.EmployeeEmail = user.Email;
                var attach = user.UserAttachments?.FirstOrDefault(a => a.AttachmentTypeId == 9);
                response.ProfileImagePath = attach?.FilePath;
                //___________Get Teams
                var teamUsersList = await _unitOfWork.TeamUser.GetUserTeams(entity.UserId);
                var teamsList = MapperObject.Mapper.Map<List<TeamDto>>(teamUsersList.Item1);

                response.TeamList = teamsList;
                //_____________Get vacation balance
                var vacationAllocations = await _unitOfWork.OrderAllocation.GetUserAllocations(entity.UserId, 1, 1, entity.Id, DateTime.UtcNow.Year);
                var remainVacations = vacationAllocations;
                response.RestVacations = remainVacations == null ? entity.VacationDays : remainVacations.NumberOfDays;
                response.SpentVacations = entity.VacationDays - response.RestVacations;
                //__-----------Order Requests----
                var orderRequestsList = await _unitOfWork.OrderRequest.GetAllUserOrdersAsync(entity.UserId, contractId);
                var vacationsList = orderRequestsList.Item1.Where(o => o.OrderStatus == ApprovalStatusEnum.Approved && o.OrderTypeId == 1 && (o.LeaveTypeId == 1 || o.LeaveTypeId == 2));
                var orderList = MapperObject.Mapper.Map<List<OrderRequestHRDto>>(vacationsList);
                response.OrderList = orderList;
                /////------------------------
                var invoiceList = await _unitOfWork.Invoice.GetAllUserInvoices(contractId);
                var Invoices = MapperObject.Mapper.Map<List<InvoiceDto>>(invoiceList.Item1);

                response.InvoiceList = Invoices;

                //------------------
                if(entity.ContractTypeId == (int)ContractTypeEnum.EmployeeContract)
                {
                    var lastInvoice = Invoices.LastOrDefault();
                    var diffDate = DateTime.UtcNow.Date - entity.StartDate.Value.Date;
                    if(lastInvoice != null)
                    {
                        if (entity.BillingCycle == "Monthly")
                            response.NextSalaryDate = lastInvoice.InvoiceDate.Value.AddMonths(1);
                        else if (entity.BillingCycle == "Fortnightly")
                            response.NextSalaryDate = lastInvoice.InvoiceDate.Value.AddDays(15);
                    }
                    else
                    {
                        if (entity.BillingCycle == "Monthly")
                            response.NextSalaryDate = entity.StartDate.Value.AddMonths(1);
                        else if(entity.BillingCycle == "Fortnightly")
                            response.NextSalaryDate = entity.StartDate.Value.AddDays(15);
                    }
                }
                return response;
            }
            return new ContractHRDto();
        }
        #endregion
        public async Task<bool> ChangeStatus(long contractId, int statusId)
        {
            var entity = await _unitOfWork.Contract.GetByIdAsync(contractId);
            if (entity == null)
                throw new AppException(ExceptionEnum.RecordNotExist);
            entity.ContractStatusId = statusId;
            bool result = true;
            if (statusId == (int)ContractStatusEnum.Approved)
            {
                var updatedSuccess = await _userService.Update(entity.UserId, entity.CompanyId);
                addVacationAllocation(entity.VacationDays.HasValue ? entity.VacationDays.Value : 0, entity.UserId, contractId);
                if (!updatedSuccess) { 
                    result = false; 
                }
            }
            return result;
        }

        private async Task addVacationAllocation(int noVacationDays, string employeeId, long contractId)
        {
            try 
            { 
                await _orderAllocationService.Create(new OrderAllocationDto { ContractId = contractId, EmployeeId = employeeId, OrderTypeId = 1, LeaveTypeId = 1, NumberOfDays = noVacationDays, Period = DateTime.UtcNow.Year });
               // var orderTypes = await _unitOfWork.OrderType.GetAllAsync();
               // var leaveTypes = await _unitOfWork.LeaveType.GetAllAsync();
                //foreach (var orderType in orderTypes.Item1)
                //{
                //    foreach (var leaveType in leaveTypes.Item1)
                //    {
                //        if (orderType.Id != 1 && leaveType.Id != 1 && (leaveType.DefaultDays > 0 || orderType.DefaultDays > 0))
                //        {
                //            await _orderAllocationService.Create(new OrderAllocationDto
                //            {
                //                ContractId = contractId,
                //                EmployeeId = employeeId,
                //                OrderTypeId = (int)orderType.Id,
                //                LeaveTypeId = (int)leaveType.Id,
                //                NumberOfDays = leaveType.DefaultDays > 0 ? leaveType.DefaultDays : orderType.DefaultDays,
                //                Period = DateTime.UtcNow.Year
                //            }
                //            );
                //        }
                //    }
                //}
            }
            catch(Exception ex)
            {
                throw ;
            }
            
        }

        public async Task<ContractDetail> GetByIdReport(long id)
        {
            var entity = await _unitOfWork.Contract.GetByIdWithAllChildren(id);
            var contract = MapperObject.Mapper.Map<ContractDetail>(entity);
            contract.WorkingDays = entity.WorkingDays;//!= null ? entity.WorkingDays.Split(",").ToList() : null;
            var user = await _userService.GetUserById(entity.UserId);
            if (user != null)
            {
                contract.EmployeeName = user.FirstName + " " + user.LastName;
                contract.EmployeeEmail = user.Email;
                contract.EmployeePassport = user.PassportNumber;
                contract.EmployeePhone = user.PhoneNumber;
                contract.EmployeeUniversity = user.UniversityName;
                contract.AcademicQualification = user.QualificationName;
                contract.EmployeeJobName = user.JobTitleName;
            }
            return contract;
        }



        public async Task<AttachmentResponseDto> GenerateContractPdf(string outputPath, long contractId)
        {
            try
            {
                var entity = await _unitOfWork.Contract.GetByIdWithAllChildren(contractId);

                var contract = new ContractDetail
                {
                    StartDate = entity.StartDate,
                    BillingCycle = entity.BillingCycle,
                    ContractDuration = entity.ContractDurationId != null ? ((ContractDurationEnum)entity.ContractDurationId).ToString() : "",
                    ContractStatusId = (ContractStatusEnum)entity.ContractStatusId,
                    FirstPatchAmount = entity.FirstPatchAmount,
                    FirstPatchDateFrom = entity.FirstPatchDateFrom,
                    FirstPatchDateTo = entity.FirstPatchDateTo,
                    LastPatchAmount = entity.LastPatchAmount,
                    LastPatchDateFrom = entity.LastPatchDateFrom,
                    LastPatchDateTo = entity.LastPatchDateTo,
                    JobDescription = entity.JobDescription,
                    JobNumber = entity.JobNumber,
                    Salary = entity.Salary,
                    JobTitleName = entity.JobTitleName,//Specialization = entity.SpecializationId,
                    TrialPeriod = entity.TrialPeriod,
                    VacationDays = entity.VacationDays, //WhoCanTerminateContract = entity.WhoCanTerminateContract,TypeOfWork = entity.TypeOfWork
                                                        //WhoCanTerminateContractInTrial = 
                    WorkCountry = entity.WorkCountryId + "",
                    WorkingHours = entity.WorkingHours + "",
                    ContractTypeId = (ContractTypeEnum)entity.ContractTypeId,
                    Currency = entity.Currency,
                    FixedAllowances = entity.FixedAllowances,
                    ProjectStages = entity.ProjectStages,
                    ContractTasks = entity.ContractTasks,
                    WorkState = entity.WorkStateId + "",
                    WorkingDays = entity.WorkingDays
                };              
                
                var user = await _userService.GetUserById(entity.UserId);
                if (user != null)
                {
                    contract.EmployeeName = user.FirstName + " " + user.LastName;
                    contract.EmployeeEmail = user.Email;
                    contract.EmployeePassport = user.PassportNumber;
                    contract.EmployeePhone = user.PhoneNumber;
                    contract.EmployeeUniversity = user.UniversityName;
                    contract.AcademicQualification = user.QualificationName;
                    contract.EmployeeJobName = user.JobTitleName;
                    contract.EmployeeDateOfBirth = user.DateOfBirth;
                }
                var representativeUser = await _userService.GetUserById(entity.CompanyRepresentativeId);
                if (representativeUser != null)
                {
                    contract.CompanyRepresentativeName = representativeUser.FirstName + " " + representativeUser.LastName;
                    contract.CompanyRepresentativeEmail = representativeUser.Email;
                    contract.CompanyRepresentativePassport = representativeUser.PassportNumber;
                    contract.CompanyRepresentativePhone = representativeUser.PhoneNumber;
                    contract.CompanyRepresentativePosition = representativeUser.Position ?? "";
                }
                var company = await _unitOfWork.Company.GetByIdAsync(entity.CompanyId);
                if (company != null)
                {
                    contract.CompanyName = company.CompanyName;
                    contract.CompanyEmail = company.Email;
                    contract.CompanyPhone = company.PhoneNumber;
                    contract.CompanyCR = company.CRNumber;
                    contract.CompanyAddress = company.Address;
                }


                var pdfBytes = await new ContractPdfGenerator().GenerateContractPdf(outputPath, contract);
                // Upload to Azure Blob Storage and return the Blob URL
                string fileName = $"contract_{Guid.NewGuid()}.pdf";
                var result = await _fileService.UploadFile(pdfBytes, fileName);
                entity.FilePath = result.FilePath;
                await _unitOfWork.CompleteAsync();

                return result;
            }
            catch (Exception e)
            {
                throw e;

                //     _logger.LogError(e, "Failed to generate contract PDF for contractId {ContractId}", contractId);
                var msg = e.Message;
                //return new AttachmentResponseDto();
            }
        }
        public byte[] generatePdfTest()
        {
            var pdfBytes = new ContractPdfGenerator().GeneratePdf();
            return pdfBytes;
        }
    }
}
