using Marco.Pms.DataAccess.Data;
using Marco.Pms.Model.AttendanceModule;
using Marco.Pms.Model.Dtos.Attendance;
using Marco.Pms.Model.Employees;
using Marco.Pms.Model.Entitlements;
using Marco.Pms.Model.Mapper;
using Marco.Pms.Model.Projects;
using Marco.Pms.Model.Utilities;
using Marco.Pms.Model.ViewModels.AttendanceVM;
using Marco.Pms.Services.Hubs;
using Marco.Pms.Services.Service;
using Marco.Pms.Services.Service.ServiceInterfaces;
using MarcoBMS.Services.Helpers;
using MarcoBMS.Services.Service;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using Microsoft.CodeAnalysis;
using Microsoft.EntityFrameworkCore;
using System.Globalization;
using Document = Marco.Pms.Model.DocumentManager.Document;

namespace MarcoBMS.Services.Controllers
{
    [Authorize]
    [ApiController]
    [Route("api/[controller]")]
    public class AttendanceController : ControllerBase
    {
        private readonly ApplicationDbContext _context;
        private readonly EmployeeHelper _employeeHelper;
        private readonly IProjectServices _projectServices;
        private readonly UserHelper _userHelper;
        private readonly S3UploadService _s3Service;
        private readonly PermissionServices _permission;
        private readonly ILoggingService _logger;
        private readonly IHubContext<MarcoHub> _signalR;
        private readonly IFirebaseService _firebase;

        public AttendanceController(
             ApplicationDbContext context, EmployeeHelper employeeHelper, IProjectServices projectServices, UserHelper userHelper,
             S3UploadService s3Service, ILoggingService logger, PermissionServices permission, IHubContext<MarcoHub> signalR, IFirebaseService firebase)
        {
            _context = context;
            _employeeHelper = employeeHelper;
            _projectServices = projectServices;
            _userHelper = userHelper;
            _s3Service = s3Service;
            _logger = logger;
            _permission = permission;
            _signalR = signalR;
            _firebase = firebase;
        }

        private Guid GetTenantId()
        {
            return _userHelper.GetTenantId();
            //var tenant = User.FindFirst("TenantId")?.Value;
            //return (tenant != null ? Convert.ToInt32(tenant) : 1);
        }

        [HttpGet("log/attendance/{attendanceid}")]

        public async Task<IActionResult> GetAttendanceLogById(Guid attendanceid)
        {
            Guid TenantId = GetTenantId();

            List<AttendanceLog> lstAttendance = await _context.AttendanceLogs
                .Include(a => a.Document)
                .Include(a => a.Employee)
                .Include(a => a.UpdatedByEmployee)
                .Where(c => c.AttendanceId == attendanceid && c.TenantId == TenantId)
                .ToListAsync();

            List<AttendanceLogVM> attendanceLogVMs = new List<AttendanceLogVM>();
            foreach (var attendanceLog in lstAttendance)
            {
                string objectKey = attendanceLog.Document != null ? attendanceLog.Document.S3Key : string.Empty;
                string preSignedUrl = string.IsNullOrEmpty(objectKey) ? string.Empty : _s3Service.GeneratePreSignedUrl(objectKey);
                attendanceLogVMs.Add(attendanceLog.ToAttendanceLogVMFromAttendanceLog(preSignedUrl, preSignedUrl));
            }
            _logger.LogInfo("{count} Attendance records fetched successfully", lstAttendance.Count);
            return Ok(ApiResponse<object>.SuccessResponse(attendanceLogVMs, System.String.Format("{0} Attendance records fetched successfully", lstAttendance.Count), 200));

        }

