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 System.Linq.Dynamic.Core;
using System.Linq;
using Microsoft.AspNetCore.Http;
using MTWorkHR.Core.Entities.Base;

namespace MTWorkHR.Application.Services
{
    public class AttendanceService : BaseService<Attendance, AttendanceDto, AttendanceDto>, IAttendanceService
    {
        private readonly IUnitOfWork _unitOfWork;
        private readonly GlobalInfo _globalInfo;
        private readonly IUserService _userService;

        public AttendanceService(IUnitOfWork unitOfWork, GlobalInfo globalInfo, IUserService userService) :base(unitOfWork)
        {
            _unitOfWork = unitOfWork;
            _globalInfo = globalInfo;
            _userService = userService;
        }
        public async Task<PagingResultDto<AttendanceDto>> GetAll(AttendancePagingInputDto pagingInputDto)
        {
            // Fetch all attendances with related data
            var attendanceResult = await _unitOfWork.Attendance.GetAllWithChildrenAsync();
            var query = attendanceResult.Item1.AsQueryable();

            // Apply filters based on user type and input
            if (_globalInfo.UserType != UserTypeEnum.Business)
            {
                query = query.Where(m => m.UserId == _globalInfo.UserId);
            }

            if (!string.IsNullOrEmpty(pagingInputDto.Filter))
            {
                query = query.Where(u => u.LeaveReason!.Contains(pagingInputDto.Filter) || u.UserName!.Contains(pagingInputDto.Filter));
            }

            if (pagingInputDto.SearchDate != null)
            {
                query = query.Where(u => u.AttendanceDate.Date == pagingInputDto.SearchDate.Value.Date);
            }

            if (!string.IsNullOrEmpty(pagingInputDto.UserId))
            {
                query = query.Where(u => u.UserId == pagingInputDto.UserId);
            }

            // Apply sorting
            var orderedQuery = query.OrderBy($"{pagingInputDto.OrderByField} {pagingInputDto.OrderType}");

            // Apply paging
            var pagedQuery = orderedQuery
                .Skip((pagingInputDto.PageNumber - 1) * pagingInputDto.PageSize)
                .Take(pagingInputDto.PageSize);

            // Fetch total count for pagination
            var totalCount = await query.CountAsync();

            // Map attendance data to DTOs
            var attendanceDtos = MapperObject.Mapper.Map<IList<AttendanceDto>>(await pagedQuery.ToListAsync());


            // Fetch all company employees
            var employeesList = await _userService.GetAllCompanyEmployees();

            // Filter employees by name if provided
            if (!string.IsNullOrEmpty(pagingInputDto.UserName))
            {
                var filter = pagingInputDto.UserName;
                var filters = filter.Split(" ");
                var firstNameFilter = filters[0];
                var lastNameFilter = filters.Length > 1 ? filters[1] : "";

                employeesList = employeesList.Where(u =>
                    u.UserName.Contains(filter) || u.Email.Contains(filter) || u.FavoriteName.Contains(filter) ||
                    u.FirstName.Contains(filter) || u.LastName.Contains(filter) ||
                    (u.FirstName.Contains(firstNameFilter) && (string.IsNullOrEmpty(lastNameFilter) || u.LastName.Contains(lastNameFilter))) ||
                    (u.LastName.Contains(lastNameFilter) && (string.IsNullOrEmpty(firstNameFilter) || u.FirstName.Contains(firstNameFilter)))
                ).ToList();
            }

            // Create a dictionary to store user data
            var userDataDictionary = new Dictionary<string, UserBasicInfoDto>();

            // Fetch user data for all employees
            foreach (var employee in employeesList)
            {
                var user = await _userService.GetUserWithAttachmentById(employee.Id);
                if (user != null)
                {
                    var userDto = MapperObject.Mapper.Map<UserBasicInfoDto>(user);
                    userDto.ProfileImage = await GetProfileImageAsync(user.UserAttachments);
                    userDataDictionary[employee.Id] = userDto;
                }
            }

            // Combine attendance data with user data
            var result = new List<AttendanceDto>();
            if (string.IsNullOrEmpty(pagingInputDto.UserId))
            {
                foreach (var employee in employeesList)
                {
                    var latestAttendance = attendanceDtos.LastOrDefault(a => a.UserId == employee.Id);
                    if (latestAttendance != null)
                    {
                        latestAttendance.Employee = userDataDictionary[employee.Id];
                    }
                    else
                    {
                        latestAttendance = new AttendanceDto
                        {
                            UserId = employee.Id,
                            UserName = employee.UserName,
                            Employee = userDataDictionary[employee.Id]
                        };
                    }
                    result.Add(latestAttendance);
                }
            }
            else
            {
                foreach (var attendance in attendanceDtos)
                {
                    attendance.Employee = userDataDictionary[pagingInputDto.UserId];
                }
                result = attendanceDtos.ToList();
            }

            // Return the paged result
            return new PagingResultDto<AttendanceDto>
            {
                Result = result,
                Total = string.IsNullOrEmpty(pagingInputDto.UserId) ? result.Count : totalCount
            };
        }

