using ClosedXML.Excel;
using HospitalManagementSystem.Dto;
using HospitalManagementSystem.GlobalEnum;
using HospitalManagementSystem.Patients.Dto;
using HospitalManagementSystem.Permissions;
using Microsoft.AspNetCore.Authorization;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Repositories;


namespace HospitalManagementSystem.Patients
{
    public class PatientAppService : ApplicationService
    {
        private IRepository<PatientRecord, Guid> _patientrecordRepository;
        private IRepository<Patient, Guid> _patientRepository;

        public PatientAppService(IRepository<PatientRecord, Guid> patientrecordRepository, IRepository<Patient, Guid> patientRepository)
        {
            _patientrecordRepository = patientrecordRepository;
            _patientRepository = patientRepository;
        }

        #region PatientRecord

        #region Get Patient List with Paging and Searching
        [Authorize(HospitalManagementSystemPermissions.Patient.Default)]
        public async Task<PagedResultDto<PatientRecordDto>> GetPatientRecordListAsync(PagingSortPatientResultDto input, Guid Id)
        {
            //List<PatientRecord>? query;

            //query = _patientrecordRepository.GetQueryableAsync().Result
            //    .Include(x => x.Patients)
            //    .WhereIf(!String.IsNullOrEmpty(input.Search), x => x.Patients.Name.ToLower().Contains(input.Search.ToLower()))
            //    .Where(x => x.Patients.Id == Id)
            //    .OrderBy(input.Sorting ?? (nameof(PatientRecord.Id) + " asc"))
            //    .Skip(input.SkipCount)
            //    .Take(input.MaxResultCount)
            //    .ToList();

            //var totalCount = await _patientrecordRepository.CountAsync();
            //return new PagedResultDto<PatientRecordDto>(
            //    totalCount,
            //    ObjectMapper.Map<List<PatientRecord>, List<PatientRecordDto>>(query)
            //);
            var queryable = await _patientrecordRepository.GetQueryableAsync();

            var filteredQuery = queryable
                .Include(x => x.Patients)
                .WhereIf(!string.IsNullOrEmpty(input.Search), x => x.Patients.Name.ToLower().Contains(input.Search.ToLower()))
                .Where(x => x.Patients.Id == Id);

            // Get total count after filtering
            var totalCount = await filteredQuery.CountAsync();

            // Apply sorting dynamically (ensure input.Sorting is valid)
            filteredQuery = !string.IsNullOrEmpty(input.Sorting)
                ? filteredQuery.OrderBy(input.Sorting)
                : filteredQuery.OrderBy(x => x.Id);

            // Apply paging
            var pagedQuery = await filteredQuery
                .Skip(input.SkipCount)
                .Take(input.MaxResultCount)
                .ToListAsync();

            return new PagedResultDto<PatientRecordDto>(
                totalCount,
                ObjectMapper.Map<List<PatientRecord>, List<PatientRecordDto>>(pagedQuery)
            );

        }
        #endregion

        #region Get Single Patient by Id
        [Authorize(HospitalManagementSystemPermissions.Patient.Default)]
        public async Task<PatientRecordDto> GetPatientRecordByIdAsync(Guid id)
        {
            var patient = await _patientrecordRepository.GetAsync(id);
            return ObjectMapper.Map<PatientRecord, PatientRecordDto>(patient);
        }
        #endregion

        #region Export Patient Data to Excel
        public async Task<FileDownloadDto> GetExportPatientRecordAsync()
        {
            var patients = await _patientrecordRepository.GetListAsync();

            var folderPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "exports");
            if (!Directory.Exists(folderPath))
            {
                Directory.CreateDirectory(folderPath); // Ensure the folder exists
            }

            var filename = "Patients_" + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".xlsx";
            var filePath = Path.Combine(folderPath, filename);

