using System.Security.Claims;
using System.Text.Json;
using BMA.EHR.Recurit.Exam.Service.Extensions;
using BMA.EHR.Recurit.Exam.Service.Core;
using BMA.EHR.Recurit.Exam.Service.Data;
using BMA.EHR.Recurit.Exam.Service.Models;
using BMA.EHR.Recurit.Exam.Service.Models.Documents;
using BMA.EHR.Recurit.Exam.Service.Request;
using BMA.EHR.Recurit.Exam.Service.Response;
using BMA.EHR.Recurit.Exam.Service.Responses.Document;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json.Linq;
using OfficeOpenXml;

namespace BMA.EHR.Recurit.Exam.Service.Services
{
    public class CMSCandidateService
    {
        #region " Fields "

        private readonly ApplicationDbContext _context;
        private readonly OrgDbContext _contextOrg;
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly MinIOService _minioService;

        #endregion

        #region " Constructor and Destructor "

        public CMSCandidateService(ApplicationDbContext context,
                                     OrgDbContext contextOrg,
                                     IHttpContextAccessor httpContextAccessor,
                                     MinIOService minioService)
        {
            _context = context;
            _contextOrg = contextOrg;
            _httpContextAccessor = httpContextAccessor;
            _minioService = minioService;
        }

        public static IConfigurationRoot Configuration
        {
            get
            {
                return new ConfigurationBuilder()
                                  .SetBasePath(Directory.GetCurrentDirectory())
                                  .AddJsonFile("appsettings.json")
                                  .Build();
            }
        }

        #endregion

        #region " Properties "

        private string? UserId => _httpContextAccessor?.HttpContext?.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value;

        private string? FullName => _httpContextAccessor?.HttpContext?.User?.FindFirst("name")?.Value;

        #endregion

        #region " Methods "

        static public string GetNameCategory(string? category)
        {
            if (category == null)
                return "-";
            switch (category)
            {
                case "hygiene":
                    return "สำนักอนามัย";
                case "physician":
                    return "สำนักการแพทย์";
                case "city":
                    return "สำนักผังเมือง";
                case "culture":
                    return "สำนักวัฒนธรรม กีฬา และการท่องเที่ยว";
                default:
                    return "-";
            }
        }

        public async Task<CMSCandidate> GetsAsync()
        {
            var cms = await createCMS();

            if (cms.LogoImg != null)
                cms.LogoImg.Detail = _minioService.ImagesPath(cms.LogoImg.Id).Result;

            if (cms.BannerImg != null)
                cms.BannerImg.Detail = _minioService.ImagesPath(cms.BannerImg.Id).Result;

            return cms;
        }

        public async Task<CMSCandidate> createCMS()
        {
            var cmsCandidates = await _context.CMSCandidates.AsQueryable()
                            .Include(x => x.BannerImg)
                            .Include(x => x.LogoImg)
                            .Include(x => x.CMSAgencys)
                            .Include(x => x.CMSGovernments)
                            .FirstOrDefaultAsync();
            if (cmsCandidates == null)
            {
                cmsCandidates = new CMSCandidate
                {
                    CreatedAt = DateTime.Now,
                    CreatedUserId = UserId ?? "",
                    LastUpdatedAt = DateTime.Now,
                    LastUpdateUserId = UserId ?? "",
                    CreatedFullName = FullName ?? "",
                    LastUpdateFullName = FullName ?? "",
                };
                await _context.CMSCandidates.AddAsync(cmsCandidates);
            }

            return cmsCandidates;
        }

        public async Task UpdateDetailAsync(RequestCMSAbout updated)
        {
            var cms = await createCMS();

            cms.NameTh = updated.NameTh;
            cms.ShortName = updated.ShortName;
            cms.NameEn = updated.NameEn;
            cms.Description = updated.Description;
            cms.LastUpdatedAt = DateTime.Now;
            cms.LastUpdateUserId = UserId ?? "";
            cms.LastUpdateFullName = FullName ?? "";

            await _context.SaveChangesAsync();
        }

