using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Abp.Application.Services.Dto;
using Abp.Authorization;
using Abp.Domain.Repositories;
using Abp.EntityFrameworkCore;
using Abp.Extensions;
using Abp.Linq.Extensions;
using Abp.Runtime.Session;
using Abp.UI;
using Mapster;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using NUglify.Helpers;
using OfficeOpenXmlHelp.Word;
using YGNT.File.Core;
using YGNT.File.Core.Cache;
using YGNT.Infrastructure.Extension;
using YGNT.Infrastructure.Helper;
using YGNT.QuestionLibrary.Core.BaseData.Enum;
using YGNT.QuestionLibrary.Core.Chapters;
using YGNT.QuestionLibrary.Core.Disciplines;
using YGNT.QuestionLibrary.Core.Disciplines.Cache;
using YGNT.QuestionLibrary.Core.Helper;
using YGNT.QuestionLibrary.Core.KnowledgePoints;
using YGNT.QuestionLibrary.Core.Models;
using YGNT.QuestionLibrary.Core.Questions;
using YGNT.QuestionLibrary.Core.Questions.Domain;
using YGNT.QuestionLibrary.Core.Tasks;
using YGNT.QuestionLibrary.Core.Tasks.Domain;
using YGNT.QuestionLibrary.EntityFrameworkCore;
using YGNT.QuestionLibrary.KnowledgePoints.Dto;
using YGNT.QuestionLibrary.Questions.Dto;

namespace YGNT.QuestionLibrary.Questions
{
    /// <summary>
    /// 试题管理
    /// </summary>
    [AbpAuthorize()]
    public class QuestionAppService : IQuestionAppService
    {
        private readonly IUriHelper _urlHelper;
        private readonly IWebHostEnvironment _webHostEnvironment;
        private readonly IQuestionManager _questionManager;
        private readonly IQuestionResolveManager _questionResolveTaskManager;
        private readonly IRepository<Resolve, long> _resolveRepository;
        private readonly IRepository<Question, long> _questionRepository;
        private readonly IRepository<QuestionOrigin, long> _questionOriginRepository;
        private readonly IRepository<KnowledgePointTreePath, long> _knowledgeTreePathRepository;
        private readonly IRepository<ChapterTreePath, long> _chapterTreePathRepository;
        private readonly IRepository<DisciplineQuestionType> _disciplineQuestionTypeRepository;
        private readonly IRepository<QuestionConvertTask, long> _questionConvertTaskRepository;
        private readonly IRepository<QuestionReplaceTask, long> _questionReplaceTaskRepository;
        private readonly IRepository<Discipline> _disciplineRepository;
        private readonly IDisciplineCache _disciplineCache;
        private readonly IDisciplineQuestionTypeCache _disciplineQuestionTypeCache;
        private readonly ITaskManager _taskManager;
        private readonly IQuestionOriginManager _questionOriginManager;
        private readonly IDocHelper _docHelper;
        private readonly IFileCache _fileCache;
        public IAbpSession AbpSession { get; set; }

        IDbContextProvider<QuestionLibraryDbContext> _dbContextProvider;


