using Hotline.Application.Tools;
using Hotline.KnowledgeBase;
using Hotline.Repository.SqlSugar.Extensions;
using Hotline.Settings;
using Hotline.Settings.Hotspots;
using Hotline.Share.Dtos;
using Hotline.Share.Dtos.Knowledge;
using Hotline.Share.Enums.Article;
using Hotline.Share.Enums.KnowledgeBase;
using Hotline.Share.Tools;
using Hotline.Users;
using Mapster;
using MapsterMapper;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Org.BouncyCastle.Utilities.IO;
using PanGu;
using SqlSugar;
using XF.Domain.Authentications;
using XF.Domain.Dependency;
using XF.Domain.Exceptions;
using XF.Domain.Repository;

namespace Hotline.Application.Knowledge
{
    public class KnowApplication : IKnowApplication, IScopeDependency
    {
        private readonly IKnowledgeRepository _knowledgeRepository;
        private readonly IRepository<KnowledgePv> _knowledgePvepository;
        private readonly IRepository<Hotspot> _hotspotTypeRepository;
        private readonly IRepository<KnowledgeType> _knowledgeTypeRepository;
        private readonly IRepository<KnowledgeApply> _knowledgeApplyRepository;
        private readonly IRepository<KnowledgeWord> _knowledgeWordRepository;
        private readonly IRepository<KnowledgeHotWord> _knowledgeHotWordRepository;
        private readonly ISessionContext _sessionContext;
        private readonly IMapper _mapper;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="knowledgeRepository"></param>
        /// <param name="knowledgeApplyRepository"></param>
        /// <param name="sessionContext"></param>
        /// <param name="knowledgeDomainService"></param>
        /// <param name="mapper"></param>
        public KnowApplication(IKnowledgeRepository knowledgeRepository, IRepository<KnowledgeApply> knowledgeApplyRepository, ISessionContext sessionContext, IMapper mapper, IRepository<KnowledgeType> knowledgeTypeRepository, IRepository<Hotspot> hotspotTypeRepository, IRepository<KnowledgeWord> knowledgeWordRepository, IRepository<KnowledgePv> knowledgePvepository, IRepository<KnowledgeHotWord> knowledgeHotWordRepository)
        {
            _knowledgeRepository = knowledgeRepository;
            _knowledgeApplyRepository = knowledgeApplyRepository;
            _sessionContext = sessionContext;
            _mapper = mapper;
            _knowledgeTypeRepository = knowledgeTypeRepository;
            _hotspotTypeRepository = hotspotTypeRepository;
            _knowledgeWordRepository = knowledgeWordRepository;
            _knowledgePvepository = knowledgePvepository;
            _knowledgeHotWordRepository = knowledgeHotWordRepository;
        }