        public async Task UpdateAboutAsync(RequestCMSAbout updated)
        {
            var cms = await createCMS();

            if (updated.ProvinceId != null)
            {
                var province = await _contextOrg.province.AsQueryable()
                                .FirstOrDefaultAsync(x => x.Id == Guid.Parse(updated.ProvinceId));

                if (province == null)
                    throw new Exception(GlobalMessages.ProvinceNotFound);

                cms.ProvinceId = province.Id;
                cms.ProvinceName = province.name;
            }

            if (updated.DistrictId != null)
            {
                var pistrict = await _contextOrg.district.AsQueryable()
                                .FirstOrDefaultAsync(x => x.Id == Guid.Parse(updated.DistrictId));

                if (pistrict == null)
                    throw new Exception(GlobalMessages.DistrictNotFound);

                cms.DistrictId = pistrict.Id;
                cms.DistrictName = pistrict.name;
            }

            if (updated.SubDistrictId != null)
            {
                var subDistrict = await _contextOrg.subDistrict.AsQueryable()
                                .FirstOrDefaultAsync(x => x.Id == Guid.Parse(updated.SubDistrictId));

                if (subDistrict == null)
                    throw new Exception(GlobalMessages.SubDistrictNotFound);

                cms.SubDistrictId = subDistrict.Id;
                cms.SubDistrictName = subDistrict.name;
                cms.ZipCode = subDistrict.zipCode;
            }

            cms.About = updated.About;
            cms.Address = updated.Address;
            cms.Telephone = updated.Telephone;
            cms.LastUpdatedAt = DateTime.Now;
            cms.LastUpdateUserId = UserId ?? "";
            cms.LastUpdateFullName = FullName ?? "";

            await _context.SaveChangesAsync();
        }

        public async Task UpdateLogoAsync(IFormFile file)
        {
            var cms = await createCMS();

            if (cms.LogoImg != null)
            {
                var docId = cms.LogoImg.Id.ToString();
                cms.LogoImg = null;
                await _context.SaveChangesAsync();
                await DeleteAsyncDocument(docId);
            }

            var doc = await _minioService.UploadFileAsync(file);

            var document = await _context.Documents.AsQueryable()
                            .FirstOrDefaultAsync(x => x.Id == doc.Id);

            cms.LogoImg = document;
            await _context.SaveChangesAsync();
        }

        public async Task UpdateBannerAsync(IFormFile file)
        {
            var cms = await createCMS();

            if (cms.BannerImg != null)
            {
                var docId = cms.BannerImg.Id.ToString();
                cms.BannerImg = null;
                await _context.SaveChangesAsync();
                await DeleteAsyncDocument(docId);
            }

            var doc = await _minioService.UploadFileAsync(file);

            var document = await _context.Documents.AsQueryable()
                            .FirstOrDefaultAsync(x => x.Id == doc.Id);

            cms.BannerImg = document;
            await _context.SaveChangesAsync();
        }

        public async Task DeleteAsyncDocument(string documentId)
        {
            await _minioService.DeleteFileAsync(Guid.Parse(documentId));
        }

        public async Task UpdateAgencyAsync(List<RequestCMSAgency> updated)
        {
            var cms = await createCMS();

            _context.CMSAgencys.RemoveRange(cms.CMSAgencys);

            foreach (var agencyData in updated)
            {
                var agency = new CMSAgency
                {
                    CMSCandidate = cms,
                    Name = agencyData.Name,
                    Link = agencyData.Link,
                    CreatedAt = DateTime.Now,
                    CreatedUserId = UserId ?? "",
                    LastUpdatedAt = DateTime.Now,
                    LastUpdateUserId = UserId ?? "",
                    CreatedFullName = FullName ?? "",
                    LastUpdateFullName = FullName ?? "",
                };
                await _context.AddAsync(agency);
            }
            await _context.SaveChangesAsync();
        }

