using Abp.Application.Services.Dto;
using Abp.Authorization;
using Abp.Domain.Repositories;
using Abp.Extensions;
using Abp.Linq.Extensions;
using Abp.Runtime.Session;
using Abp.UI;
using Mapster;
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using YGNT.QuestionLibrary.Core.Disciplines;
using YGNT.QuestionLibrary.Core.Grades;
using YGNT.QuestionLibrary.Core.Testings;
using YGNT.QuestionLibrary.Testings.Dto;
using Z.EntityFramework.Plus;

namespace YGNT.QuestionLibrary.Testings
{
    /// <summary>
    /// 测验
    /// </summary>
    public class TestingAppService : ITestingAppService
    {
        public IAbpSession AbpSession { get; set; }

        private readonly IRepository<Testing> _testingRepository;
        private readonly IRepository<TestingQuestion> _testingQuestionRepository;
        private readonly IRepository<Discipline> _disciplineRepository;
        private readonly IRepository<Grade> _gradeRepository;

        public TestingAppService(IRepository<Testing> testingRepository,
            IRepository<Discipline> disciplineRepository,
            IRepository<Grade> gradeRepository,
            IRepository<TestingQuestion> testingQuestionRepository)
        {
            AbpSession = NullAbpSession.Instance;
            _testingRepository = testingRepository;
            _disciplineRepository = disciplineRepository;
            _gradeRepository = gradeRepository;
            _testingQuestionRepository = testingQuestionRepository;
        }

        /// <summary>
        /// 查询测验列表
        /// </summary>
        /// <param name="search"></param>
        /// <returns></returns>
        public async Task<PagedResultDto<TestingDto>> GetListAsync(SearchTestingInputDto search)
        {
            var userId = AbpSession.GetUserId();
            var queryable = _testingRepository.GetAll()
                .WhereIf(search.DisciplineId.HasValue, x => x.DisciplineId == search.DisciplineId.Value)
                .WhereIf(!search.KeyWord.IsNullOrEmpty(), x => x.Name.Contains(search.KeyWord))
                .Where(x => x.CreatorUserId == userId)
                .OrderBy(x => x.Name);

            var result = new PagedResultDto<TestingDto>()
            {
                TotalCount = await queryable.CountAsync(),
                Items = await queryable.PageBy(search.SkipCount, search.MaxResultCount)
                    .Select(x => x.Adapt(new TestingDto()
                    {
                        TestingTypeName = "测验",
                        GradeName = x.Grade.Name,
                        DisciplineName = x.Discipline.Name
                    })).ToListAsync()
            };
            return result;
        }

        /// <summary>
        /// 创建新测验
        /// </summary>
        /// <param name="create"></param>
        /// <returns></returns>
        public async Task<int> CreateAsync(CreateTestingDto create)
        {
            var testing = create.Adapt<Testing>();
            var discipline = await _disciplineRepository.SingleAsync(i => i.Id == testing.DisciplineId);
            testing.TestingType = 1;
            testing.TotalQuestion = 0;
            if (discipline != null)
            {
                testing.GradeId = discipline.GradeId;
                testing.CourseId = discipline.CourseId;

                var grade = await _gradeRepository.SingleAsync(i => i.Id == testing.GradeId);
                if (grade != null)
                {
                    testing.EducationCategorieId = grade.EducationCategoryId;
                }
            }
            await _testingRepository.InsertAndGetIdAsync(testing);
            return testing.Id;
        }

        /// <summary>
        /// 修改测验
        /// </summary>
        /// <param name="id"></param>
        /// <param name="update"></param>
        /// <returns></returns>
        public async Task UpdateAsync([Required] int id, UpdateTestingDto update)
        {
            var testing = await _testingRepository.GetAsync(id);
            if (update.DisciplineId != testing.DisciplineId)
            {
                var discipline = await _disciplineRepository.SingleAsync(i => i.Id == testing.DisciplineId);
                if (discipline != null)
                {
                    testing.GradeId = discipline.GradeId;
                    testing.CourseId = discipline.CourseId;

                    var grade = await _gradeRepository.SingleAsync(i => i.Id == testing.GradeId);
                    if (grade != null)
                    {
                        testing.EducationCategorieId = grade.EducationCategoryId;
                    }
                }
            }
            update.Adapt(testing);
            await _testingRepository.UpdateAsync(testing);
        }

        /// <summary>
        /// 删除测验
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task DeleteAsync([Required] int id)
        {
            await _testingRepository.DeleteAsync(id);
            await _testingQuestionRepository.DeleteAsync(i => i.TestingId == id);
        }

        #region API
        /// <summary>
        /// 查询测验列表(外接口)
        /// </summary>
        /// <param name="search"></param>
        /// <returns></returns>
        [AbpAllowAnonymous]
        public async Task<PagedResultDto<TestingDto>> GetPageAsync(SearchTestingInputDto search)
        {
            var queryable = _testingRepository.GetAll()
                .WhereIf(search.EducationCategorieId.HasValue, x => x.EducationCategorieId == search.EducationCategorieId.Value)
                .WhereIf(search.GradeId.HasValue, x => x.GradeId == search.GradeId.Value)
                .WhereIf(search.TextbookId.HasValue, x => x.TextbookId == search.TextbookId.Value)
                .WhereIf(search.DisciplineId.HasValue, x => x.DisciplineId == search.DisciplineId.Value)
                .WhereIf(search.ChapterId.HasValue, x => x.ChapterId == search.ChapterId.Value)
                .WhereIf(search.CourseId.HasValue, x => x.CourseId == search.CourseId.Value)
                .WhereIf(!search.KeyWord.IsNullOrEmpty(), x => x.Name.Contains(search.KeyWord))
                //知识点筛选
                .WhereIf(search.KnowledgeId > 0, x => x.TestingKnowledges.Any(f => search.KnowledgeId == f.KnowledgeId))
                .Where(x => x.TotalQuestion > 0)
                .OrderBy(x => x.Name);

            var result = new PagedResultDto<TestingDto>()
            {
                TotalCount = await queryable.CountAsync(),
                Items = await queryable.PageBy(search.SkipCount, search.MaxResultCount)
                    .Select(x => x.Adapt(new TestingDto()
                    {
                        TestingTypeName = "测验",
                        GradeName = x.Grade.Name,
                        DisciplineName = x.Discipline.Name
                    })).ToListAsync()
            };
            return result;
        }

        /// <summary>
        /// 根据测验ID查询列表(外接口)
        /// </summary>
        /// <param name="search"></param>
        /// <returns></returns>
        [AbpAllowAnonymous]
        public async Task<List<TestingDto>> PostListAsync(SelectTestingInputDto search)
        {
            var result = await _testingRepository.GetAll()
                .Where(x => search.TestingIds.Contains(x.Id))
                .Select(x => x.Adapt(new TestingDto()
                {
                    TestingTypeName = "测验",
                    GradeName = x.Grade.Name,
                    DisciplineName = x.Discipline.Name
                })).ToListAsync();
            return result;
        }

        /// <summary>
        /// 获取测验详情
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [AbpAllowAnonymous]
        public async Task<TestingDto> GetEntityAsync([Required] int id)
        {
            return await _testingRepository.GetAll()
                .Where(x => x.Id == id)
                .Select(x => x.Adapt(new TestingDto()
                {
                    TestingTypeName = "测验",
                    GradeName = x.Grade.Name,
                    DisciplineName = x.Discipline.Name
                })).FirstOrDefaultAsync();
        }
        #endregion
    }
}