        /// <summary>
        /// 知识库查询
        /// </summary>
        /// <param name="pagedDto"></param>
        /// <returns></returns>
        public async Task<(int, IList<KnowledgeDataDto>)> GetKnowList(KnowPagedListDto pagedDto, CancellationToken cancellationToken)
        {
            var typeSpliceName = string.Empty;
            var hotspotHotSpotFullName = string.Empty;
            if (!string.IsNullOrEmpty(pagedDto.KnowledgeTypeId))
            {
                var type = await _knowledgeTypeRepository.GetAsync(x => x.Id == pagedDto.KnowledgeTypeId);
                typeSpliceName = type?.SpliceName;
            }
            if (!string.IsNullOrEmpty(pagedDto.HotspotId))
            {
                var hotspot = await _hotspotTypeRepository.GetAsync(x => x.Id == pagedDto.HotspotId);
                hotspotHotSpotFullName = hotspot?.HotSpotFullName;
            }
            var (total, temp) = await _knowledgeRepository.Queryable(false, false, false)
                .Includes(x => x.User)
                .Includes(x => x.SystemOrganize)
                .Includes(x => x.SourceOrganize)
                .Includes(x => x.HotspotType)
                .Includes(x => x.Workflow)
                .Includes(x => x.KnowledgeType)
                .Where(x => x.IsDeleted == false)
                .Where(x => x.KnowledgeType.Any(t => t.KnowledgeType.KnowledgeTypeOrgs.Any(to => to.OrgId == _sessionContext.RequiredOrgId) || t.KnowledgeType.KnowledgeTypeOrgs.Any() == false))
                .Where(x => (x.Status == EKnowledgeStatus.Drafts && x.CreatorId == _sessionContext.UserId) || (x.Status != EKnowledgeStatus.Drafts))
                .WhereIF(!string.IsNullOrEmpty(pagedDto.Title), x => x.Title.Contains(pagedDto.Title!))
                .WhereIF(!string.IsNullOrEmpty(pagedDto.Keyword), x => x.Title.Contains(pagedDto.Keyword!) || x.CreatorName.Contains(pagedDto.Keyword!) || x.CreatorOrgName.Contains(pagedDto.Keyword!) || x.SourceOrganize.Name.Contains(pagedDto.Keyword!))
                .WhereIF(pagedDto.Status.HasValue && pagedDto.Status != EKnowledgeStatus.OffShelf, x => x.Status == pagedDto.Status && ((x.ExpiredTime != null && x.ExpiredTime > DateTime.Now) || x.ExpiredTime == null))
                .WhereIF(pagedDto.Status.HasValue && pagedDto.Status == EKnowledgeStatus.OffShelf, x => x.Status == pagedDto.Status || (x.ExpiredTime != null && x.ExpiredTime < DateTime.Now && x.Status != EKnowledgeStatus.Drafts))
                .WhereIF(pagedDto.IsPublic.HasValue, x => x.IsPublic == pagedDto.IsPublic)
                .WhereIF(!string.IsNullOrEmpty(pagedDto.Summary), x => x.Summary != null && x.Summary.Contains(pagedDto.Summary!))
                .WhereIF(!string.IsNullOrEmpty(typeSpliceName), x => x.KnowledgeType.Any(t => t.KnowledgeTypeSpliceName.EndsWith(typeSpliceName)))
                .WhereIF(!string.IsNullOrEmpty(hotspotHotSpotFullName), x => x.HotspotType.HotSpotFullName.EndsWith(hotspotHotSpotFullName!))
                .WhereIF(!string.IsNullOrEmpty(pagedDto.CreateOrgId), x => x.SourceOrganizeId != null && x.SourceOrganizeId.EndsWith(pagedDto.CreateOrgId!))
                .WhereIF(!string.IsNullOrEmpty(pagedDto.ModuleCode), x => x.Workflow.ModuleCode == pagedDto.ModuleCode)
                .OrderByDescending(d => d.CreationTime)
                .ToPagedListAsync(pagedDto.PageIndex, pagedDto.PageSize, cancellationToken);
            //返回数据
            return (total, _mapper.Map<IList<KnowledgeDataDto>>(temp));
        }

        /// <summary>
        /// 知识申请查询
        /// </summary>
        /// <param name="pagedDto"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<PagedDto<KnowledgeApplyHandlePageDto>> GetApplyList(KnowledgeApplyPagedDto pagedDto, CancellationToken cancellationToken)
        {
            var (total, items) = await _knowledgeApplyRepository
               .Queryable()
               .Includes(it => it.User)
               .Includes(it => it.SystemOrganize)
               .Where(d => d.CreatorId == _sessionContext.RequiredUserId)
               .WhereIF(pagedDto.Status.HasValue, d => d.Status == pagedDto.Status)
               .WhereIF(pagedDto.ApplyType.HasValue, d => d.KnowledgeApplyType == pagedDto.ApplyType)
               .WhereIF(!string.IsNullOrEmpty(pagedDto.IsOvertime) && pagedDto.IsOvertime == "0", d => d.IsOvertime == true)
               .WhereIF(!string.IsNullOrEmpty(pagedDto.IsOvertime) && pagedDto.IsOvertime == "1", d => d.IsOvertime == false)
               .WhereIF(pagedDto.StartTime.HasValue, d => d.CreationTime >= pagedDto.StartTime)
               .WhereIF(pagedDto.EndTime.HasValue, d => d.CreationTime <= pagedDto.EndTime)
               .OrderByDescending(p => p.CreationTime)
               .ToPagedListAsync(pagedDto.PageIndex, pagedDto.PageSize, cancellationToken);

            return new PagedDto<KnowledgeApplyHandlePageDto>(total, _mapper.Map<IReadOnlyList<KnowledgeApplyHandlePageDto>>(items));
        }