        public async Task UpdateGovernmentAsync(List<RequestCMSGovernment> updated)
        {
            var cms = await createCMS();

            _context.CMSGovernments.RemoveRange(cms.CMSGovernments);

            foreach (var governmentData in updated)
            {
                var government = new CMSGovernment
                {
                    CMSCandidate = cms,
                    Name = governmentData.Name,
                    Link = governmentData.Link,
                    CreatedAt = DateTime.Now,
                    CreatedUserId = UserId ?? "",
                    LastUpdatedAt = DateTime.Now,
                    LastUpdateUserId = UserId ?? "",
                    CreatedFullName = FullName ?? "",
                    LastUpdateFullName = FullName ?? "",
                };
                await _context.AddAsync(government);
            }

            await _context.SaveChangesAsync();
        }

        public async Task<HomePageResponseItem> GetHomePageAsync()
        {
            var cms = await createCMS();

            var cmsCandidates = await _context.CMSCandidates.AsQueryable()
                            .Select(x => new HomePageResponseItem
                            {
                                Logo_url = x.LogoImg == null ? "" : x.LogoImg.Id.ToString(),
                                Title = x.NameTh,
                                Subtitle = x.NameEn,
                                Supervised = x.ShortName,
                                Telephone = x.Telephone,
                                Address = x.Address + " " + (x.SubDistrictName == null ? "" : "แขวง" + x.SubDistrictName + " ") + (x.DistrictName == null ? "" : "เขต" + x.DistrictName) + " " + (x.ProvinceName == null ? "" : x.ProvinceName) + " " + (x.ZipCode == null ? "" : x.ZipCode),
                                Divisions = x.CMSAgencys.OrderBy(x => x.CreatedAt).Select(s => new HomePageLinkResponseItem
                                {
                                    Title = s.Name,
                                    Url = s.Link,
                                }).ToList(),
                                Institutes = x.CMSGovernments.OrderBy(x => x.CreatedAt).Select(s => new HomePageLinkResponseItem
                                {
                                    Title = s.Name,
                                    Url = s.Link,
                                }).ToList(),
                            })
                            .FirstOrDefaultAsync();

            if (cmsCandidates == null)
                throw new Exception(GlobalMessages.CMSNotFound);

            if (cmsCandidates.Logo_url != null && cmsCandidates.Logo_url != "")
                cmsCandidates.Logo_url = _minioService.ImagesPath(Guid.Parse(cmsCandidates.Logo_url)).Result;

            return cmsCandidates;
        }

        public async Task<HomePageContentResponseItem> GetHomePageContentAsync()
        {
            var cms = await createCMS();

            var cmsCandidates = await _context.CMSCandidates.AsQueryable()
                            .Select(x => new HomePageContentResponseItem
                            {
                                Content = x.Description,
                                Image = x.BannerImg == null ? "" : x.BannerImg.Id.ToString(),
                            })
                            .FirstOrDefaultAsync();

            if (cmsCandidates == null)
                throw new Exception(GlobalMessages.CMSNotFound);

            if (cmsCandidates.Image != null && cmsCandidates.Image != "")
                cmsCandidates.Image = _minioService.ImagesPath(Guid.Parse(cmsCandidates.Image)).Result;

            return cmsCandidates;
        }

        public async Task<AboutPageContentResponseItem> GetAboutPageContentAsync()
        {
            var cms = await createCMS();

            var cmsCandidates = await _context.CMSCandidates.AsQueryable()
                            .Select(x => new AboutPageContentResponseItem
                            {
                                Content = x.About,
                            })
                            .FirstOrDefaultAsync();

            if (cmsCandidates == null)
                throw new Exception(GlobalMessages.CMSNotFound);

            return cmsCandidates;
        }