        public QuestionAppService(IQuestionManager questionManager, 
            IRepository<Question, long> questionRepository, 
            IRepository<KnowledgePointTreePath, long> treePathRepository, 
            IFileCache fileCache, 
            IRepository<ChapterTreePath, long> chapterTreePathRepository, 
            IQuestionResolveManager questionResolveTaskManager, 
            IUriHelper urlHelper, IQuestionOriginManager questionOriginManager, 
            IRepository<DisciplineQuestionType> disciplineQuestionTypeRepository, 
            IDisciplineCache disciplineCache, IRepository<QuestionOrigin, long> questionOriginRepository, 
            IDisciplineQuestionTypeCache disciplineQuestionTypeCache, IRepository<Resolve, long> resolveRepository,
            IWebHostEnvironment webHostEnvironment, ITaskManager taskManager, 
            IRepository<QuestionConvertTask, long> questionConvertTaskRepository, 
            IRepository<QuestionReplaceTask, long> questionReplaceTaskRepository, IDocHelper docHelper,
            IRepository<Discipline> disciplineRepository,
            IDbContextProvider<QuestionLibraryDbContext> dbContextProvider)
        {
            _questionManager = questionManager;
            _questionRepository = questionRepository;
            _knowledgeTreePathRepository = treePathRepository;
            _fileCache = fileCache;
            _chapterTreePathRepository = chapterTreePathRepository;
            _questionResolveTaskManager = questionResolveTaskManager;
            _urlHelper = urlHelper;
            _questionOriginManager = questionOriginManager;
            _disciplineQuestionTypeRepository = disciplineQuestionTypeRepository;
            _disciplineCache = disciplineCache;
            _questionOriginRepository = questionOriginRepository;
            _disciplineQuestionTypeCache = disciplineQuestionTypeCache;
            _resolveRepository = resolveRepository;
            _webHostEnvironment = webHostEnvironment;
            _taskManager = taskManager;
            _questionConvertTaskRepository = questionConvertTaskRepository;
            _questionReplaceTaskRepository = questionReplaceTaskRepository;
            _docHelper = docHelper;
            AbpSession = NullAbpSession.Instance;
            _disciplineRepository = disciplineRepository;
            _dbContextProvider = dbContextProvider;
        }

        /// <summary>
        /// 查询试题列表
        /// </summary>
        /// <param name="search"></param>
        /// <returns></returns>
        public async Task<PagedResultDto<QuestionDto>> GetListAsync(SearchQuestionInputDto search)
        {
            var uri = _urlHelper.GetRequestHostUri();
            var knowledgeTreeQueryable = _knowledgeTreePathRepository.GetAll()
                .Where(x => search.KnowledgePointIdList.Contains(x.Descendant))
                .Select(x => x.Ancestor);
            var chapterTreePathQueryable = _chapterTreePathRepository.GetAll()
                .Where(x => x.Ancestor == search.ChapterId)
                .Select(x => x.Descendant);
            var queryable = _questionRepository.GetAll()
                .WhereIf(!search.KeyWord.IsNullOrEmpty(), x => x.ShortName.Contains(search.KeyWord))
                .WhereIf(search.QuestionTypeId.HasValue, x => x.QuestionTypeId == search.QuestionTypeId)
                .WhereIf(search.DisciplineQuestionTypeId.HasValue, x => x.DisciplineQuestionTypeId == search.DisciplineQuestionTypeId)
                .WhereIf(search.Difficulty.HasValue, x => x.Difficulty == search.Difficulty)
                .WhereIf(search.IsActive.HasValue, x => x.IsActive == search.IsActive)
                //知识点筛选
                .WhereIf(!search.IncludeAncestor && search.KnowledgePointIdList.Any(),
                    x => x.QuestionKnowledgePoints.Any(f => search.KnowledgePointIdList.Contains(f.KnowledgePointId)))
                .WhereIf(search.IncludeAncestor && search.KnowledgePointIdList.Any(),
                    x => x.QuestionKnowledgePoints.Any(f => knowledgeTreeQueryable.Contains(f.KnowledgePointId)))
                //章节筛选
                .WhereIf(search.ChapterId.HasValue && !search.IncludeChildChapter, x => x.ChapterId == search.ChapterId)
                .WhereIf(search.ChapterId.HasValue && search.IncludeChildChapter,
                    x => chapterTreePathQueryable.Any(f => f == x.ChapterId))
                //年份筛选
                .WhereIf(search.StartAriseTime.HasValue, x => x.AriseTime >= search.StartAriseTime)
                .WhereIf(search.EndAriseTime.HasValue, x => x.AriseTime <= search.EndAriseTime)
                .Where(x => x.DisciplineId == search.DisciplineId)
                //排序
                .OrderBy(x => x.QuestionTypeId)
                .ThenBy(x => x.ShortName);

            var result = await queryable.GetResultAsync(search, x => x.Adapt(new QuestionDto()
            {
                QuestionType = x.QuestionType.Name,
                Discipline = x.Discipline.Name,
                QuestionCategory = x.QuestionType.QuestionCategory,
                DisciplineQuestionType = x.DisciplineQuestionType.Name,
                Difficulty = x.Difficulty.ToString(),
                Chapter = x.Chapter.Name,
                Html = x.QuestionOrigin.Html,
                Answer = x.QuestionOrigin.Answer,
                Analysis = x.QuestionOrigin.Analysis,
                QuestionConvertState = x.QuestionOrigin.QuestionConvertState,
                KnowledgePoints = x.QuestionKnowledgePoints.Select(f => new KnowledgePointDto()
                {
                    Id = f.KnowledgePointId,
                    Name = f.KnowledgePoint.Name
                }).ToList()
            }), default(long));
            foreach (var x in result.Items)
            {
                x.Analysis = x.Analysis.ReplaceSrc();
                x.Answer = x.Answer.ReplaceSrc();
                x.Html = x.Html.ReplaceSrc();
                if (x.QuestionConvertState == Core.Tasks.Enum.QuestionConvertState.解析替换失败)
                {
                    x.ConvertFailMessage = await _questionReplaceTaskRepository
                        .GetAll()
                        .Where(r => r.QuestionId == x.Id)
                        .Select(r => r.Remark)
                        .FirstOrDefaultAsync();
                }
            }
            return result;
        }