        [HttpGet("log/employee/{employeeId}")]
        public async Task<IActionResult> GetAttendanceLogByEmployeeId(Guid employeeId, [FromQuery] string? dateFrom = null, [FromQuery] string? dateTo = null)
        {
            Guid TenantId = GetTenantId();
            DateTime fromDate = new DateTime();
            DateTime toDate = new DateTime();

            if (dateFrom != null && DateTime.TryParse(dateFrom, out fromDate) == false)
            {
                _logger.LogWarning("User sent Invalid from Date while featching attendance logs");
                return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Date", "Invalid Date", 400));
            }
            if (dateTo != null && DateTime.TryParse(dateTo, out toDate) == false)
            {
                _logger.LogWarning("User sent Invalid to Date while featching attendance logs");
                return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Date", "Invalid Date", 400));
            }

            if (employeeId == Guid.Empty)
            {
                _logger.LogWarning("The employee Id sent by user is empty");
                return BadRequest(ApiResponse<object>.ErrorResponse("Employee ID is required and must not be Empty.", "Employee ID is required and must not be empty.", 400));
            }
            List<Attendance> attendances = await _context.Attendes.Where(c => c.EmployeeID == employeeId && c.TenantId == TenantId && c.AttendanceDate.Date >= fromDate && c.AttendanceDate.Date <= toDate).ToListAsync();
            Employee? employee = await _context.Employees.Include(e => e.JobRole).FirstOrDefaultAsync(e => e.Id == employeeId && e.TenantId == TenantId && e.IsActive);
            List<EmployeeAttendanceVM> results = new List<EmployeeAttendanceVM>();

            if (employee != null)
            {
                foreach (var attendance in attendances)
                {
                    EmployeeAttendanceVM result = new EmployeeAttendanceVM
                    {
                        Id = attendance.Id,
                        EmployeeId = employee.Id,
                        FirstName = employee.FirstName,
                        LastName = employee.LastName,
                        CheckInTime = attendance.InTime,
                        CheckOutTime = attendance.OutTime,
                        JobRoleName = employee.JobRole != null ? employee.JobRole.Name : "",
                        Activity = attendance.Activity,
                        EmployeeAvatar = null
                    };
                    results.Add(result);
                }
            }

            _logger.LogInfo("{count} Attendance records fetched successfully", results.Count);
            return Ok(ApiResponse<object>.SuccessResponse(results, System.String.Format("{0} Attendance records fetched successfully", results.Count), 200));


        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="projectId">ProjectID</param>
        /// <param name="date">YYYY-MM-dd</param>
        /// <returns></returns>
        [HttpGet("project/log")]

        public async Task<IActionResult> EmployeeAttendanceByDateRange([FromQuery] Guid projectId, [FromQuery] string? dateFrom = null, [FromQuery] string? dateTo = null)
        {
            Guid TenantId = GetTenantId();
            var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
            var hasTeamAttendancePermission = await _permission.HasPermission(PermissionsMaster.TeamAttendance, LoggedInEmployee.Id);
            var hasSelfAttendancePermission = await _permission.HasPermission(PermissionsMaster.SelfAttendance, LoggedInEmployee.Id);
            var hasProjectPermission = await _permission.HasProjectPermission(LoggedInEmployee, projectId);

            if (!hasProjectPermission)
            {
                _logger.LogWarning("Employee {EmployeeId} tries to access attendance of project {ProjectId}, but don't have access", LoggedInEmployee.Id, projectId);
                return Unauthorized(ApiResponse<object>.ErrorResponse("Unauthorized access", "Unauthorized access", 404));
            }

            DateTime fromDate = new DateTime();
            DateTime toDate = new DateTime();

            if (dateFrom != null && DateTime.TryParse(dateFrom, out fromDate) == false)
            {
                _logger.LogWarning("User sent Invalid fromDate while featching attendance logs");
                return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Date", "Invalid Date", 400));
            }
            if (dateTo != null && DateTime.TryParse(dateTo, out toDate) == false)
            {
                _logger.LogWarning("User sent Invalid toDate while featching attendance logs");
                return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Date", "Invalid Date", 400));
            }

            if (projectId == Guid.Empty)
            {
                _logger.LogWarning("The project Id sent by user is less than or equal to zero");
                return BadRequest(ApiResponse<object>.ErrorResponse("Project ID is required and must be greater than zero.", "Project ID is required and must be greater than zero.", 400));
            }

            var result = new List<EmployeeAttendanceVM>();
            //Attendance? attendance = null;
            ProjectAllocation? teamMember = null;

            if (dateFrom == null) fromDate = DateTime.UtcNow.Date;
            if (dateTo == null && dateFrom != null) toDate = fromDate.AddDays(-1);

            if (hasTeamAttendancePermission)
            {
                List<Attendance> lstAttendance = await _context.Attendes.Where(c => c.ProjectID == projectId && c.AttendanceDate.Date >= fromDate.Date && c.AttendanceDate.Date <= toDate.Date && c.TenantId == TenantId).ToListAsync();


                List<ProjectAllocation> projectteam = await _projectServices.GetTeamByProject(TenantId, projectId, true);
                var jobRole = await _context.JobRoles.ToListAsync();
                foreach (Attendance? attendance in lstAttendance)
                {
                    var result1 = new EmployeeAttendanceVM()
                    {
                        Id = attendance.Id,
                        CheckInTime = attendance.InTime,
                        CheckOutTime = attendance.OutTime,
                        Activity = attendance.Activity
                    };
                    teamMember = projectteam.Find(x => x.EmployeeId == attendance.EmployeeID);
                    if (teamMember != null)
                    {
                        result1.EmployeeAvatar = null;
                        result1.EmployeeId = teamMember.EmployeeId;
                        if (teamMember.Employee != null)
                        {
                            result1.FirstName = teamMember.Employee.FirstName;
                            result1.LastName = teamMember.Employee.LastName;
                            result1.JobRoleName = teamMember.Employee.JobRole != null ? teamMember.Employee.JobRole.Name : null;
                        }
                        else
                        {
                            result1.FirstName = null;
                            result1.LastName = null;
                            result1.JobRoleName = null;
                        }

                        result.Add(result1);
                    }

                }
            }
            else if (hasSelfAttendancePermission)
            {
                List<Attendance> lstAttendances = await _context.Attendes.Where(c => c.ProjectID == projectId && c.EmployeeID == LoggedInEmployee.Id && c.AttendanceDate.Date >= fromDate.Date && c.AttendanceDate.Date <= toDate.Date && c.TenantId == TenantId).ToListAsync();
                ProjectAllocation? projectAllocation = await _context.ProjectAllocations.Include(pa => pa.Employee).FirstOrDefaultAsync(pa => pa.ProjectId == projectId && pa.EmployeeId == LoggedInEmployee.Id && pa.TenantId == TenantId && pa.IsActive);
                foreach (var attendance in lstAttendances)
                {
                    if (projectAllocation != null)
                    {
                        EmployeeAttendanceVM result1 = new EmployeeAttendanceVM
                        {
                            Id = attendance.Id,
                            EmployeeAvatar = null,
                            EmployeeId = projectAllocation.EmployeeId,
                            FirstName = projectAllocation.Employee?.FirstName,
                            LastName = projectAllocation.Employee?.LastName,
                            JobRoleName = projectAllocation.Employee?.JobRole?.Name,
                            CheckInTime = attendance.InTime,
                            CheckOutTime = attendance.OutTime,
                            Activity = attendance.Activity
                        };
                        result.Add(result1);
                    }
                }
            }
            _logger.LogInfo("{count} Attendance records fetched successfully", result.Count);
            return Ok(ApiResponse<object>.SuccessResponse(result, System.String.Format("{0} Attendance records fetched successfully", result.Count), 200));
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="projectId">ProjectID</param>
        /// <param name="date">YYYY-MM-dd</param>
        /// <returns></returns>
        [HttpGet("project/team")]
        public async Task<IActionResult> EmployeeAttendanceByProject([FromQuery] Guid projectId, [FromQuery] bool IncludeInActive, [FromQuery] string? date = null)
        {
            Guid TenantId = GetTenantId();
            var LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
            var hasTeamAttendancePermission = await _permission.HasPermission(PermissionsMaster.TeamAttendance, LoggedInEmployee.Id);
            var hasSelfAttendancePermission = await _permission.HasPermission(PermissionsMaster.SelfAttendance, LoggedInEmployee.Id);
            var hasProjectPermission = await _permission.HasProjectPermission(LoggedInEmployee, projectId);

            if (!hasProjectPermission)
            {
                _logger.LogWarning("Employee {EmployeeId} tries to access attendance of project {ProjectId}, but don't have access", LoggedInEmployee.Id, projectId);
                return Unauthorized(ApiResponse<object>.ErrorResponse("Unauthorized access", "Unauthorized access", 404));
            }

            DateTime forDate = new DateTime();

            if (date != null && DateTime.TryParse(date, out forDate) == false)
            {
                _logger.LogWarning("User sent Invalid Date while featching attendance logs");
                return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Date", "Invalid Date", 400));

            }
            if (projectId == Guid.Empty)
            {
                _logger.LogWarning("The project Id sent by user is less than or equal to zero");
                return BadRequest(ApiResponse<object>.ErrorResponse("Project ID is required and must be greater than zero.", "Project ID is required and must be greater than zero.", 400));
            }

            var result = new List<EmployeeAttendanceVM>();
            Attendance? attendance = null;

            if (date == null) forDate = DateTime.UtcNow.Date;
            if (hasTeamAttendancePermission)
            {
                List<Attendance> lstAttendance = await _context.Attendes.Where(c => c.ProjectID == projectId && c.AttendanceDate.Date == forDate && c.TenantId == TenantId).ToListAsync();


                List<ProjectAllocation> projectteam = await _projectServices.GetTeamByProject(TenantId, projectId, IncludeInActive);
                var idList = projectteam.Select(p => p.EmployeeId).ToList();
                //var emp = await _context.Employees.Where(e => idList.Contains(e.Id)).Include(e => e.JobRole).ToListAsync();
                var jobRole = await _context.JobRoles.ToListAsync();

                foreach (ProjectAllocation teamMember in projectteam)
                {
                    if (teamMember.Employee != null && teamMember.Employee.JobRole != null)
                    {
                        var result1 = new EmployeeAttendanceVM()
                        {
                            EmployeeAvatar = null,
                            EmployeeId = teamMember.EmployeeId,
                            FirstName = teamMember.Employee.FirstName,
                            LastName = teamMember.Employee.LastName,
                            JobRoleName = teamMember.Employee.JobRole.Name,
                        };

                        //var member = emp.Where(e => e.Id == teamMember.EmployeeId);


                        attendance = lstAttendance.Find(x => x.EmployeeID == teamMember.EmployeeId) ?? new Attendance();
                        if (attendance != null)
                        {
                            result1.Id = attendance.Id;
                            result1.CheckInTime = attendance.InTime;
                            result1.CheckOutTime = attendance.OutTime;
                            result1.Activity = attendance.Activity;
                        }

                        result.Add(result1);
                    }
                }

                result.Sort(delegate (EmployeeAttendanceVM x, EmployeeAttendanceVM y)
                {
                    //return x.FirstName.CompareTo(y.FirstName);
                    return string.Compare(x.FirstName, y.FirstName, StringComparison.Ordinal);
                });
            }
            else if (hasSelfAttendancePermission)
            {
                Attendance lstAttendance = await _context.Attendes.FirstOrDefaultAsync(c => c.ProjectID == projectId && c.EmployeeID == LoggedInEmployee.Id && c.AttendanceDate.Date == forDate && c.TenantId == TenantId) ?? new Attendance();
                ProjectAllocation? projectAllocation = await _context.ProjectAllocations.Include(pa => pa.Employee).FirstOrDefaultAsync(pa => pa.ProjectId == projectId && pa.EmployeeId == LoggedInEmployee.Id && pa.TenantId == TenantId && pa.IsActive);
                if (projectAllocation != null)
                {
                    EmployeeAttendanceVM result1 = new EmployeeAttendanceVM
                    {
                        Id = lstAttendance.Id,
                        EmployeeAvatar = null,
                        EmployeeId = projectAllocation.EmployeeId,
                        FirstName = projectAllocation.Employee?.FirstName,
                        LastName = projectAllocation.Employee?.LastName,
                        JobRoleName = projectAllocation.Employee?.JobRole?.Name,
                        CheckInTime = lstAttendance.InTime,
                        CheckOutTime = lstAttendance.OutTime,
                        Activity = lstAttendance.Activity
                    };
                    result.Add(result1);
                }
            }
            _logger.LogInfo("{count} Attendance records fetched successfully", result.Count);

            return Ok(ApiResponse<object>.SuccessResponse(result, System.String.Format("{0} Attendance records fetched successfully", result.Count), 200));
        }

        [HttpGet("regularize")]
        public async Task<IActionResult> GetRequestRegularizeAttendance([FromQuery] Guid projectId, [FromQuery] bool IncludeInActive)
        {
            Guid TenantId = GetTenantId();
            Employee LoggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
            var result = new List<EmployeeAttendanceVM>();
            var hasProjectPermission = await _permission.HasProjectPermission(LoggedInEmployee, projectId);

            if (!hasProjectPermission)
            {
                _logger.LogWarning("Employee {EmployeeId} tries to access attendance of project {ProjectId}, but don't have access", LoggedInEmployee.Id, projectId);
                return Unauthorized(ApiResponse<object>.ErrorResponse("Unauthorized access", "Unauthorized access", 404));
            }

            List<Attendance> lstAttendance = await _context.Attendes.Where(c => c.ProjectID == projectId && c.Activity == ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE && c.TenantId == TenantId).ToListAsync();

            List<ProjectAllocation> projectteam = await _projectServices.GetTeamByProject(TenantId, projectId, true);
            var idList = projectteam.Select(p => p.EmployeeId).ToList();
            var jobRole = await _context.JobRoles.ToListAsync();

            foreach (Attendance attende in lstAttendance)
            {
                var result1 = new EmployeeAttendanceVM()
                {
                    Id = attende.Id,
                    CheckInTime = attende.InTime,
                    CheckOutTime = attende.OutTime,
                    Activity = attende.Activity,
                    EmployeeAvatar = null,
                    EmployeeId = attende.EmployeeID,

                };

                var teamMember = projectteam.Find(m => m.EmployeeId == attende.EmployeeID);
                if (teamMember != null && teamMember.Employee != null && teamMember.Employee.JobRole != null)
                {
                    result1.FirstName = teamMember.Employee.FirstName;
                    result1.LastName = teamMember.Employee.LastName;
                    result1.JobRoleName = teamMember.Employee.JobRole.Name;
                }

                result.Add(result1);
            }

            result.Sort(delegate (EmployeeAttendanceVM x, EmployeeAttendanceVM y)
            {
                return string.Compare(x.FirstName, y.FirstName, StringComparison.Ordinal);
            });
            _logger.LogInfo("{count} Attendance records fetched successfully", result.Count);
            return Ok(ApiResponse<object>.SuccessResponse(result, System.String.Format("{0} Attendance records fetched successfully", result.Count), 200));
        }


        [HttpPost]
        [Route("record")]
        public async Task<IActionResult> RecordAttendance([FromBody] RecordAttendanceDot recordAttendanceDot)
        {
            if (!ModelState.IsValid)
            {
                var errors = ModelState.Values
                    .SelectMany(v => v.Errors)
                    .Select(e => e.ErrorMessage)
                    .ToList();
                _logger.LogWarning("User sent Invalid Date while marking attendance \n {Error}", string.Join(",", errors));
                return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
            }

            Guid TenantId = GetTenantId();
            var currentEmployee = await _userHelper.GetCurrentEmployeeAsync();

            using var transaction = await _context.Database.BeginTransactionAsync();
            try
            {
                Attendance? attendance = await _context.Attendes.FirstOrDefaultAsync(a => a.Id == recordAttendanceDot.Id && a.TenantId == TenantId); ;

                if (recordAttendanceDot.MarkTime == null)
                {
                    _logger.LogWarning("User sent Invalid Mark Time while marking attendance");
                    return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Mark Time", "Invalid Mark Time", 400));
                }

                DateTime finalDateTime = GetDateFromTimeStamp(recordAttendanceDot.Date, recordAttendanceDot.MarkTime);
                if (recordAttendanceDot.Comment == null)
                {
                    _logger.LogWarning("User sent Invalid comment while marking attendance");
                    return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Comment", "Invalid Comment", 400));
                }

                if (attendance != null)
                {
                    attendance.Comment = recordAttendanceDot.Comment;
                    if (recordAttendanceDot.Action == ATTENDANCE_MARK_TYPE.CHECK_IN)
                    {
                        attendance.InTime = finalDateTime;
                        attendance.OutTime = null;
                        attendance.Activity = ATTENDANCE_MARK_TYPE.CHECK_OUT;
                    }
                    else if (recordAttendanceDot.Action == ATTENDANCE_MARK_TYPE.CHECK_OUT)
                    {
                        attendance.IsApproved = true;
                        attendance.Activity = ATTENDANCE_MARK_TYPE.REGULARIZE;


                        //string timeString = "10:30 PM"; // Format: "hh:mm tt"

                        attendance.OutTime = finalDateTime;
                    }
                    else if (recordAttendanceDot.Action == ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE)
                    {
                        DateTime date = attendance.AttendanceDate;
                        finalDateTime = GetDateFromTimeStamp(date.Date, recordAttendanceDot.MarkTime);
                        if (attendance.InTime < finalDateTime)
                        {
                            attendance.OutTime = finalDateTime;
                            attendance.Activity = ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE;
                        }
                        else
                        {
                            _logger.LogWarning("Employee {EmployeeId} sent regularization request but it check-out time is earlier than check-out");
                            return BadRequest(ApiResponse<object>.ErrorResponse("Check-out time must be later than check-in time", "Check-out time must be later than check-in time", 400));
                        }
                        // do nothing
                    }
                    else if (recordAttendanceDot.Action == ATTENDANCE_MARK_TYPE.REGULARIZE)
                    {
                        attendance.IsApproved = true;
                        attendance.Activity = ATTENDANCE_MARK_TYPE.REGULARIZE;
                        attendance.ApprovedBy = currentEmployee.Id;
                        // do nothing
                    }
                    else if (recordAttendanceDot.Action == ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT)
                    {
                        attendance.IsApproved = false;
                        attendance.Activity = ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT;
                        // do nothing
                    }
                    attendance.Date = DateTime.UtcNow;

                    // update code
                    _context.Attendes.Update(attendance);
                }
                else
                {
                    attendance = new Attendance();
                    attendance.TenantId = TenantId;
                    attendance.AttendanceDate = recordAttendanceDot.Date;
                    // attendance.Activity = recordAttendanceDot.Action;
                    attendance.Comment = recordAttendanceDot.Comment;
                    attendance.EmployeeID = recordAttendanceDot.EmployeeID;
                    attendance.ProjectID = recordAttendanceDot.ProjectID;
                    attendance.Date = DateTime.UtcNow;



                    attendance.InTime = finalDateTime;
                    attendance.OutTime = null;
                    attendance.Activity = ATTENDANCE_MARK_TYPE.CHECK_OUT;

                    _context.Attendes.Add(attendance);
                }

                await _context.SaveChangesAsync();


                // Step 3: Always insert a new log entry
                var attendanceLog = new AttendanceLog
                {
                    AttendanceId = attendance.Id, // Use existing or new AttendanceId
                    Activity = attendance.Activity,

                    ActivityTime = finalDateTime,
                    Comment = recordAttendanceDot.Comment,
                    EmployeeID = recordAttendanceDot.EmployeeID,
                    Latitude = recordAttendanceDot.Latitude,
                    Longitude = recordAttendanceDot.Longitude,

                    TenantId = TenantId,
                    UpdatedBy = currentEmployee.Id,
                    UpdatedOn = recordAttendanceDot.Date
                };
                //if (recordAttendanceDot.Image != null && recordAttendanceDot.Image.Count > 0)
                //{
                //    attendanceLog.Photo = recordAttendanceDot.Image[0].Base64Data;
                //}


                _context.AttendanceLogs.Add(attendanceLog);
                await _context.SaveChangesAsync();

                await transaction.CommitAsync(); // Commit transaction

                Employee employee = await _employeeHelper.GetEmployeeByID(recordAttendanceDot.EmployeeID);
                if (employee.JobRole != null)
                {
                    EmployeeAttendanceVM vm = new EmployeeAttendanceVM()
                    {
                        CheckInTime = attendance.InTime,
                        CheckOutTime = attendance.OutTime,
                        EmployeeAvatar = null,
                        EmployeeId = recordAttendanceDot.EmployeeID,
                        FirstName = employee.FirstName,
                        LastName = employee.LastName,
                        Id = attendance.Id,
                        Activity = attendance.Activity,
                        JobRoleName = employee.JobRole.Name
                    };
                    var sendActivity = 0;
                    if (recordAttendanceDot.Id == Guid.Empty)
                    {
                        sendActivity = 1;
                    }
                    var notification = new { LoggedInUserId = currentEmployee.Id, Keyword = "Attendance", Activity = sendActivity, ProjectId = attendance.ProjectID, Response = vm };
                    await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification);
                    _logger.LogInfo("Attendance for employee {FirstName} {LastName} has been marked", employee.FirstName ?? string.Empty, employee.LastName ?? string.Empty);

                    _ = Task.Run(async () =>
                    {
                        // --- Push Notification Section ---
                        // This section attempts to send a test push notification to the user's device.
                        // It's designed to fail gracefully and handle invalid Firebase Cloud Messaging (FCM) tokens.

                        var name = $"{vm.FirstName} {vm.LastName}";

                        await _firebase.SendAttendanceMessageAsync(attendance.ProjectID, name, recordAttendanceDot.Action, attendance.EmployeeID, TenantId);

                    });

                    return Ok(ApiResponse<object>.SuccessResponse(vm, "Attendance marked successfully.", 200));
                }
                _logger.LogInfo("Attendance for employee {FirstName} {LastName} has been marked", employee.FirstName ?? string.Empty, employee.LastName ?? string.Empty);
                return Ok(ApiResponse<object>.SuccessResponse(new EmployeeAttendanceVM(), "Attendance marked successfully.", 200));

            }
            catch (Exception ex)
            {
                await transaction.RollbackAsync(); // Rollback on failure
                _logger.LogError(ex, "An Error occured while marking attendance");
                var response = new
                {
                    message = ex.Message,
                    detail = ex.StackTrace,
                    statusCode = StatusCodes.Status500InternalServerError
                };
                return BadRequest(ApiResponse<object>.ErrorResponse(ex.Message, response, 400));
            }

        }

        [HttpPost]
        [Route("record-image")]
        public async Task<IActionResult> RecordAttendanceWithImage([FromBody] RecordAttendanceDot recordAttendanceDot)
        {
            if (!ModelState.IsValid)
            {
                var errors = ModelState.Values.SelectMany(v => v.Errors).Select(e => e.ErrorMessage).ToList();
                _logger.LogWarning("Invalid attendance model received. \n {Error}", string.Join(",", errors));
                return BadRequest(ApiResponse<object>.ErrorResponse("Invalid data", errors, 400));
            }

            Guid tenantId = GetTenantId();
            var loggedInEmployee = await _userHelper.GetCurrentEmployeeAsync();
            var batchId = Guid.NewGuid();

            using var transaction = await _context.Database.BeginTransactionAsync();
            try
            {
                // Validate mark time
                if (recordAttendanceDot.MarkTime == null)
                {
                    _logger.LogWarning("Null mark time provided.");
                    return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Mark Time", "Mark time is required", 400));
                }

                if (string.IsNullOrWhiteSpace(recordAttendanceDot.Comment))
                {
                    _logger.LogWarning("Empty comment provided.");
                    return BadRequest(ApiResponse<object>.ErrorResponse("Invalid Comment", "Comment is required", 400));
                }

                var finalDateTime = GetDateFromTimeStamp(recordAttendanceDot.Date, recordAttendanceDot.MarkTime);
                var attendance = await _context.Attendes
                    .FirstOrDefaultAsync(a => a.Id == recordAttendanceDot.Id && a.TenantId == tenantId);

                // Create or update attendance
                if (attendance == null)
                {
                    attendance = new Attendance
                    {
                        TenantId = tenantId,
                        AttendanceDate = recordAttendanceDot.Date,
                        Comment = recordAttendanceDot.Comment,
                        EmployeeID = recordAttendanceDot.EmployeeID,
                        ProjectID = recordAttendanceDot.ProjectID,
                        Date = DateTime.UtcNow,
                        InTime = finalDateTime,
                        Activity = ATTENDANCE_MARK_TYPE.CHECK_OUT
                    };
                    _context.Attendes.Add(attendance);
                }
                else
                {
                    attendance.Comment = recordAttendanceDot.Comment;
                    attendance.Date = DateTime.UtcNow;

                    switch (recordAttendanceDot.Action)
                    {
                        case ATTENDANCE_MARK_TYPE.CHECK_IN:
                            attendance.InTime = finalDateTime;
                            attendance.OutTime = null;
                            attendance.Activity = ATTENDANCE_MARK_TYPE.CHECK_OUT;
                            break;
                        case ATTENDANCE_MARK_TYPE.CHECK_OUT:
                            attendance.OutTime = finalDateTime;
                            attendance.Activity = ATTENDANCE_MARK_TYPE.REGULARIZE;
                            attendance.IsApproved = true;
                            break;
                        case ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE:
                            DateTime date = attendance.InTime ?? recordAttendanceDot.Date;
                            finalDateTime = GetDateFromTimeStamp(date.Date, recordAttendanceDot.MarkTime);
                            if (attendance.InTime < finalDateTime)
                            {
                                attendance.OutTime = finalDateTime;
                                attendance.Activity = ATTENDANCE_MARK_TYPE.REQUEST_REGULARIZE;
                            }
                            else
                            {
                                _logger.LogWarning("Regularization check-out time is before check-in.");
                                return BadRequest(ApiResponse<object>.ErrorResponse("Check-out time must be later than check-in time", "Invalid regularization", 400));
                            }
                            break;
                        case ATTENDANCE_MARK_TYPE.REGULARIZE:
                            attendance.IsApproved = true;
                            attendance.Activity = ATTENDANCE_MARK_TYPE.REGULARIZE;
                            break;
                        case ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT:
                            attendance.IsApproved = false;
                            attendance.Activity = ATTENDANCE_MARK_TYPE.REGULARIZE_REJECT;
                            break;
                    }

                    _context.Attendes.Update(attendance);
                }

                // Upload image if present
                Document? document = null;
                string? preSignedUrl = null;

                if (recordAttendanceDot.Image != null && recordAttendanceDot.Image.ContentType != null)
                {
                    string base64 = recordAttendanceDot.Image.Base64Data?.Split(',').LastOrDefault() ?? "";
                    if (string.IsNullOrWhiteSpace(base64))
                        return BadRequest(ApiResponse<object>.ErrorResponse("Base64 data is missing", "Image data missing", 400));

                    var fileType = _s3Service.GetContentTypeFromBase64(base64);
                    var fileName = _s3Service.GenerateFileName(fileType, tenantId, "attendance");
                    var objectKey = $"tenant-{tenantId}/Employee/{recordAttendanceDot.EmployeeID}/Attendance/{fileName}";

                    await _s3Service.UploadFileAsync(base64, fileType, objectKey);
                    preSignedUrl = _s3Service.GeneratePreSignedUrl(objectKey);

                    document = new Document
                    {
                        BatchId = batchId,
                        UploadedById = loggedInEmployee.Id,
                        FileName = recordAttendanceDot.Image.FileName ?? "",
                        ContentType = recordAttendanceDot.Image.ContentType,
                        S3Key = objectKey,
                        //Base64Data = recordAttendanceDot.Image.Base64Data,
                        FileSize = recordAttendanceDot.Image.FileSize,
                        UploadedAt = recordAttendanceDot.Date,
                        TenantId = tenantId
                    };

                    _context.Documents.Add(document);
                }

                // Log attendance
                var attendanceLog = new AttendanceLog
                {
                    AttendanceId = attendance.Id,
                    Activity = attendance.Activity,
                    ActivityTime = finalDateTime,
                    Comment = recordAttendanceDot.Comment,
                    EmployeeID = recordAttendanceDot.EmployeeID,
                    Latitude = recordAttendanceDot.Latitude,
                    Longitude = recordAttendanceDot.Longitude,
                    DocumentId = document?.Id,
                    TenantId = tenantId,
                    UpdatedBy = loggedInEmployee.Id,
                    UpdatedOn = recordAttendanceDot.Date
                };
                _context.AttendanceLogs.Add(attendanceLog);

                await _context.SaveChangesAsync();
                await transaction.CommitAsync();

                // Construct view model
                var employee = await _employeeHelper.GetEmployeeByID(recordAttendanceDot.EmployeeID);
                var vm = new EmployeeAttendanceVM
                {
                    Id = attendance.Id,
                    EmployeeId = employee.Id,
                    FirstName = employee.FirstName,
                    LastName = employee.LastName,
                    CheckInTime = attendance.InTime,
                    CheckOutTime = attendance.OutTime,
                    Activity = attendance.Activity,
                    JobRoleName = employee.JobRole?.Name,
                    DocumentId = document?.Id ?? Guid.Empty,
                    ThumbPreSignedUrl = preSignedUrl ?? "",
                    PreSignedUrl = preSignedUrl ?? ""
                };

                var notification = new
                {
                    LoggedInUserId = loggedInEmployee.Id,
                    Keyword = "Attendance",
                    Activity = recordAttendanceDot.Id == Guid.Empty ? 1 : 0,
                    ProjectId = attendance.ProjectID,
                    Response = vm
                };

                await _signalR.Clients.All.SendAsync("NotificationEventHandler", notification);

                _ = Task.Run(async () =>
                {
                    // --- Push Notification Section ---
                    // This section attempts to send a test push notification to the user's device.
                    // It's designed to fail gracefully and handle invalid Firebase Cloud Messaging (FCM) tokens.

                    var name = $"{vm.FirstName} {vm.LastName}";

                    await _firebase.SendAttendanceMessageAsync(attendance.ProjectID, name, recordAttendanceDot.Action, attendance.EmployeeID, tenantId);

                });

                _logger.LogInfo("Attendance recorded for employee: {FullName}", $"{employee.FirstName} {employee.LastName}");
                return Ok(ApiResponse<object>.SuccessResponse(vm, "Attendance marked successfully.", 200));
            }
            catch (Exception ex)
            {
                await transaction.RollbackAsync();
                _logger.LogError(ex, "Error while recording attendance");
                return BadRequest(ApiResponse<object>.ErrorResponse("Something went wrong", ex.Message, 500));
            }
        }

        private static DateTime GetDateFromTimeStamp(DateTime date, string timeString)
        {
            //DateTime date = recordAttendanceDot.Date;



            // Parse time string to TimeSpan
            DateTime parsedTime = DateTime.ParseExact(timeString, "hh:mm tt", CultureInfo.InvariantCulture);

            // Combine date with time
            DateTime finalDateTime = new DateTime(date.Year, date.Month, date.Day, parsedTime.Hour, parsedTime.Minute, 0);
            return finalDateTime;
        }
    }
}
