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

namespace YGNT.QuestionLibrary.Core.Tasks.Domain
{
    public class QuestionResolveManager : DomainService, IQuestionResolveManager
    {
        private readonly ITaskManager _taskManager;
        private readonly IDisciplineQuestionTypeManager _disciplineQuestionTypeManager;
        private readonly IRepository<QuestionResolveTask, long> _repository;
        private readonly IRepository<QuestionOrigin, long> _questionOriginRepository;
        private readonly IRepository<Resolve, long> _resolveRepository;
        private readonly IRepository<ResolveTaskQuestion, long> _resolveTaskQuestionRepository;
        private readonly IRepository<QuestionType> _questionTypeRepository;
        private readonly IRepository<DisciplineQuestionType> _disciplineQuestionTypeRepository;
        private readonly IChapterCache _chapterCache;
        private readonly IKnowledgePointCache _knowledgePointCache;
        private readonly IWebHostEnvironment _webHostEnvironment;
        private readonly IFileCache _fileCache;
        public IAbpSession AbpSession { get; set; }


        public QuestionResolveManager(IRepository<QuestionResolveTask, long> repository, IFileCache fileCache, IWebHostEnvironment webHostEnvironment, IRepository<QuestionOrigin, long> questionOriginRepository, IRepository<Resolve, long> resolveRepository, IRepository<ResolveTaskQuestion, long> resolveTaskQuestionRepository, IChapterCache chapterCache, IKnowledgePointCache knowledgePointCache, ITaskManager taskManager, IRepository<QuestionType> questionTypeRepository, IRepository<DisciplineQuestionType> disciplineQuestionTypeRepository, IDisciplineQuestionTypeManager disciplineQuestionTypeManager)
        {
            _repository = repository;
            _fileCache = fileCache;
            _webHostEnvironment = webHostEnvironment;
            _questionOriginRepository = questionOriginRepository;
            _resolveRepository = resolveRepository;
            _resolveTaskQuestionRepository = resolveTaskQuestionRepository;
            _chapterCache = chapterCache;
            _knowledgePointCache = knowledgePointCache;
            _taskManager = taskManager;
            _questionTypeRepository = questionTypeRepository;
            _disciplineQuestionTypeRepository = disciplineQuestionTypeRepository;
            _disciplineQuestionTypeManager = disciplineQuestionTypeManager;
            AbpSession = NullAbpSession.Instance;
        }

        /// <summary>
        /// 创建试题解析任务
        /// </summary>
        /// <param name="name"></param>
        /// <param name="fileId"></param>
        /// <param name="disciplineId"></param>
        /// <param name="targetId"></param>
        /// <param name="targetType"></param>
        /// <param name="userId"></param>
        /// <returns></returns>
        public async Task<QuestionResolveTask> CreateTaskAsync(string name, Guid fileId, int disciplineId, long targetId,
            ImportTargetType targetType, long? userId = null)
        {
            if (!userId.HasValue)
            {
                userId = AbpSession.GetUserId();
            }
            var taskState = await CheckStateAsync(fileId);
            if (taskState != null && taskState != TaskState.失败)
                throw new UserFriendlyException($"文件{name}解析{taskState},禁止再次解析!");
            switch (targetType)
            {
                case ImportTargetType.章节:
                    var chapter = await _chapterCache.GetAsync(targetId);
                    if (chapter == null)
                        throw new UserFriendlyException($"章节id:{targetId}不存在!");
                    break;
                case ImportTargetType.知识点:
                    var knowledgePoint = await _knowledgePointCache.GetAsync(targetId);
                    if (knowledgePoint == null)
                        throw new UserFriendlyException($"知识点id:{targetId}不存在!");
                    break;
            }
            var questionResolveTask = new QuestionResolveTask()
            {
                UserId = (long)userId,
                Name = name,
                FileId = fileId,
                DisciplineId = disciplineId,
                State = TaskState.等待中,
                TargetId = targetId,
                TargetType = targetType
            };
            var taskId = await _repository.InsertAndGetIdAsync(questionResolveTask);
            await _taskManager.EnqueueQuestionResolveAsync(taskId);
            return questionResolveTask;
        }

        /// <summary>
        /// 检查是否已解析
        /// </summary>
        /// <param name="fileId"></param>
        /// <returns></returns>
        public async Task<TaskState?> CheckStateAsync(Guid fileId)
        {
            var task = await _repository.GetAll()
                .Where(x => x.FileId == fileId)
                .FirstOrDefaultAsync();
            return task?.State;
        }

        /// <summary>
        /// 解析
        /// </summary>
        /// <param name="resolveTask"></param>
        /// <returns></returns>
        [UnitOfWork]
        public void Resolve(QuestionResolveTask resolveTask)
        {
            try
            {
                resolveTask.Remark = null;
                if (resolveTask.State == TaskState.成功) return;
                var fileCacheItem = _fileCache.Get(resolveTask.FileId);
                using (var unit = UnitOfWorkManager.Begin(TransactionScopeOption.RequiresNew))
                {
                    resolveTask.State = TaskState.正在执行;
                    _repository.Update(resolveTask);
                    unit.Complete();
                }

                var questions = ResolveDoc(fileCacheItem.Path, resolveTask.DisciplineId);
                resolveTask.Remark = string.Join("\n", questions.Warning);
                foreach (var question in questions.QueItems)
                {
                    ResolveToDb(resolveTask, question);
                }
                resolveTask.State = TaskState.成功;
            }
            catch (Exception exception)
            {
                resolveTask.State = TaskState.失败;
                var errMsg = $"解析失败:{exception.GetInnerExceptionMessage()}";
                if (!resolveTask.Remark.IsNullOrEmpty())
                {
                    errMsg += "\n" + resolveTask.Remark;
                }
                resolveTask.Remark = errMsg;
            }
            _repository.Update(resolveTask);
        }