        public async Task<IEnumerable<CMSExamResponseItem?>> GetsCMSExamsAsync(int limit)
        {
            var cms = await createCMS();

            var periodExams = await _context.PeriodExams.AsQueryable()
                            .Where(x => x.AnnouncementStartDate.Date <= DateTime.Now.Date)
                            .Where(x => x.AnnouncementEndDate.Date >= DateTime.Now.Date)
                            .Include(x => x.PeriodExamImages)
                            .OrderByDescending(x => x.AnnouncementStartDate)
                            .Select(x => new CMSExamResponseItem
                            {
                                Id = x.Id.ToString(),
                                Category = GetNameCategory(x.Category),
                                CategoryId = x.Category,
                                Start = x.ExamDate == null ? null : x.ExamDate.Value.ToString("yyyy-MM-dd"),
                                End = x.ExamDate == null ? null : x.ExamDate.Value.ToString("yyyy-MM-dd"),
                                ExamDate = x.ExamDate == null ? null : x.ExamDate.Value.ToString("yyyy-MM-dd"),
                                Announcement_date = x.AnnouncementDate == null ? null : x.AnnouncementDate.Value.ToString("yyyy-MM-dd"),
                                Announcement_startDate = x.AnnouncementStartDate.ToString("yyyy-MM-dd"),
                                Announcement_endDate = x.AnnouncementEndDate.ToString("yyyy-MM-dd"),
                                AnnouncementExam = x.AnnouncementExam,
                                Register_startDate = x.RegisterStartDate == null ? null : x.RegisterStartDate.Value.ToString("yyyy-MM-dd"),
                                Register_endDate = x.RegisterEndDate == null ? null : x.RegisterEndDate.Value.ToString("yyyy-MM-dd"),
                                Payment_startDate = x.PaymentStartDate == null ? null : x.PaymentStartDate.Value.ToString("yyyy-MM-dd"),
                                Payment_endDate = x.PaymentEndDate == null ? null : x.PaymentEndDate.Value.ToString("yyyy-MM-dd"),
                                Title = x.Name,
                                Image = x.PeriodExamImages.OrderBy(o => o.CreatedAt).FirstOrDefault() == null ?
                                            "" :
                                            x.PeriodExamImages.OrderBy(o => o.CreatedAt).FirstOrDefault().Document.Id.ToString(),
                            })
                            .ToListAsync();
            if (limit > 0)
                periodExams = periodExams.Take(limit).ToList();

            var i = 0;
            foreach (var item in periodExams)
            {
                if (periodExams[i].Image != null && periodExams[i].Image != "")
                    periodExams[i].Image = _minioService.ImagesPath(Guid.Parse(periodExams[i].Image)).Result;
                i++;
            }

            return periodExams;
        }