        /// <summary>
        /// 申请处理查询
        /// </summary>
        /// <param name="pagedDto"></param>
        /// <param name="cancellationToken"></param>
        /// <returns></returns>
        public async Task<PagedDto<KnowledgeApplyHandlePageDto>> GetApplyHandleList(KnowledgeHandlePagedDto pagedDto, CancellationToken cancellationToken)
        {
            var (total, items) = await _knowledgeApplyRepository
                 .Queryable()
                 .Includes(it => it.User)
                 .Includes(it => it.SystemOrganize)
                 .Where(p => p.DepartmentId == _sessionContext.RequiredOrgId)
                 .WhereIF(pagedDto.Status.HasValue, d => d.Status == pagedDto.Status)
                 .WhereIF(!string.IsNullOrEmpty(pagedDto.Keyword), d => d.User.Name.Contains(pagedDto.Keyword!) || d.SystemOrganize.Name.Contains(pagedDto.Keyword!))
                 .WhereIF(pagedDto.ApplyType.HasValue, d => d.KnowledgeApplyType == pagedDto.ApplyType)
                 .WhereIF(!string.IsNullOrEmpty(pagedDto.IsOvertime) && pagedDto.IsOvertime == "0", d => d.IsOvertime == true)
                 .WhereIF(!string.IsNullOrEmpty(pagedDto.IsOvertime) && pagedDto.IsOvertime == "1", d => d.IsOvertime == false)

                 .WhereIF(pagedDto.StartTime.HasValue && pagedDto.Status != EKnowledgeApplyStatus.Failed && pagedDto.Status != EKnowledgeApplyStatus.Succeed, d => d.CreationTime >= pagedDto.StartTime)
                 .WhereIF(pagedDto.EndTime.HasValue && pagedDto.Status != EKnowledgeApplyStatus.Failed && pagedDto.Status != EKnowledgeApplyStatus.Succeed, d => d.CreationTime <= pagedDto.EndTime)
                 .WhereIF(pagedDto.StartTime.HasValue && pagedDto.Status == EKnowledgeApplyStatus.Failed, d => d.ReturnTime >= pagedDto.StartTime)
                 .WhereIF(pagedDto.EndTime.HasValue && pagedDto.Status == EKnowledgeApplyStatus.Failed, d => d.ReturnTime <= pagedDto.EndTime)
                  .WhereIF(pagedDto.StartTime.HasValue && pagedDto.Status == EKnowledgeApplyStatus.Succeed, d => d.HandleTime >= pagedDto.StartTime)
                 .WhereIF(pagedDto.EndTime.HasValue && pagedDto.Status == EKnowledgeApplyStatus.Succeed, d => d.HandleTime <= pagedDto.EndTime)

                 .OrderByDescending(p => p.CreationTime)
                 .ToPagedListAsync(pagedDto.PageIndex, pagedDto.PageSize, cancellationToken);

            return new PagedDto<KnowledgeApplyHandlePageDto>(total, _mapper.Map<IReadOnlyList<KnowledgeApplyHandlePageDto>>(items));
        }

        public async Task<Dictionary<string, Stream>> KnowledgeInfoListExportAsync(KnowledgeInfoExportInDto dto, CancellationToken cancellationToken)
        {
            var streamList = new Dictionary<string, Stream>();
            var knowList = await _knowledgeRepository.Queryable()
                .Where(m => dto.Ids.Contains(m.Id))
                .Select(m => new { m.Title, m.Content })
                .ToListAsync(cancellationToken);
            foreach (var item in knowList)
            {
                Stream stream = item.Content.HtmlToStream(dto.FileType);
                streamList.Add(item.Title + dto.FileType.GetFileExtension(), stream);
            }
            return streamList;
        }

        public async Task<IList<KnowledgeWordOutDto>> TitleParticiple(string title)
        {
            var keywords = new List<KnowledgeWordOutDto>();
            var splitWords = new Segment().DoSegment(title);
            for (int i = 0;i < splitWords.Count;i++)
            {
                var word = splitWords.ElementAt(i);
                if (word is not { WordType: WordType.SimplifiedChinese, Word.Length: > 1 }) continue;

                var tag = splitWords.ElementAt(i).Word;
                var entity = await _knowledgeWordRepository.Queryable().Where(m => m.Tag == tag).FirstAsync();
                if (entity is not null)
                {
                    keywords.Add(new KnowledgeWordOutDto(entity.Id, entity.Tag));
                    continue;
                }

                var wordEntity = new KnowledgeWord
                {
                    Tag = tag,
                    Classify = "普通标签",
                    Remark = "系统自动从知识标题分词",
                    Synonym = "",
                    IsEnable = 1
                };
                var keyId = await _knowledgeWordRepository.AddAsync(wordEntity);
                keywords.Add(new KnowledgeWordOutDto(keyId, tag));
            }
            return keywords;
        }

