using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Transactions;
using Abp.Collections.Extensions;
using Abp.Domain.Repositories;
using Abp.Domain.Services;
using Abp.Domain.Uow;
using Abp.Extensions;
using Abp.Json;
using Abp.Runtime.Session;
using Abp.UI;
using Mapster;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using QuestionSwapDoc;
using QuestionSwapDoc.SwapWordModel;
using YGNT.File.Core.Domain;
using YGNT.Infrastructure.Extension;
using YGNT.QuestionLibrary.Core.BaseData.PaperStructureSettingParam;
using YGNT.QuestionLibrary.Core.Disciplines.Cache;
using YGNT.QuestionLibrary.Core.Helper;
using YGNT.QuestionLibrary.Core.Models;
using YGNT.QuestionLibrary.Core.Papers;
using YGNT.QuestionLibrary.Core.Questions;
using YGNT.QuestionLibrary.Core.Questions.Cache;
using YGNT.QuestionLibrary.Core.Tasks.Domain.Dto;
using YGNT.QuestionLibrary.Core.Tasks.Enum;

namespace YGNT.QuestionLibrary.Core.Tasks.Domain
{
    public class PaperProduceManager : DomainService, IPaperProduceManager
    {
        private readonly IFileManager _fileManager;
        private readonly IRepository<PaperProduceResult, long> _paperProduceResultRepository;
        private readonly IRepository<PaperQuestion, long> _paperQuestionRepository;
        private readonly IRepository<Paper> _paperRepository;
        private readonly IRepository<PaperSetting> _paperSettingRepository;
        private readonly IRepository<PaperProduceTask, long> _paperProduceTaskRepository;
        private readonly IDisciplineQuestionTypeCache _disciplineQuestionTypeCache;
        private readonly IQuestionTypeCache _questionTypeCache;
        private readonly IRepository<Resolve, long> _resolveRepository;
        private readonly IWebHostEnvironment _webHostEnvironment;
        private readonly ITaskManager _taskManager;
        private readonly IDocHelper _docHelper;

        public IAbpSession AbpSession { get; set; }

        public PaperProduceManager(IRepository<PaperQuestion, long> paperQuestionRepository, IRepository<PaperSetting> paperSettingRepository, IDisciplineQuestionTypeCache disciplineQuestionTypeCache, IQuestionTypeCache questionTypeCache, IRepository<Resolve, long> resolveRepository, IWebHostEnvironment webHostEnvironment, IFileManager fileManager, IRepository<PaperProduceTask, long> paperProduceTaskRepository, ITaskManager taskManager, IRepository<Paper> paperRepository, IDocHelper docHelper, IRepository<PaperProduceResult, long> paperProduceResultRepository)
        {
            _paperQuestionRepository = paperQuestionRepository;
            _paperSettingRepository = paperSettingRepository;
            _disciplineQuestionTypeCache = disciplineQuestionTypeCache;
            _questionTypeCache = questionTypeCache;
            _resolveRepository = resolveRepository;
            _webHostEnvironment = webHostEnvironment;
            _fileManager = fileManager;
            _paperProduceTaskRepository = paperProduceTaskRepository;
            _taskManager = taskManager;
            _paperRepository = paperRepository;
            _docHelper = docHelper;
            _paperProduceResultRepository = paperProduceResultRepository;
            AbpSession = NullAbpSession.Instance;
        }

        /// <summary>
        /// 创建试卷生成任务
        /// </summary>
        /// <param name="paper"></param>
        /// <param name="paperProduce"></param>
        /// <param name="userId"></param>
        /// <returns></returns>
        public async Task<PaperProduceTask> CreateTaskAsync(Paper paper,
            PaperProduceDto paperProduce,
            long? userId = null)
        {
            if (!userId.HasValue)
            {
                userId = AbpSession.GetUserId();
            }

            if (paper.Status == Status.等待生成 || paper.Status == Status.生成中)
                throw new UserFriendlyException($"试卷正在{paper.Status}!");
            paper.Status = Status.等待生成;

            var produceTask = new PaperProduceTask()
            {
                PaperId = paper.Id,
                Name = $"试卷{paper.Name}生成任务",
                UserId = (long)userId,
                TaskState = TaskState.等待中
            };
            await _paperProduceTaskRepository.InsertAndGetIdAsync(produceTask);
            await _taskManager.EnqueuePaperProduceAsync(produceTask.Id, paperProduce);
            return produceTask;
        }

