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.EntityFrameworkCore;
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using System.Transactions;
using YGNT.Infrastructure.Extension;
using YGNT.QuestionLibrary.Core.Papers;
using YGNT.QuestionLibrary.Core.Papers.Domain;
using YGNT.QuestionLibrary.Core.Papers.Domain.AutoPaper;
using YGNT.QuestionLibrary.Core.Tasks.Domain;
using YGNT.QuestionLibrary.Core.Tasks.Domain.Dto;
using YGNT.QuestionLibrary.Papers.Dto;
using Z.EntityFramework.Plus;

namespace YGNT.QuestionLibrary.Papers
{
    /// <summary>
    /// 试卷管理
    /// </summary>
    [AbpAuthorize()]
    public class PaperAppService : IPaperAppService
    {
        public IAbpSession AbpSession { get; set; }

        private readonly IPaperProduceManager _paperProduceTaskManager;
        private readonly IRepository<File.Core.File, Guid> _fileRepository;
        private readonly IUnitOfWorkManager _unitOfWorkManager;
        private readonly IRepository<Paper> _paperRepository;
        private readonly IPaperManager _paperManager;
        private readonly IPaperQuestionManager _paperQuestionManager;
        private readonly IAutoPaperManagerFactory _autoPaperManagerFactory;
        private readonly IRepository<PaperQuestion, long> _paperQuestionRepository;
        private readonly IRepository<PaperProduceResult, long> _paperProduceResultRepository;
        private readonly IPaperSettingAppService _paperSettingAppService;
        private readonly IPaperQuestionAppService _paperQuestionAppService;

        public PaperAppService(IRepository<Paper> paperRepository, IPaperManager paperManager, IAutoPaperManagerFactory autoPaperManagerFactory, IRepository<PaperQuestion, long> paperQuestionRepository, IUnitOfWorkManager unitOfWorkManager, IPaperQuestionAppService paperQuestionAppService, IPaperSettingAppService paperSettingAppService, IPaperQuestionManager paperQuestionManager, IPaperProduceManager paperProduceTaskManager, IRepository<PaperProduceResult, long> paperProduceResultRepository, IRepository<File.Core.File, Guid> fileRepository)
        {
            _paperRepository = paperRepository;
            AbpSession = NullAbpSession.Instance;
            _paperManager = paperManager;
            _autoPaperManagerFactory = autoPaperManagerFactory;
            _paperQuestionRepository = paperQuestionRepository;
            _unitOfWorkManager = unitOfWorkManager;
            _paperQuestionAppService = paperQuestionAppService;
            _paperSettingAppService = paperSettingAppService;
            _paperQuestionManager = paperQuestionManager;
            _paperProduceTaskManager = paperProduceTaskManager;
            _paperProduceResultRepository = paperProduceResultRepository;
            _fileRepository = fileRepository;
        }

        /// <summary>
        /// 查询试卷列表
        /// </summary>
        /// <param name="search"></param>
        /// <returns></returns>
        [AbpAuthorize()]
        public async Task<PagedResultDto<PaperDto>> GetListAsync(SearchPaperInputDto search)
        {
            var userId = AbpSession.GetUserId();
            var queryable = _paperRepository.GetAll()
                .WhereIf(search.Disciplines != null,
                    x => x.PaperDisciplines.Any(f => search.Disciplines.Contains(f.DisciplineId)))
                .WhereIf(search.Status.HasValue, x => x.Status == search.Status)
                .WhereIf(!search.KeyWord.IsNullOrEmpty(), x => x.Name.Contains(search.KeyWord))
                .Where(x => x.CreatorUserId == userId)
                .OrderBy(x => x.Name);

            var result = new PagedResultDto<PaperDto>()
            {
                TotalCount = await queryable.CountAsync(),
                Items = await queryable.PageBy(search.SkipCount, search.MaxResultCount)
                    .Select(x => x.Adapt(new PaperDto()
                    {
                        PaperQuestionCount = x.PaperQuestions.Count,
                        PaperDisciplines = string.Join(',', x.PaperDisciplines.Select(f => f.Discipline.Name)),
                        EducationCategorys = string.Join(',', x.PaperDisciplines.Select(f => f.Discipline.Grade.Name).Distinct()),
                    })).ToListAsync()
            };
            return result;
        }