        /// <summary>
        /// 储存试题解析
        /// </summary>
        /// <param name="resolveTask"></param>
        /// <param name="question"></param>
        /// <returns></returns>
        public void ResolveToDb(QuestionResolveTask resolveTask, QueItem question)
        {
            var questionOrigin = new QuestionOrigin()
            {
                QuestionTypeName = question.TiXin,
                ShortName = question.TiStr
            };
            var id = _questionOriginRepository.InsertAndGetId(questionOrigin);
            SaveResolve(questionOrigin, question);

            var taskQuestion = new ResolveTaskQuestion()
            {
                QuestionResolveTaskId = resolveTask.Id,
                QuestionOriginId = id,
            };
            _resolveTaskQuestionRepository.Insert(taskQuestion);
        }

        /// <summary>
        /// 保存试题解析
        /// </summary>
        /// <param name="questionOrigin"></param>
        /// <param name="question"></param>
        public void SaveResolve(QuestionOrigin questionOrigin, QueItem question)
        {
            questionOrigin.Analysis = question.JieXiRange.HtmlText;
            questionOrigin.Answer = question.DaAnRange.HtmlText;
            questionOrigin.Html = question.TiRange.HtmlText;
            questionOrigin.Difficulty = (Difficulty)(question.NanYi ?? 1);
            questionOrigin.ShortName = question.TiStr;
            var sort = 0;
            //解析的选项
            foreach (var option in question.XuanXianRanges)
            {
                sort++;
                var optionResolve = new Resolve()
                {
                    Sort = sort,
                    QuestionOriginId = questionOrigin.Id,
                    Html = option.HtmlText,
                    XmlPath = GetXmlRelativePath(option.XmlFile),
                    IsAnswer = question.IsXuanXianDaAn(option),
                    ResolveType = ResolveType.选项
                };
                _resolveRepository.Insert(optionResolve);
            }
            //题干和选项
            var stemAndOption = new Resolve()
            {
                Html = question.TiRange.HtmlText,
                QuestionOriginId = questionOrigin.Id,
                XmlPath = GetXmlRelativePath(question.TiRange.XmlFile),
                ResolveType = ResolveType.题干和选项
            };
            _resolveRepository.Insert(stemAndOption);

            //解析题干
            var stemResolve = new Resolve()
            {
                Html = question.TiGanRange.HtmlText,
                QuestionOriginId = questionOrigin.Id,
                XmlPath = GetXmlRelativePath(question.TiGanRange.XmlFile),
                ResolveType = ResolveType.题干
            };
            _resolveRepository.Insert(stemResolve);
            //答案
            var answerResolve = new Resolve()
            {
                Html = question.DaAnRange.HtmlText,
                QuestionOriginId = questionOrigin.Id,
                XmlPath = GetXmlRelativePath(question.DaAnRange.XmlFile),
                ResolveType = ResolveType.答案
            };
            _resolveRepository.Insert(answerResolve);
            //解析
            var analysisResolve = new Resolve()
            {
                Html = question.JieXiRange.HtmlText,
                QuestionOriginId = questionOrigin.Id,
                XmlPath = GetXmlRelativePath(question.JieXiRange.XmlFile),
                ResolveType = ResolveType.解析
            };
            _resolveRepository.Insert(analysisResolve);

            //完整试题
            var fullResolve = new Resolve()
            {
                Html = question.TiAllRange.HtmlText,
                QuestionOriginId = questionOrigin.Id,
                XmlPath = GetXmlRelativePath(question.TiAllRange.XmlFile),
                ResolveType = ResolveType.完整试题
            };
            _resolveRepository.Insert(fullResolve);

            _questionOriginRepository.Update(questionOrigin);
        }

        /// <summary>
        /// 解析文档
        /// </summary>
        /// <param name="fileRelativePath">文档相对路径</param>
        /// <param name="disciplineId"></param>
        /// <returns></returns>
        public QueDoc ResolveDoc(string fileRelativePath, int disciplineId)
        {
            var questionTypes = _disciplineQuestionTypeManager.GetDisciplineQuestionTypes(disciplineId);
            var filePath = Path.Combine(_webHostEnvironment.WebRootPath, fileRelativePath);
            var questions = DocHelper.ResolveDoc(_webHostEnvironment, filePath, questionTypes);
            return questions;
        }

        private string GetXmlRelativePath(string xmlPath)
        {
            if (xmlPath.IsNullOrEmpty()) return string.Empty;
            return Path.GetRelativePath(_webHostEnvironment.WebRootPath, xmlPath);
        }

    }
}