        /// <summary>
        /// 获取试题详情(结构)
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<QuestionInfoDto> GetInfoAsync([Required] long id)
        {
            return await _questionRepository.GetAll()
                .Where(x => x.Id == id)
                .Select(x => new QuestionInfoDto()
                {
                    Html = x.QuestionOrigin.Html,
                    Id = x.Id,
                    Resolves = x.QuestionOrigin.Resolves
                        .OrderBy(f => f.ResolveType)
                        .ThenBy(f => f.Sort)
                        .Select(f => new ResolveDto()
                        {
                            Html = f.Html,
                            Id = f.Id,
                            IsAnswer = f.IsAnswer,
                            QuestionOriginId = f.QuestionOriginId,
                            ResolveType = f.ResolveType
                        }).ToList()
                }).FirstOrDefaultAsync();
        }

        /// <summary>
        /// 获取多个试题详情(结构)
        /// </summary>
        /// <param name="ids"></param>
        /// <returns></returns>
        public async Task<List<QuestionInfoDto>> GetInfosAsync([Required] List<long> ids)
        {
            return await _questionRepository.GetAll()
                .Where(x => ids.Contains(x.Id))
                .Select(x => new QuestionInfoDto()
                {
                    Html = x.QuestionOrigin.Html,
                    Id = x.Id,
                    Resolves = x.QuestionOrigin.Resolves
                        .OrderBy(f => f.ResolveType)
                        .ThenBy(f => f.Sort)
                        .Select(f => new ResolveDto()
                        {
                            Html = f.Html,
                            Id = f.Id,
                            IsAnswer = f.IsAnswer,
                            QuestionOriginId = f.QuestionOriginId,
                            ResolveType = f.ResolveType
                        }).ToList()
                }).ToListAsync();
        }

        /// <summary>
        ///  获取导入模板
        /// </summary>
        /// <param name="disciplineId"></param>
        /// <returns></returns>
        public async Task<object> GetQuestionTemplateAsync([Required] int disciplineId)
        {
            var disciplineCacheItem = await _disciplineCache.GetAsync(disciplineId);
            var disciplineQuestionTypes = await _disciplineQuestionTypeRepository.GetAll()
                .Where(x => x.DisciplineId == disciplineId)
                .ToListAsync();
            string message = $"学科:{disciplineCacheItem.Name} 题型:{string.Join(",", disciplineQuestionTypes.Select(x => x.Name))}";
            var filePath = Path.Combine(AppContext.BaseDirectory, "Doc", "试题标记说明举例.docx");
            if (!System.IO.File.Exists(filePath))
                throw new UserFriendlyException("模板文件不存在,请联系管理员");
            await using var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
            await using var steam = new MemoryStream();
            await fs.CopyToAsync(steam);
            fs.Close();
            WordOpenXml wordOpenXml = new WordOpenXml(steam, true);
            wordOpenXml.Replace("AAAAAAAAAA", message);
            wordOpenXml.Save();
            steam.Seek(0, SeekOrigin.Begin);
            return new FileContentResult(steam.GetBuffer(), FileHelper.GetContextType(".docx"))
            {
                FileDownloadName = "试题标记说明举例.docx"
            };
        }