        public async Task<IEnumerable<CMSExamResponseItem?>> GetsCMSPostsAsync(int limit)
        {
            var cms = await createCMS();

            var periodExams = await _context.PeriodExams.AsQueryable()
                            .Where(x => x.AnnouncementStartDate.Date <= DateTime.Now.Date)
                            .Where(x => x.AnnouncementEndDate.Date >= DateTime.Now.Date)
                            .OrderByDescending(x => x.AnnouncementStartDate)
                            .Include(x => x.PeriodExamImages)
                            .Select(x => new CMSExamResponseItem
                            {
                                Id = x.Id.ToString(),
                                Category = GetNameCategory(x.Category),
                                CategoryId = x.Category,
                                Start = x.ExamDate == null ? null : x.ExamDate.Value.ToString("yyyy-MM-dd"),
                                End = x.ExamDate == null ? null : x.ExamDate.Value.ToString("yyyy-MM-dd"),
                                ExamDate = x.ExamDate == null ? null : x.ExamDate.Value.ToString("yyyy-MM-dd"),
                                Announcement_date = x.AnnouncementDate == null ? null : x.AnnouncementDate.Value.ToString("yyyy-MM-dd"),
                                Announcement_startDate = x.AnnouncementStartDate.ToString("yyyy-MM-dd"),
                                Announcement_endDate = x.AnnouncementEndDate.ToString("yyyy-MM-dd"),
                                AnnouncementExam = x.AnnouncementExam,
                                Register_startDate = x.RegisterStartDate == null ? null : x.RegisterStartDate.Value.ToString("yyyy-MM-dd"),
                                Register_endDate = x.RegisterEndDate == null ? null : x.RegisterEndDate.Value.ToString("yyyy-MM-dd"),
                                Payment_startDate = x.PaymentStartDate == null ? null : x.PaymentStartDate.Value.ToString("yyyy-MM-dd"),
                                Payment_endDate = x.PaymentEndDate == null ? null : x.PaymentEndDate.Value.ToString("yyyy-MM-dd"),
                                Title = x.Name,
                                Image = x.PeriodExamImages.OrderBy(o => o.CreatedAt).FirstOrDefault() == null ?
                                            "" :
                                            x.PeriodExamImages.OrderBy(o => o.CreatedAt).FirstOrDefault().Document.Id.ToString(),
                            })
                            .ToListAsync();
            if (limit > 0)
                periodExams = periodExams.Take(limit).ToList();

            var i = 0;
            foreach (var item in periodExams)
            {
                if (periodExams[i].Image != null && periodExams[i].Image != "")
                    periodExams[i].Image = _minioService.ImagesPath(Guid.Parse(periodExams[i].Image)).Result;
                i++;
            }

            return periodExams;
        }