        /// <summary>
        /// 查询试卷生成结果
        /// </summary>
        /// <param name="id"></param>
        /// <param name="search"></param>
        /// <returns></returns>
        public async Task<PagedResultDto<PaperProduceResultDto>> GetPaperProduceResultAsync([Required] int id,
            PagedResultRequestDto search)
        {
            var queryable = _paperProduceResultRepository.GetAll()
                .Where(x => x.PaperId == id)
                .OrderByDescending(x => x.CreationTime);
            var result = await queryable.GetResultAsync(search, x => x.Adapt(new PaperProduceResultDto()), default(long));
            return result;
        }

        /// <summary>
        /// 查询试卷最新生成结果
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<PaperProduceResultDto> GetPaperProduceResultOneAsync([Required] int id)
        {
            var queryable = await _paperProduceResultRepository.GetAll()
                .Where(x => x.PaperId == id)
                .OrderByDescending(x => x.CreationTime)
                .FirstOrDefaultAsync()
                ;
            return queryable.Adapt<PaperProduceResultDto>();
        }

        /// <summary>
        /// 获取试卷详情
        /// </summary>
        /// <param name="paperId">试卷id</param>
        /// <returns></returns>
        public async Task<PaperInfoDto> GetPaperInfoAsync([Required] int paperId)
        {
            var paper = await _paperRepository.GetAll()
                .Include(x => x.PaperQuestions)
                .Include(x => x.PaperDisciplines)
                .ThenInclude(x => x.Discipline)
                .FirstOrDefaultAsync(x => x.Id == paperId);
            if (paper == null) throw new UserFriendlyException($"试卷id:{paperId}不存在");

            var result = await GetPaperInfoAsync(paper);
            return result;
        }

        /// <summary>
        /// 获取试卷详情
        /// </summary>
        /// <param name="paper"></param>
        /// <returns></returns>
        private async Task<PaperInfoDto> GetPaperInfoAsync(Paper paper)
        {
            var result = paper.Adapt<PaperInfoDto>();
            result.PaperDisciplines = paper.PaperDisciplines.Select(x => x.Discipline.Name).ToList();
            result.PaperQuestions = await _paperQuestionAppService.GetListAsync(paper.Id);
            result.PaperSetting = await _paperSettingAppService.GetPaperSettingsAsync(paper.Id);

            var chapters = await _paperQuestionManager.GetPaperQuestionChapterAsync(paper);
            result.PaperChapters = string.Join(",", chapters.Select(x => x.Name));

            var knowledgePoints = await _paperQuestionManager.GetPaperQuestionKnowledgePointAsync(paper);
            result.PaperKnowledgePoints = string.Join(",", knowledgePoints.Select(x => x.Name));

            return result;
        }

        /// <summary>
        /// 创建新试卷
        /// </summary>
        /// <param name="create"></param>
        /// <returns></returns>
        public async Task<int> CreateAsync(CreatePaperDto create)
        {
            var paper = await _paperManager.CreateAsync(create.Name,
                create.ExamTime,
                create.Disciplines,
                create.Remark);
            foreach (var paperQuestion in create.PaperQuestions)
            {
                await _paperManager.AddQuestion(paper,
                    paperQuestion.QuestionId,
                    paperQuestion.DisciplineQuestionTypeId,
                    paperQuestion.SingleScore);
            }
            return paper.Id;
        }