        /// <summary>
        /// 获取试题的doc文本
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<object> GetQuestionDocAsync([Required] long id)
        {
            var question = await _questionRepository.GetAsync(id);
            var fullResolve = await _resolveRepository.SingleAsync(x => x.QuestionOriginId == question.QuestionOriginId
                                                                        && x.ResolveType == ResolveType.完整试题);

            return new PhysicalFileResult(_webHostEnvironment.SplicePath(fullResolve.XmlPath),
                FileHelper.GetContextType(".docx"))
            {
                FileDownloadName = question.ShortName + ".docx"
            };
        }

        /// <summary>
        /// 修改试题
        /// </summary>
        /// <param name="id"></param>
        /// <param name="update"></param>
        /// <returns></returns>
        public async Task UpdateAsync([Required] long id, UpdateQuestionDto update)
        {
            var question = await _questionRepository.GetAsync(id);
            await _questionManager.UpdateQuestionAsync(question,
                update.QuestionHtml,
                update.StemHtml,
                update.AnswerHtml,
                update.AnalysisHtml,
                update.Options);
        }

        /// <summary>
        /// 修改试题原始数据
        /// </summary>
        /// <param name="questionOriginId"></param>
        /// <param name="update"></param>
        /// <returns></returns>
        public async Task UpdateOriginAsync([Required] long questionOriginId, UpdateQuestionOriginDto update)
        {
            var questionOrigin = await _questionOriginRepository.GetAsync(questionOriginId);
            questionOrigin.QuestionCategory = update.QuestionCategory;
            await _questionManager.UpdateQuestionOriginAsync(questionOrigin,
                update.QuestionHtml,
                update.StemHtml,
                update.AnswerHtml,
                update.AnalysisHtml,
                update.Options);
        }

        /// <summary>
        /// 试题解析
        /// </summary>
        /// <param name="resolveDto"></param>
        /// <returns></returns>
        public async Task ResolveAsync(QuestionResolveDto resolveDto)
        {
            var fileCacheItem = await _fileCache.GetAsync(resolveDto.FileId);
            if (fileCacheItem == null)
                throw new UserFriendlyException($"文件id:{resolveDto.FileId}不存在!");
            _docHelper.Filter(fileCacheItem.Suffix);
            await _questionResolveTaskManager.CreateTaskAsync(fileCacheItem.FileName,
                resolveDto.FileId,
                resolveDto.DisciplineId,
                resolveDto.TargetId,
                resolveDto.TargetType,
                AbpSession.GetUserId());
        }

        /// <summary>
        /// 替换试题
        /// </summary>
        /// <param name="replace"></param>
        /// <returns></returns>
        public async Task ReplaceAsync(ReplaceQuestionDto replace)
        {
            var fileCacheItem = await _fileCache.GetAsync(replace.FileId);
            if (fileCacheItem == null)
                throw new UserFriendlyException($"文件id:{replace.FileId}不存在!");
            _docHelper.Filter(fileCacheItem.Suffix);
            var question = await _questionRepository.GetAsync(replace.QuestionId);
            await _taskManager.EnqueueQuestionReplaceAsync(question, replace.FileId);
        }

        /// <summary>
        /// 是否激活
        /// </summary>
        /// <param name="id"></param>
        /// <param name="isActive"></param>
        /// <returns></returns>
        [HttpPut]
        public async Task ActiveAsync([Required] long id, bool isActive)
        {
            _questionRepository.Update(id, x => x.IsActive = isActive);
            await Task.CompletedTask;
        }

        /// <summary>
        /// 是否激活
        /// </summary>
        /// <param name="idList"></param>
        /// <param name="isActive"></param>
        /// <returns></returns>
        [HttpPut]
        public async Task ActiveListAsync(List<long> idList, bool isActive)
        {
            foreach (var id in idList)
            {
                await ActiveAsync(id, isActive);
            }
        }