        /// <summary>
        /// 生成试卷
        /// </summary>
        /// <param name="produceTask"></param>
        /// <param name="paperProduce"></param>
        [UnitOfWork]
        public void Produce(PaperProduceTask produceTask, PaperProduceDto paperProduce)
        {
            try
            {
                produceTask.Remark = null;
                if (produceTask.TaskState == TaskState.成功) return;
                var paper = _paperRepository.Get(produceTask.PaperId);
                using (var unit = UnitOfWorkManager.Begin(TransactionScopeOption.RequiresNew))
                {
                    paper.Status = Status.生成中;
                    produceTask.TaskState = TaskState.正在执行;
                    _paperProduceTaskRepository.Update(produceTask);
                    _paperRepository.Update(paper);
                    unit.Complete();
                }
                Produce(paper, produceTask, paperProduce);
                produceTask.TaskState = TaskState.成功;
                paper.Status = Status.已生成;
            }
            catch (Exception exception)
            {
                produceTask.TaskState = TaskState.失败;
                var errMsg = $"解析失败:{exception.Message}";
                if (!produceTask.Remark.IsNullOrEmpty())
                {
                    errMsg += "\n" + produceTask.Remark;
                }
                produceTask.Remark = errMsg;
            }
            _paperProduceTaskRepository.Update(produceTask);
        }

        /// <summary>
        /// 生成试卷
        /// </summary>
        /// <param name="paper"></param>
        /// <param name="produceTask"></param>
        /// <param name="paperProduce"></param>
        /// <returns></returns>
        [UnitOfWork]
        public Paper Produce(Paper paper, PaperProduceTask produceTask, PaperProduceDto paperProduce)
        {
            var swapQueConfig = CreateQueConfig(paper.Id);
            swapQueConfig.ZhiZhangDaXiao = paperProduce.PaperSize.To<int>().To<PaperQue>();
            var answerSheetConfig = GetAnswerSheetConfig(paper);
            var swapDaTiKaConfig = answerSheetConfig.Item1;
            var questionTypeStyles = answerSheetConfig.Item2;
            var swapTiXins = CreateSwapTiXins(paper.Id);
            foreach (var swapTiXin in swapTiXins.TiXinList)
            {
                var questionTypeStyle = questionTypeStyles.FirstOrDefault(x =>
                      _disciplineQuestionTypeCache.Get(x.DisciplineQuestionTypeId)?.Name == swapTiXin.TiXinStr);
                if (questionTypeStyle != null)
                    swapTiXin.DaTiKaType = questionTypeStyle.AnswerSheetStyle.To<int>().To<SwapTiXinDaTiKaType>();
            }
            var producePath = Path.Combine(PathHelper.GetBaseProducePath(), Guid.NewGuid().ToString());
            var results = SwapWord.CreateQue(_webHostEnvironment.SplicePath(producePath),
                swapQueConfig,
                new SwapDaAnConfig() { IsInQue = false },
                swapDaTiKaConfig,
                swapTiXins);
            var produceResult = CreateProduceResult(paper, produceTask, results);
            produceResult.ProducePath = producePath;
            _paperProduceResultRepository.InsertAndGetId(produceResult);
            return paper;
        }


        /// <summary>
        /// 记录生成的试卷地址
        /// </summary>
        /// <param name="produceTask"></param>
        /// <param name="results"></param>
        /// <param name="paper"></param>
        private PaperProduceResult CreateProduceResult(Paper paper, PaperProduceTask produceTask, List<SwapWordCreateQueResult> results)
        {
            PaperProduceResult produceResult = new PaperProduceResult()
            {
                PaperId = paper.Id,
                PaperProduceTaskId = produceTask.Id
            };
            using var unitOfWork = UnitOfWorkManager.Begin(TransactionScopeOption.RequiresNew);
            produceResult.FileId = null;
            produceResult.AnswerFileId = null;
            produceResult.AnswerSheetFileId = null;
            produceResult.FullPaperFileId = null;
            foreach (var swapWordResult in results)
            {
                switch (swapWordResult.resultType)
                {
                    case SwapWordResultType.试卷:
                        produceResult.FileId = _fileManager.Create(swapWordResult.Path, $"{paper.Name}{swapWordResult.resultType}").Id;
                        break;
                    case SwapWordResultType.试卷和答案解析:
                        produceResult.FullPaperFileId = _fileManager.Create(swapWordResult.Path, $"{paper.Name}{swapWordResult.resultType}").Id;
                        break;
                    case SwapWordResultType.答案解析:
                        produceResult.AnswerFileId = _fileManager.Create(swapWordResult.Path, $"{paper.Name}{swapWordResult.resultType}").Id;
                        break;
                    case SwapWordResultType.答题卡:
                        produceResult.AnswerSheetFileId = _fileManager.Create(swapWordResult.Path, $"{paper.Name}{swapWordResult.resultType}").Id;
                        break;
                }
            }
            unitOfWork.Complete();
            return produceResult;
        }

