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

namespace YGNT.QuestionLibrary.Tasks
{
    /// <summary>
    /// 试题解析任务管理
    /// </summary>
    [AbpAuthorize()]
    public class QuestionResolveTaskAppService : IQuestionResolveTaskAppService
    {
        private readonly IUriHelper _urlHelper;
        private readonly IFileCache _fileCache;
        private readonly ITaskManager _taskManager;
        private readonly IWebHostEnvironment _hostEnvironment;
        private readonly IRepository<QuestionResolveTask, long> _questionResolveTaskRepository;
        private readonly IRepository<ResolveTaskQuestion, long> _resolveTaskQuestionRepository;
        private readonly IRepository<QuestionOrigin, long> _questionOriginRepository;
        private readonly IRepository<DisciplineQuestionType> _disciplineQuestionTypeRepository;
        private readonly IKnowledgePointTreePathManager _knowledgePointTreePathManager;
        private readonly IChapterTreePathManager _chapterTreePathManager;
        private readonly IQuestionManager _questionManager;
        private readonly IDisciplineQuestionTypeCache _disciplineQuestionTypeCache;
        private readonly IQuestionTypeCache _questionTypeCache;
        private readonly IQuestionResolveManager _questionResolveManager;

        public IAbpSession AbpSession { get; set; }

        public QuestionResolveTaskAppService(IRepository<QuestionResolveTask, long> questionResolveTaskRepository, IKnowledgePointTreePathManager knowledgePointTreePathManager, IChapterTreePathManager chapterTreePathManager, IRepository<QuestionOrigin, long> questionOriginRepository, IRepository<ResolveTaskQuestion, long> resolveTaskQuestionRepository, IRepository<DisciplineQuestionType> disciplineQuestionTypeRepository, IDisciplineQuestionTypeCache disciplineQuestionTypeCache, IQuestionManager questionManager, IFileCache fileCache, IWebHostEnvironment hostEnvironment, ITaskManager taskManager, IQuestionTypeCache questionTypeCache, IQuestionResolveManager questionResolveManager, IUriHelper urlHelper)
        {
            _questionResolveTaskRepository = questionResolveTaskRepository;
            _knowledgePointTreePathManager = knowledgePointTreePathManager;
            _chapterTreePathManager = chapterTreePathManager;
            _questionOriginRepository = questionOriginRepository;
            _resolveTaskQuestionRepository = resolveTaskQuestionRepository;
            _disciplineQuestionTypeRepository = disciplineQuestionTypeRepository;
            _disciplineQuestionTypeCache = disciplineQuestionTypeCache;
            _questionManager = questionManager;
            _fileCache = fileCache;
            _hostEnvironment = hostEnvironment;
            _taskManager = taskManager;
            _questionTypeCache = questionTypeCache;
            _questionResolveManager = questionResolveManager;
            _urlHelper = urlHelper;
            AbpSession = NullAbpSession.Instance;
        }

        /// <summary>
        /// 查询用户试题解析任务列表
        /// </summary>
        /// <param name="search"></param>
        /// <returns></returns>
        public async Task<PagedResultDto<UserQuestionResoleTaskDto>> GetListAsync(SearchPaperProduceTaskInputDto search)
        {
            var userId = AbpSession.GetUserId();
            var queryable = _questionResolveTaskRepository.GetAll()
                .WhereIf(search.State.HasValue, x => x.State == search.State)
                .WhereIf(search.DisciplineId.HasValue, x => x.DisciplineId == search.DisciplineId)
                .WhereIf(!search.KeyWord.IsNullOrEmpty(), x => x.Name.Contains(search.KeyWord))
                .Where(x => x.UserId == userId)
                .OrderBy(x => x.State)
                .ThenByDescending(x => x.CreationTime);

            var result = await queryable.GetResultAsync(search,
                x => x.Adapt(new UserQuestionResoleTaskDto()
                {
                    Discipline = x.Discipline.Name,
                    EducationCategory = x.Discipline.Grade.Name,
                }), default(long));

            foreach (var resoleTaskDto in result.Items)
            {
                switch (resoleTaskDto.TargetType)
                {
                    case ImportTargetType.知识点:
                        resoleTaskDto.Target = await _knowledgePointTreePathManager.GetNamePathAsync(resoleTaskDto.TargetId);
                        break;
                    case ImportTargetType.章节:
                        resoleTaskDto.Target = await _chapterTreePathManager.GetNamePathAsync(resoleTaskDto.TargetId);
                        break;
                }
            }
            return result;
        }

        /// <summary>
        /// 获取任务所解析完成的试题
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<List<ResoleTaskQuestionDto>> GetTaskQuestionsAsync([Required] long id)
        {
            var uri = _urlHelper.GetRequestHostUri();
            var task = await _questionResolveTaskRepository.GetAsync(id);
            var questions = await _resolveTaskQuestionRepository.GetAll()
                 .Where(x => x.QuestionResolveTaskId == id)
                 .GroupJoin(_disciplineQuestionTypeRepository.GetAll()
                         .Where(f => f.DisciplineId == task.DisciplineId),
                     x => x.QuestionOrigin.QuestionTypeName,
                     x => x.Name,
                     (question, type) => new { type, question })
                 .SelectMany(x => x.type.DefaultIfEmpty(),
                     (question, type) => new ResoleTaskQuestionDto()
                     {
                         Id = question.question.Id,
                         QuestionOriginId = question.question.QuestionOriginId,
                         DisciplineQuestionTypeName = type != null ? type.Name : "",
                         DisciplineQuestionTypeId = type != null ? type.Id : (int?)null,
                         QuestionTypeId = type != null ? type.QuestionTypeId : (int?)null,
                         ResoleQuestionTypeName = question.question.QuestionOrigin.QuestionTypeName,
                         Difficulty = question.question.QuestionOrigin.Difficulty,
                         ShortName = question.question.QuestionOrigin.ShortName,
                         Html = question.question.QuestionOrigin.Html,
                         Answer = question.question.QuestionOrigin.Answer,
                         Analysis = question.question.QuestionOrigin.Analysis,
                     }).ToListAsync();
            questions.ForEach(x =>
            {
                x.Analysis = x.Analysis.ReplaceSrc();
                x.Answer = x.Answer.ReplaceSrc();
                x.Html = x.Html.ReplaceSrc();
            });
            return questions;
        }