        // Helper method to get profile image
        private async Task<FormFile> GetProfileImageAsync(ICollection<AttachmentDto> attachments)
        {
            var profileImage = attachments?.FirstOrDefault(a => a.AttachmentTypeId == 9);
            if (profileImage == null) return null;

            using (var stream = new MemoryStream(profileImage.Content))
            {
                var file = new FormFile(stream, 0, stream.Length, Path.GetFileNameWithoutExtension(profileImage.FileName), profileImage.FilePath)
                {
                    Headers = new HeaderDictionary(),
                    ContentType = profileImage.ContentType,
                    ContentDisposition = new System.Net.Mime.ContentDisposition
                    {
                        FileName = profileImage.FileName
                    }.ToString()
                };
                return file;
            }
        }

        public override async Task<AttendanceDto> Create(AttendanceDto input)
        {
            if (input.CheckInTime.HasValue)
                input.AttendanceDate = input.CheckInTime.Value.Date;
            var oldEntity = await _unitOfWork.Attendance.GetAttendanceByUserId(input.UserId, input.AttendanceDate);
            if (oldEntity is null)
            {
                var entitiy = MapperObject.Mapper.Map<Attendance>(input);
                if (entitiy is null)
                    throw new AppException(ExceptionEnum.MapperIssue);

                var newEntity = await _unitOfWork.Attendance.AddAsync(entitiy);
                var Success = await _unitOfWork.CompleteAsync();

                var response = Mapper.MapperObject.Mapper.Map<AttendanceDto>(newEntity);
                return response;
            }
            else
            {
                throw new AppException(ExceptionEnum.UserAlreadyCheckedIn);
                var response = Mapper.MapperObject.Mapper.Map<AttendanceDto>(oldEntity);
                return response;
            }

        }

        public override async Task<AttendanceDto> Update(AttendanceDto input)
        {
            Attendance? entity = null;
            if (input.CheckInTime.HasValue)
                input.AttendanceDate = input.CheckInTime.Value.Date;
            else if (input.CheckOutTime.HasValue)
                input.AttendanceDate = input.CheckOutTime.Value.Date;
            if (input.Id > 0)
                entity = await _unitOfWork.Attendance.GetByIdAsync(input.Id);
            else
                entity = await _unitOfWork.Attendance.GetAttendanceByUserId(input.UserId, input.AttendanceDate);
            if (entity is null)
                throw new AppException(ExceptionEnum.RecordNotExist);
            entity.CheckOutTime = input.CheckOutTime;
            entity.LeaveType = input.LeaveType;
            entity.LeaveReason = input.LeaveReason;
          
            await _unitOfWork.CompleteAsync();

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

        public async Task<AttendanceDto?> GetAttendanceByUserId(string userId, DateTime attendanceDate)
        {
            var entity = await _unitOfWork.Attendance.GetAttendanceByUserId(userId, attendanceDate);
            if (entity is null)
                return null;
            var response = Mapper.MapperObject.Mapper.Map<AttendanceDto>(entity);
            return response;
        }

    }
}