            // Create a workbook and worksheet
            using (var workbook = new XLWorkbook())
            {
                var worksheet = workbook.Worksheets.Add("Patients");

                // Add headers
                worksheet.Cell(1, 1).Value = "Full Name";
                worksheet.Cell(1, 2).Value = "Gender";
                worksheet.Cell(1, 3).Value = "Date of Admission";
                worksheet.Cell(1, 4).Value = "Diagnosis";
                worksheet.Cell(1, 5).Value = "Next Follow-Up";
                worksheet.Cell(1, 6).Value = "Status";

                for (int i = 0; i < patients.Count; i++)
                {
                    worksheet.Cell(i + 2, 1).Value = patients[i].Patients.Name;
                    worksheet.Cell(i + 2, 2).Value = patients[i].Patients.Gender.ToString();
                    worksheet.Cell(i + 2, 3).Value = patients[i].DateOfAdmission.ToShortDateString();
                    worksheet.Cell(i + 2, 4).Value = patients[i].Diagnosis;
                    worksheet.Cell(i + 2, 5).Value = patients[i].NextFollowUp?.ToShortDateString();
                    worksheet.Cell(i + 2, 6).Value = patients[i].Status.ToString();
                }

                worksheet.Columns().AdjustToContents();
                workbook.SaveAs(filePath);
            }

            byte[] fileBytes = await File.ReadAllBytesAsync(filePath);
            File.Delete(filePath);

            return new FileDownloadDto
            {
                FileName = filename,
                FileContent = Convert.ToBase64String(fileBytes) // Use Base64 encoding for file content
            };
        }

        #endregion

        #region Create Patient
        [Authorize(HospitalManagementSystemPermissions.Patient.Create)]
        public async Task<PatientRecordDto> CreatePatientRecordAsync(CreateUpdatePatientRecordDto input)
        {
            var patientEntity = await _patientRepository.GetAsync(input.PatientId); // Get Patient from DB

            var patientRecord = ObjectMapper.Map<CreateUpdatePatientRecordDto, PatientRecord>(input);
            patientRecord.Patients = patientEntity; // Assign the fetched entity

            patientRecord = await _patientrecordRepository.InsertAsync(patientRecord);

            return ObjectMapper.Map<PatientRecord, PatientRecordDto>(patientRecord);
        }
        #endregion

        #region Update Patient
        [Authorize(HospitalManagementSystemPermissions.Patient.Edit)]
        public async Task<PatientRecordDto> UpdatePatientRecordAsync(Guid id, CreateUpdatePatientRecordDto input)
        {
            var patientRecord = await _patientrecordRepository.GetAsync(id);

            if (patientRecord.Patients.Id != input.PatientId) // If PatientId is updated, fetch new Patient
            {
                var newPatient = await _patientRepository.GetAsync(input.PatientId);
                patientRecord.Patients = newPatient;
            }

            ObjectMapper.Map(input, patientRecord);

            patientRecord = await _patientrecordRepository.UpdateAsync(patientRecord);

            return ObjectMapper.Map<PatientRecord, PatientRecordDto>(patientRecord);

        }
        #endregion

        #region Delete Patient
        [Authorize(HospitalManagementSystemPermissions.Patient.Delete)]
        public async Task DeletePatientRecordAsync(Guid id)
        {
            await _patientrecordRepository.DeleteAsync(id);
        }
        #endregion
        #endregion

        #region Patient

        #region Get Patient List with Paging and Searching
        [Authorize(HospitalManagementSystemPermissions.Patient.Default)]
        public async Task<PagedResultDto<PatientDto>> GetPatientListAsync(PagingSortPatientResultDto input)
        {
            List<Patient> query;

            if (!string.IsNullOrEmpty(input.Search))
            {
                query = _patientRepository.GetQueryableAsync().Result
                    .Where(x => x.Name.ToLower().Contains(input.Search.ToLower()))
                    .OrderBy(input.Sorting ?? (nameof(Patient.Id) + " asc"))
                    .Skip(input.SkipCount)
                    .Take(input.MaxResultCount)
                    .ToList();
            }
            else
            {
                query = await _patientRepository.GetPagedListAsync(
                    input.SkipCount,
                    input.MaxResultCount,
                    input.Sorting ?? nameof(Patient.Name)
                );
            }

            var totalCount = await _patientRepository.CountAsync();
            return new PagedResultDto<PatientDto>(
                totalCount,
                ObjectMapper.Map<List<Patient>, List<PatientDto>>(query)
            );
        }
        #endregion