        /// <summary>
        /// 获取试题详情(结构)
        /// </summary>
        /// <param name="resoleTaskQuestionId"></param>
        /// <returns></returns>
        public async Task<QuestionInfoDto> GetInfoAsync([Required] long resoleTaskQuestionId)
        {
            return await _resolveTaskQuestionRepository.GetAll()
                  .Where(x => x.Id == resoleTaskQuestionId)
                  .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="id"></param>
        /// <returns></returns>
        public async Task<object> GetTaskFileAsync([Required] long id)
        {
            var task = await _questionResolveTaskRepository.GetAsync(id);
            var fileCache = await _fileCache.GetAsync(task.FileId);
            var filePath = Path.Combine(_hostEnvironment.WebRootPath, fileCache.Path);
            if (!System.IO.File.Exists(filePath))
            {
                throw new UserFriendlyException("文件不存在!");
            }
            var fileBytes = await System.IO.File.ReadAllBytesAsync(filePath);
            return new FileContentResult(fileBytes, FileHelper.GetContextType(fileCache.Suffix))
            {
                FileDownloadName = fileCache.FileName + fileCache.Suffix,
            };
        }

        /// <summary>
        /// 重试
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task ReTryAsync([Required] long id)
        {
            var task = await _questionResolveTaskRepository.GetAsync(id);
            if (task.State != TaskState.失败)
                throw new UserFriendlyException($"任务{task.State},无法重试!");
            task.State = TaskState.等待中;
            task.Remark = "重新尝试解析,等待队列中...";
            //_questionResolveManager.Resolve(task);
            await _taskManager.EnqueueQuestionResolveAsync(task.Id);
        }

        /// <summary>
        /// 确认导入任务解析试题
        /// </summary>
        /// <param name="taskId">任务id</param>
        /// <param name="affirms"></param>
        /// <returns></returns>
        public async Task AffirmTaskQuestionAsync([Required] long taskId, List<AffirmTaskQuestionDto> affirms)
        {
            var task = await _questionResolveTaskRepository.GetAsync(taskId);
            if (task.State != TaskState.成功)
                throw new UserFriendlyException($"当前任务解析{task.State},无法导入试题!");
            long? chapterId = task.TargetType == ImportTargetType.章节 ? task.TargetId : (long?)null;
            //var knowledgePointList = task.TargetType == ImportTargetType.知识点 ? new List<long>() { task.TargetId } : null;
            foreach (var affirm in affirms)
            {
                var taskQuestion = await _resolveTaskQuestionRepository.GetAsync(affirm.ResoleTaskQuestionId);
                if (taskQuestion.QuestionResolveTaskId != taskId)
                    throw new UserFriendlyException($"任务试题id:{taskQuestion.Id}所属任务不匹配!");
                var disciplineQuestionType = await _disciplineQuestionTypeCache.GetAsync(affirm.DisciplineQuestionTypeId);
                if (disciplineQuestionType == null || disciplineQuestionType.DisciplineId != task.DisciplineId)
                    throw new UserFriendlyException($"细分题型输入有误!");
                var questionOrigin = await _questionOriginRepository.GetAll().Where(x => x.Id == taskQuestion.QuestionOriginId)
                    .FirstOrDefaultAsync();

                if (!chapterId.HasValue && !affirm.KnowledgePointList.Any())
                {
                    throw new UserFriendlyException($"试题:{questionOrigin.ShortName}\r至少需要一个章节或知识点");
                }

                var question = await _questionManager.CreateAsync(questionOrigin.ShortName,
                    task.DisciplineId,
                    affirm.DisciplineQuestionTypeId,
                    affirm.Difficulty,
                    taskQuestion.QuestionOriginId,
                    chapterId,
                    affirm.KnowledgePointList,
                    affirm.AriseTime,
                    isActive: true);
                //var questionType = await _questionTypeCache.GetAsync(disciplineQuestionType.QuestionTypeId);
                questionOrigin.QuestionTypeName = disciplineQuestionType.Name;
                var questionType = await _questionTypeCache.GetAsync(disciplineQuestionType.QuestionTypeId);
                questionOrigin.QuestionCategory = questionType.QuestionCategory;
            }
            task.State = TaskState.导入完成;
        }

        /// <summary>
        /// 删除任务
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task DeleteAsync([Required] long id)
        {
            var userId = AbpSession.GetUserId();
            var task = await _questionResolveTaskRepository.GetAsync(id);
            if (task.UserId != userId)
                throw new UserFriendlyException("禁止操作其他人的任务");
            if (task.State == TaskState.正在执行)
                throw new UserFriendlyException("禁止删除正在执行的任务");
            await _questionResolveTaskRepository.DeleteAsync(task);
        }
    }
}