        /// <summary>
        /// 自动组卷(调用后会清除当前试卷原有试题,重新组卷可用此接口)
        /// </summary>
        /// <param name="paperId"></param>
        /// <param name="autoPaperSetting"></param>
        /// <returns></returns>
        public async Task<PaperInfoDto> AutoPaperAsync([Required] int paperId, AutoPaperSettingDto autoPaperSetting)
        {
            await _paperQuestionRepository.DeleteAsync(x => x.PaperId == paperId);
            await _unitOfWorkManager.Current.SaveChangesAsync();
            var autoPaperManager = _autoPaperManagerFactory.Create(autoPaperSetting.PaperCompositionModel);
            var paper = await _paperRepository.GetAll()
                .Include(x => x.PaperQuestions)
                .Include(x => x.PaperDisciplines)
                .ThenInclude(x => x.Discipline)
                .FirstOrDefaultAsync(x => x.Id == paperId);
            if (paper == null) throw new UserFriendlyException($"试卷id:{paperId}不存在");
            paper.TotalScore = autoPaperSetting.TotalScore;
            foreach (var disciplineSetting in autoPaperSetting.AutoPaperDisciplineSettings)
            {
                foreach (var autoPaperTypeSetting in disciplineSetting.AutoPaperTypeSettings)
                {
                    foreach (var difficultySettingDto in autoPaperTypeSetting.DifficultySettings)
                    {
                        await autoPaperManager.AddRandomQuestionAsync(paper,
                            autoPaperTypeSetting.Id,
                            autoPaperTypeSetting.DisciplineQuestionTypeId,
                            difficultySettingDto.Count,
                            autoPaperTypeSetting.Score,
                            difficultySettingDto.Difficulty);
                    }
                }
            }
            await _paperManager.SortPaperAsync(paper);

            var result = await GetPaperInfoAsync(paper);
            return result;
        }

        /// <summary>
        /// 生成试卷
        /// </summary>
        /// <param name="paperId"></param>
        /// <param name="paperProduce"></param>
        /// <returns></returns>
        public async Task ProduceAsync([Required] int paperId, [Required] PaperProduceDto paperProduce)
        {
            var paper = await _paperRepository.GetAsync(paperId);
            await _paperProduceTaskManager.CreateTaskAsync(paper, paperProduce, AbpSession.GetUserId());
        }

        /// <summary>
        /// 保存试卷
        /// </summary>
        /// <param name="save"></param>
        /// <returns></returns>
        public async Task SaveAsync(PaperSaveDto save)
        {
            if (save.PassingScore > save.TotalScore)
                throw new UserFriendlyException("合格分数不能大于试卷总分!");
            var paper = await _paperRepository.GetAll()
                .Include(x => x.PaperQuestions)
                .Where(x => x.Id == save.Id)
                .FirstOrDefaultAsync();
            if (paper == null) throw new UserFriendlyException("试卷不存在!");
            paper.TotalScore = save.TotalScore;
            paper.PassingScore = save.PassingScore;
            var totalScore = 0f;
            foreach (var paperQuestion in paper.PaperQuestions)
            {
                var saveQuestion = save.PaperQuestions?.FirstOrDefault(x => x.PaperQuestionId == paperQuestion.Id);
                if (saveQuestion != null)
                {
                    paperQuestion.SingleScore = saveQuestion.SingleScore;
                }
                totalScore += paperQuestion.SingleScore;
            }
            if (Math.Abs(totalScore - paper.TotalScore) > 0.00001)
                throw new UserFriendlyException("试卷总分和试题分数总和不一致!请检查分值设置");
        }


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

        /// <summary>
        /// 删除生成结果
        /// </summary>
        /// <param name="paperProduceResultId"></param>
        /// <returns></returns>
        public async Task DeleteProduceResultAsync([Required] long paperProduceResultId)
        {
            var produceResult = await _paperProduceResultRepository.GetAsync(paperProduceResultId);
            await _paperProduceResultRepository.DeleteAsync(produceResult);
            await _unitOfWorkManager.Current.SaveChangesAsync();
            using var fileUnit = _unitOfWorkManager.Begin(TransactionScopeOption.RequiresNew);
            var files = new[]
            {
                produceResult.AnswerFileId,
                produceResult.AnswerSheetFileId,
                produceResult.FileId,
                produceResult.FullPaperFileId
            };
            await _fileRepository.GetAll()
                .Where(x => files.Any(f => f == x.Id))
                .UpdateAsync(x => new File.Core.File() { IsDeleted = true });
            await fileUnit.CompleteAsync();
        }
    }
}