        #region Get Single Patient by Id
        [Authorize(HospitalManagementSystemPermissions.Patient.Default)]
        public async Task<PatientDto> GetPatientByIdAsync(Guid id)
        {
            var patient = await _patientRepository.GetAsync(id);
            return ObjectMapper.Map<Patient, PatientDto>(patient);
        }
        #endregion

        #region Export Patient Data to Excel
        public async Task<FileDownloadDto> GetExportPatientDataAsync()
        {
            var patients = await _patientRepository.GetListAsync();

            var folderPath = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "exports");
            if (!Directory.Exists(folderPath))
            {
                Directory.CreateDirectory(folderPath);
            }

            var filename = "Patients_" + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".xlsx";
            var filePath = Path.Combine(folderPath, filename);

            using (var workbook = new XLWorkbook())
            {
                var worksheet = workbook.Worksheets.Add("Patients");

                // Add headers
                worksheet.Cell(1, 1).Value = "Name";
                worksheet.Cell(1, 2).Value = "Gender";
                worksheet.Cell(1, 3).Value = "Age";
                worksheet.Cell(1, 4).Value = "Treatment";
                worksheet.Cell(1, 5).Value = "Doctor Assigned";
                worksheet.Cell(1, 6).Value = "Admission Date";
                worksheet.Cell(1, 7).Value = "Discharge Date";
                worksheet.Cell(1, 8).Value = "Status";

                // Add data rows
                for (int i = 0; i < patients.Count; i++)
                {
                    worksheet.Cell(i + 2, 1).Value = patients[i].Name;
                    worksheet.Cell(i + 2, 2).Value = patients[i].Gender.ToString();
                    worksheet.Cell(i + 2, 3).Value = patients[i].Age;
                    worksheet.Cell(i + 2, 4).Value = patients[i].Treatment;
                    worksheet.Cell(i + 2, 5).Value = patients[i].DoctorAssigned;
                    worksheet.Cell(i + 2, 6).Value = patients[i].AdmissionDate.ToShortDateString();
                    worksheet.Cell(i + 2, 7).Value = patients[i].DischargeDate?.ToShortDateString();
                    worksheet.Cell(i + 2, 8).Value = patients[i].Status.ToString();
                }

                worksheet.Columns().AdjustToContents();
                workbook.SaveAs(filePath);
            }

            byte[] fileBytes = await File.ReadAllBytesAsync(filePath);
            File.Delete(filePath);

            return new FileDownloadDto
            {
                FileName = filename,
                FileContent = Convert.ToBase64String(fileBytes)
            };
        }
        #endregion

        #region Create Patient
        [Authorize(HospitalManagementSystemPermissions.Patient.Create)]
        public async Task<PatientDto> CreatePatientAsync(CreateUpdatePatientDto input)
        {
            var patient = ObjectMapper.Map<CreateUpdatePatientDto, Patient>(input);

            patient = await _patientRepository.InsertAsync(patient);
            return ObjectMapper.Map<Patient, PatientDto>(patient);
        }
        #endregion

        #region Update Patient
        [Authorize(HospitalManagementSystemPermissions.Patient.Edit)]
        public async Task<PatientDto> UpdatePatientAsync(Guid id, CreateUpdatePatientDto input)
        {
            var patient = await _patientRepository.GetAsync(id);

            ObjectMapper.Map(input, patient);

            patient = await _patientRepository.UpdateAsync(patient);
            return ObjectMapper.Map<Patient, PatientDto>(patient);
        }
        #endregion

        #region Delete Patient
        [Authorize(HospitalManagementSystemPermissions.Patient.Delete)]
        public async Task DeletePatientAsync(Guid id)
        {
            await _patientRepository.DeleteAsync(id);
        }
        #endregion

        #endregion

        #region Get Status Dropdown
        public async Task<List<DropDownItems>> GetStatusDropdownAsync()
        {
            List<DropDownItems> statuslist = new List<DropDownItems>();

            statuslist = Enum.GetValues(typeof(Status))
                .Cast<Status>()
                .Select(e => new DropDownItems
                {
                    Label = e.ToString(),
                    Value = (int)e
                })
                .ToList();
            statuslist.Add(new DropDownItems
            {
                Label = "Select a Status",
                Value = 0
            });
            statuslist = statuslist.OrderBy(x => x.Value).ToList();

            return await Task.FromResult(statuslist);
        }
        #endregion
    }
}