        #region 获取参数配置

        /// <summary>
        /// 创建答题卡配置
        /// </summary>
        /// <param name="paper"></param>
        /// <returns></returns>
        private Tuple<SwapDaTiKaConfig, List<QuestionTypeStyle>> GetAnswerSheetConfig(Paper paper)
        {
            SwapDaTiKaConfig swapDaTiKaConfig = null;
            //创建答题卡配置
            var setting = _paperSettingRepository
                .GetAll()
                .Where(x => x.PaperId == paper.Id)
                .FirstOrDefault(x => x.PaperStructureSettingCode == PaperStructureSettingConsts.答题卡);
            var questionTypeStyles = new List<QuestionTypeStyle>();
            if (setting != null)
            {
                swapDaTiKaConfig = new SwapDaTiKaConfig();
                var answerSheet = setting.Param.FromJsonString<AnswerSheet>();
                swapDaTiKaConfig.ZhuBiaoTi = answerSheet.MainTitle;
                swapDaTiKaConfig.FuBiaoTi = answerSheet.SubTitle;
                swapDaTiKaConfig.DaTiKaType = answerSheet.AnswerSheetStyle.To<int>().To<DaTiKaStyle>();
                swapDaTiKaConfig.ZhiZhangDaXiao = answerSheet.AnswerSheetSize.To<int>().To<PaperDaTiKa>();
                swapDaTiKaConfig.ZhuYiShiXian = answerSheet.Notice;
                swapDaTiKaConfig.ZhunKaoZhenHaoShu = answerSheet.CandidateNoCount;
                swapDaTiKaConfig.IsErWeiMa = answerSheet.IsShowQrCode;
                questionTypeStyles = answerSheet.QuestionTypeStyles;
            }
            var result = new Tuple<SwapDaTiKaConfig, List<QuestionTypeStyle>>(swapDaTiKaConfig, questionTypeStyles);
            return result;
        }

        /// <summary>
        /// 创建试卷配置参数
        /// </summary>
        /// <param name="paperId"></param>
        /// <returns></returns>
        private SwapQueConfig CreateQueConfig(long paperId)
        {
            var swapQueConfig = new SwapQueConfig();
            var settings = _paperSettingRepository
                .GetAllList(x => x.PaperId == paperId)
                .ToDictionary(x => x.PaperStructureSettingCode);
            foreach (var setting in settings)
            {
                switch (setting.Key)
                {
                    case PaperStructureSettingConsts.主标题:
                        var mainTitle = setting.Value.Param.DeserializeObject<MainTitle>();
                        swapQueConfig.ZhuBiaoTi = mainTitle.Name;
                        break;
                    case PaperStructureSettingConsts.分卷:
                        var subsection = setting.Value.Param.DeserializeObject<Subsection>();
                        swapQueConfig.IsFenJuan = subsection.Enable;
                        break;
                    case PaperStructureSettingConsts.装订线:
                        var gutter = setting.Value.Param.DeserializeObject<Gutter>();
                        swapQueConfig.ZhuanDinXian = gutter.Adapt<ZhuanDinXian>();
                        break;
                    case PaperStructureSettingConsts.副标题:
                        var subTitle = setting.Value.Param.DeserializeObject<SubTitle>();
                        swapQueConfig.FuBiaoTi = subTitle.Name;
                        break;
                    case PaperStructureSettingConsts.考试信息栏:
                        var testInformationBar = setting.Value.Param.DeserializeObject<TestInformationBar>();
                        swapQueConfig.ShiTiXinXi = testInformationBar.Name;
                        break;
                    case PaperStructureSettingConsts.考生信息栏:
                        var candidateInformationBar = setting.Value.Param.DeserializeObject<CandidateInformationBar>();
                        swapQueConfig.KaoShengShuRu = candidateInformationBar.Name;
                        break;
                    case PaperStructureSettingConsts.注意事项:
                        var attentionNotice = setting.Value.Param.DeserializeObject<AttentionNotice>();
                        swapQueConfig.ZhuYiShiXian = attentionNotice.Notice;
                        break;
                }
            }
            return swapQueConfig;
        }