        public async Task<(int, IList<PageViewOutDto>)> GetPageViewListAsync(PageViewInDto dto, CancellationToken requestAborted = default)
        {
            var query = _knowledgePvepository.Queryable(includeDeleted: true)
                .LeftJoin<KnowledgeBase.Knowledge>((p, k) => p.KnowledgeCode == k.Code)
                .LeftJoin<KnowledgeRelationType>((p, k, r) => r.KnowledgeId == k.Id)
                .WhereIF(dto.Title.NotNullOrEmpty(), (p, k, r) => k.Title.Contains(dto.Title))
                .WhereIF(dto.CreatorName.NotNullOrEmpty(), (p, k, r) => p.CreatorName.Contains(dto.CreatorName))
                .WhereIF(dto.KnowledgeTypeId.NotNullOrEmpty(), (p, k, r) => r.KnowledgeTypeId == dto.KnowledgeTypeId)
                .WhereIF(dto.StartTime.HasValue && dto.EndTime.HasValue, (p, k, r) => p.CreationTime >= dto.StartTime && p.CreationTime <= dto.EndTime)
                .OrderByDescending((p, k, r) => p.CreationTime);

            if (_sessionContext.OrgIsCenter == false)
                query = query.Where((p, k, r) => p.CreatorOrgId.StartsWith(_sessionContext.OrgId));

            return await query.Select<PageViewOutDto>().ToPagedListAsync(dto.PageIndex, dto.PageSize, requestAborted);
        }

        public async Task<(int total, IList<KnowledgeHotWordOutDto> items)> GetKnowledgeHotWordListAsync(KnowledgeHotWordInDto dto, CancellationToken requestAborted)
        {
            var query = _knowledgeHotWordRepository.Queryable()
                .WhereIF(dto.IsEnable != null, m => m.IsEnable == dto.IsEnable)
                .WhereIF(dto.KeyWord.NotNullOrEmpty(), m => m.KeyWord.Contains(dto.KeyWord))
                .WhereIF(dto.Type != null, m => m.Type == dto.Type)
                // .WhereIF(dto.StartTime.HasValue && dto.EndTime.HasValue, m => m.CreationTime >= dto.StartTime && m.CreationTime <= dto.EndTime)
                ;

            if (dto.SortField.NotNullOrEmpty())
                query = query.OrderByPropertyName(dto.SortField, (OrderByType)dto.OrderByType);
            else
            {
                query = query.OrderByDescending(m => m.Sort);
                query = query.OrderByDescending(m => m.CreationTime);
            }

            return await query.Select<KnowledgeHotWordOutDto>().ToPagedListAsync(dto.PageIndex, dto.PageSize, requestAborted);
        }

        public async Task UpdateKnowledgeHotWordAsync(UpdateKnowledgeHotWordInDto dto, CancellationToken requestAborted)
        {
            var entity = await _knowledgeHotWordRepository.GetAsync(dto.Id)
                ?? throw UserFriendlyException.SameMessage("数据不存在");
            dto.Adapt(entity);
            await _knowledgeHotWordRepository.UpdateAsync(entity);
        }

        public async Task AddKnowledgeHotWordAsync(AddKnowledgeHotWordInDto dto, CancellationToken requestAborted)
        {
            var isExist = await _knowledgeHotWordRepository.Queryable()
                .Where(m => m.KeyWord == dto.KeyWord).AnyAsync();
            if (isExist) throw UserFriendlyException.SameMessage("热词已存在");
            var entity = dto.Adapt<KnowledgeHotWord>();
            await _knowledgeHotWordRepository.AddAsync(entity, requestAborted);
        }