        public async Task<CMSExamResponseItem> GetCMSExamsAsync(string examId)
        {
            var cms = await createCMS();

            var periodExam = await _context.PeriodExams.AsQueryable()
                            .Where(x => x.Id == Guid.Parse(examId))
                            .Include(x => x.PeriodExamImages)
                            .Include(x => x.PeriodExamDocuments)
                            .Include(x => x.PositionExam)
                            .Select(x => new CMSExamResponseItem
                            {
                                Id = x.Id.ToString(),
                                Category = GetNameCategory(x.Category),
                                CategoryId = x.Category,
                                Start = x.AnnouncementStartDate.ToString("yyyy-MM-dd"),
                                End = x.AnnouncementEndDate.ToString("yyyy-MM-dd"),
                                ExamDate = x.ExamDate == null ? null : x.ExamDate.Value.ToString("yyyy-MM-dd"),
                                Announcement_date = x.AnnouncementDate == null ? null : x.AnnouncementDate.Value.ToString("yyyy-MM-dd"),
                                Announcement_startDate = x.AnnouncementStartDate.ToString("yyyy-MM-dd"),
                                Announcement_endDate = x.AnnouncementEndDate.ToString("yyyy-MM-dd"),
                                AnnouncementExam = x.AnnouncementExam,
                                Register_startDate = x.RegisterStartDate == null ? null : x.RegisterStartDate.Value.ToString("yyyy-MM-dd"),
                                Register_endDate = x.RegisterEndDate == null || x.RegisterEndDate == x.RegisterStartDate ? null : x.RegisterEndDate.Value.ToString("yyyy-MM-dd"),
                                Payment_startDate = x.PaymentStartDate == null ? null : x.PaymentStartDate.Value.ToString("yyyy-MM-dd"),
                                Payment_endDate = x.PaymentEndDate == null || x.PaymentEndDate == x.PaymentStartDate ? null : x.PaymentEndDate.Value.ToString("yyyy-MM-dd"),
                                Title = $"{x.Name} ครั้งที่ {x.Round}/{(x.Year == null ? null : x.Year.Value.ToThaiYear())}",
                                Year = x.Year,
                                Round = x.Round,
                                Detail = x.Detail,
                                EditorCondition = x.EditorCondition,
                                EditorConfirm = x.EditorConfirm,
                                Images = x.PeriodExamImages.OrderBy(x => x.CreatedAt).Select(s => new HomePageLinkResponseItem
                                {
                                    Title = s.Document.FileName,
                                    Url = s.Document.Id.ToString(),
                                }).ToList(),
                                Files = x.PeriodExamDocuments.OrderBy(x => x.CreatedAt).Select(s => new HomePageLinkResponseItem
                                {
                                    Title = s.Document.FileName,
                                    Url = s.Document.Id.ToString(),
                                }).ToList(),
                                Positions = x.PositionExam.OrderBy(x => x.CreatedAt).Select(s => new HomePageLinkResponseItem
                                {
                                    Id = s.Id.ToString(),
                                    Title = $"{(s.PositionName == null ? x.Name : s.PositionName)}{(s.PositionLevelName == null ? "" : s.PositionLevelName)}",
                                    Level = s.PositionLevelName == null ? "-" : s.PositionLevelName,
                                    HighDegree = s.HighDegree == true ? "ปริญญาบัตรขึ้นไป" : "ต่ำกว่าปริญญาบัตร",
                                    Path = $"{x.Id}/{s.Id}",
                                })
                                .ToList(),
                                PositionsTrue = x.PositionExam.OrderBy(x => x.CreatedAt).Where(x => x.HighDegree == true).Select(s => new HomePageLinkResponseItem
                                {
                                    Id = s.Id.ToString(),
                                    Title = $"{(s.PositionName == null ? x.Name : s.PositionName)}{(s.PositionLevelName == null ? "" : s.PositionLevelName)}",
                                    Level = s.PositionLevelName == null ? "-" : s.PositionLevelName,
                                    HighDegree = s.HighDegree == true ? "ปริญญาบัตรขึ้นไป" : "ต่ำกว่าปริญญาบัตร",
                                    Path = $"{x.Id}/{s.Id}",
                                })
                                .ToList(),
                                PositionsFalse = x.PositionExam.OrderBy(x => x.CreatedAt).Where(x => x.HighDegree == false).Select(s => new HomePageLinkResponseItem
                                {
                                    Id = s.Id.ToString(),
                                    Title = $"{(s.PositionName == null ? x.Name : s.PositionName)}{(s.PositionLevelName == null ? "" : s.PositionLevelName)}",
                                    Level = s.PositionLevelName == null ? "-" : s.PositionLevelName,
                                    HighDegree = s.HighDegree == true ? "ปริญญาบัตรขึ้นไป" : "ต่ำกว่าปริญญาบัตร",
                                    Path = $"{x.Id}/{s.Id}",
                                })
                                .ToList(),
                                CanRegister = x.RegisterStartDate == null || x.RegisterEndDate == null ? true : DateOnly.FromDateTime(x.RegisterStartDate.Value) <= DateOnly.FromDateTime(DateTime.Now) && DateOnly.FromDateTime(x.RegisterEndDate.Value) >= DateOnly.FromDateTime(DateTime.Now),
                            })
                            .FirstOrDefaultAsync();

            if (periodExam == null)
                throw new Exception(GlobalMessages.ExamNotFound);

            if (periodExam.Positions.Count() <= 0)
            {
                periodExam.Positions.Add(new HomePageLinkResponseItem
                {
                    Title = "สมัครสอบ",
                    Path = $"{periodExam.Id}/00000000-0000-0000-0000-000000000000",
                });
            }

            var i = 0;
            foreach (var item in periodExam.Images)
            {
                if (periodExam.Images[i].Url != null && periodExam.Images[i].Url != "")
                    periodExam.Images[i].Url = _minioService.ImagesPath(Guid.Parse(periodExam.Images[i].Url)).Result;
                i++;
            }
            i = 0;
            foreach (var item in periodExam.Files)
            {
                if (periodExam.Files[i].Url != null && periodExam.Files[i].Url != "")
                    periodExam.Files[i].Url = _minioService.ImagesPath(Guid.Parse(periodExam.Files[i].Url)).Result;
                i++;
            }
            return periodExam;
        }
        #endregion
    }
}