        /// <summary>
        /// 删除试题
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task DeleteAsync([Required] long id)
        {
            await _questionManager.DeleteAsync(id);
        }

        /// <summary>
        /// 删除试题
        /// </summary>
        /// <param name="idList"></param>
        /// <returns></returns>
        public async Task DeleteListAsync(List<long> idList)
        {
            foreach (var id in idList)
            {
                await DeleteAsync(id);
            }
        }

        //public async Task Test(long id)
        //{
        //    var question = await _questionRepository.GetAsync(id);
        //    _questionOriginManager.ProduceDoc(question);
        //}

        /// <summary>
        /// 获取试题的解析和答案
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [AbpAllowAnonymous]
        public async Task<QuestionAnalysisDto> GetAnalysisAsync([Required] long id)
        {
            var questionAnalysis = await _questionRepository.GetAll()
                .Where(x => x.Id == id)
                .Select(x => new QuestionAnalysisDto()
                {
                    QuestionId = x.Id,
                    Analysis = x.QuestionOrigin.Analysis,
                    Answer = x.QuestionOrigin.Answer
                }).FirstOrDefaultAsync();
            return questionAnalysis;
        }

        /// <summary>
        /// 获取试题列表，根据试题ID（外接口）
        /// </summary>
        /// <param name="select"></param>
        /// <returns></returns>
        [AbpAllowAnonymous]
        public async Task<List<QuestionSelectDto>> PostListAsync(SelectQuestionInputDto select)
        {
            var result = await _questionRepository.GetAll()
                .Where(x => select.QuestionIds.Contains(x.Id))
                .Select(x => new QuestionSelectDto()
                {
                    QuestionId = x.Id,
                    ShortName = x.ShortName,
                    DisciplineQuestionTypeId = x.DisciplineQuestionTypeId,
                    DisciplineQuestionTypeName = x.DisciplineQuestionType.Name,
                    Difficulty = x.Difficulty,
                    QuestionCategory = x.QuestionOrigin.QuestionCategory,
                    Analysis = x.QuestionOrigin.Analysis,
                    Answer = x.QuestionOrigin.Answer,

                    Resolve = x.QuestionOrigin.Resolves
                        .Where(f => f.ResolveType == ResolveType.题干 || f.ResolveType == ResolveType.选项 || f.ResolveType == ResolveType.题干和选项)
                        .OrderBy(f => f.ResolveType)
                        .ThenBy(f => f.Sort)
                        .Select(f => new ResolveDto()
                        {
                            Html = f.Html,
                            IsAnswer = f.IsAnswer,
                            QuestionOriginId = f.QuestionOriginId,
                            ResolveType = f.ResolveType
                        }).ToList()
                }).ToListAsync();

            return result;
        }


        /// <summary>
        /// 随机练习
        /// </summary>
        /// <param name="search"></param>
        /// <returns></returns>
        [AbpAllowAnonymous]
        public async Task<List<long>> GetListRandAsync(SearchQuestionRandInputDto search)
        {
            var disciplineIds = new List<int>();
            if (search.GradeId.HasValue)
            {
                disciplineIds = await _disciplineRepository.GetAll()
                    .Where(x => x.GradeId == search.GradeId.Value)
                    .WhereIf(search.CourseId > 0, x => x.CourseId == search.CourseId.Value)
                    .Select(x => x.Id).ToListAsync();
            }

            var sql = "select * from Questions where IsActive=1";
            if (disciplineIds.Count > 0)
            {
                sql = $"{sql} and DisciplineId in ({string.Join(",", disciplineIds)})";
            }

            if (search.ChapterId > 0)
            {
                sql = $"{sql} and ChapterId={search.ChapterId}";
            }

            if (search.KnowledgeId > 0)
            {
                sql = $"{sql} and id in (select QuestionId from questionknowledgepoints where KnowledgePointId={search.KnowledgeId})";
            }

            var limit = search.Limit > 0 ? search.Limit : 30;
            sql = $"{sql} order by RAND() LIMIT {limit}";
            var list = await _dbContextProvider.GetDbContext()
                .Questions
                .FromSqlRaw(sql)
                .Select(i => i.Id)
                .ToListAsync();
            return list;
        }
    }
}