        public async Task<(int, List<KnowledgeRetrievalDataDto>)> KnowRetrievalAsync(KnowledgeRetrievalPagedListDto dto)
        {
            var typeSpliceName = string.Empty;
            var hotspotHotSpotFullName = string.Empty;
            if (!string.IsNullOrEmpty(dto.KnowledgeTypeId))
            {
                var type = await _knowledgeTypeRepository.GetAsync(x => x.Id == dto.KnowledgeTypeId);
                typeSpliceName = type?.SpliceName;
            }
            if (!string.IsNullOrEmpty(dto.HotspotId))
            {
                var hotspot = await _hotspotTypeRepository.GetAsync(x => x.Id == dto.HotspotId);
                hotspotHotSpotFullName = hotspot?.HotSpotFullName;
            }

            var sugar = _knowledgeRepository
                .Queryable(false, false, false)
                .Includes(x => x.User)
                .Includes(x => x.SystemOrganize)
                .Includes(x => x.HotspotType)
                .Where(x => x.IsDeleted == false)
                .Where(x => x.Status == EKnowledgeStatus.OnShelf)
                .Where(x => x.KnowledgeType.Any(t => t.KnowledgeType.KnowledgeTypeOrgs.Any(to => to.OrgId == _sessionContext.RequiredOrgId) || t.KnowledgeType.KnowledgeTypeOrgs.Any() == false))
                .WhereIF(!string.IsNullOrEmpty(typeSpliceName), x => x.KnowledgeType.Any(t => t.KnowledgeTypeSpliceName.EndsWith(typeSpliceName)))
                .WhereIF(!string.IsNullOrEmpty(hotspotHotSpotFullName), x => x.HotspotType.HotSpotFullName.EndsWith(hotspotHotSpotFullName!))
                .WhereIF(!string.IsNullOrEmpty(dto.HotspotName), x => x.HotspotType.HotSpotFullName.EndsWith(dto.HotspotName!))
                .WhereIF(!string.IsNullOrEmpty(dto.CreateOrgId), x => x.CreatorOrgId != null && x.CreatorOrgId.EndsWith(dto.CreateOrgId!))
                .WhereIF(!string.IsNullOrEmpty(dto.Attribution), x => x.Attribution == dto.Attribution!);
            if (dto.Keyword.NotNullOrEmpty())
            {
                var keywords = dto.Keyword!.SplitKeywords();
                var exp = Expressionable.Create<KnowledgeBase.Knowledge>();
                foreach (var keyword in keywords)
                {
                    if (dto.RetrievalType == EKnowledgeRetrievalType.All)
                        exp.Or(x => x.Title.Contains(keyword) || x.Content.Contains(keyword));
                    if (dto.RetrievalType == EKnowledgeRetrievalType.Title)
                        exp.Or(x => x.Title.Contains(keyword));
                    if (dto.RetrievalType == EKnowledgeRetrievalType.Content)
                        exp.Or(x => x.Content.Contains(keyword));
                    if (dto.RetrievalType == EKnowledgeRetrievalType.Summary)
                        exp.Or(x => x.Summary != null && x.Summary.Contains(keyword));
                    if (dto.RetrievalType == EKnowledgeRetrievalType.KeyWord)
                    {
                        var keywordEntity = await _knowledgeWordRepository.GetAsync(m => m.Tag == keyword && m.IsEnable == 0);
                        if (keywordEntity is null) continue;
                        exp.Or(x => SqlFunc.JsonArrayAny(x.Keywords, keywordEntity.Id));
                    }
                }
                sugar = sugar.Where(exp.ToExpression());
            }
            if (dto.Content.NotNullOrEmpty())
            {
                var keywords = dto.Content!.GetSegment();
                var exp = Expressionable.Create<KnowledgeBase.Knowledge>();
                 _knowledgeWordRepository.Queryable()
                    .Where(m => keywords.Contains(m.Tag) && m.IsEnable == 0)
                    .Select(m => m.Id)
                    .ToList()
                    .ForEach(m => 
                        exp.Or(x => SqlFunc.JsonArrayAny(x.Keywords, m))
                    );
                sugar = sugar.Where(exp.ToExpression());
            }

            switch (dto.Sort)
            {
                case "2":
                    sugar = sugar.OrderByDescending(p => p.CollectCount);
                    break;
                case "3":
                    sugar = sugar.OrderByDescending(p => p.CreationTime);
                    break;
                default:
                    sugar = sugar.OrderByDescending(p => p.PageView);
                    break;
            }
            return await sugar.Select<KnowledgeRetrievalDataDto>().ToPagedListAsync(dto.PageIndex, dto.PageSize);
        }
    }
}