        /// <summary>
        /// 创建试题集合
        /// </summary>
        /// <param name="paperId"></param>
        /// <returns></returns>
        private SwapTiXins CreateSwapTiXins(long paperId)
        {
            var questions = _paperQuestionRepository.GetAll()
                .Include(x => x.Question)
                .ThenInclude(x => x.QuestionOrigin)
                .Where(x => x.PaperId == paperId)
                .OrderBy(x => x.QuestionTypeSort)
                .ThenBy(x => x.Sort)
                .ToList();
            var dic = new Dictionary<int, QuestionTypeTitle>();
            //创建答题卡配置
            var setting = _paperSettingRepository
                .GetAll()
                .Where(x => x.PaperId == paperId)
                .FirstOrDefault(x => x.PaperStructureSettingCode == PaperStructureSettingConsts.题型标题);
            if (setting != null)
            {
                var questionTypeTitles = setting.Param.DeserializeObject<List<QuestionTypeTitle>>();
                foreach (var questionTypeTitle in questionTypeTitles)
                {
                    dic.AddIfNotContains(
                        new KeyValuePair<int, QuestionTypeTitle>(questionTypeTitle.DisciplineQuestionTypeId,
                            questionTypeTitle));
                }
            }
            var questionGroupByTypes = questions.GroupBy(x => x.DisciplineQuestionTypeId);
            var swapTiXins = new SwapTiXins();
            foreach (var questionGroupByType in questionGroupByTypes)
            {
                var disciplineQuestionType = _disciplineQuestionTypeCache.Get(questionGroupByType.Key);
                var questionTypeTitle = dic.GetValueOrDefault(questionGroupByType.Key);
                string questionTypeName = questionTypeTitle == null ? disciplineQuestionType.Name : questionTypeTitle.Title;
                var swapTiXin = new SwapTiXin()
                {
                    TiXinStr = questionTypeName,
                    IsXuanZhe = _docHelper.IsChoiceQuestion(questionGroupByType.Key),
                    IsPanDuan = _docHelper.IsTrueFalseQuestion(questionGroupByType.Key)
                };
                foreach (var paperQuestion in questionGroupByType.ToList())
                {
                    var questionOriginId = paperQuestion.Question.QuestionOriginId;
                    var swapQue = new SwapQue()
                    {
                        ImgLinkLeftPath = _webHostEnvironment.WebRootPath,
                        Fen = paperQuestion.SingleScore
                    };
                    if (swapTiXin.IsXuanZhe)
                    {
                        var count = _resolveRepository
                            .GetAll()
                            .Where(x => x.QuestionOriginId == questionOriginId)
                            .Count(x => x.ResolveType == ResolveType.选项);
                        swapQue.XuanZheTiCount = count;
                    }

                    swapQue.TiRange = GetSwapQueContent(questionOriginId, ResolveType.题干和选项);
                    swapQue.DaAnRange = GetSwapQueContent(questionOriginId, ResolveType.答案);
                    swapQue.JieXiRange = GetSwapQueContent(questionOriginId, ResolveType.解析);
                    swapTiXin.QueList.Add(swapQue);
                }

                swapTiXins.TiXinList.Add(swapTiXin);
            }
            return swapTiXins;
        }

        private SwapQueContent GetSwapQueContent(long questionOriginId, ResolveType resolveType)
        {
            return _resolveRepository
                .GetAll()
                .Where(x => x.QuestionOriginId == questionOriginId)
                .Where(x => x.ResolveType == resolveType)
                .Select(x => new SwapQueContent()
                {
                    XmlFile = x.IsModified || x.XmlPath.IsNullOrEmpty() ? null : Path.GetFullPath(x.XmlPath, _webHostEnvironment.WebRootPath),
                    HtmlText = x.Html
                }).FirstOrDefault() ?? new SwapQueContent();
        }
        #endregion



    }
}