using AsbCloudApp.Data;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using AsbCloudDb;
using AsbCloudDb.Model;
using Mapster;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace AsbCloudInfrastructure.Repository
{
    /// <summary>
    /// сервис по работе с faq-вопросами
    /// </summary>
    public class FaqRepository : IFaqRepository
    {
        private readonly IAsbCloudDbContext db;
        private readonly IWellService wellService;

        public FaqRepository(IAsbCloudDbContext db, IWellService wellService)
        {
            this.db = db;
            this.wellService = wellService;
        }

        public async Task<IEnumerable<FaqDto>> GetFilteredAsync(FaqRequest request, CancellationToken token)
        {
            var query = db.Faqs.AsNoTracking();

            if (request.IsFrequently)
            {
                query = query.Where(o => o.IsFrequently);
            }
            if (request.HasAnswer)
            {
                query = query.Where(o => !string.IsNullOrEmpty(o.Answer));
            }
            if (!request.IncludeDeleted)
            {
                query = query.Where(o => o.State != Faq.StateDeleted);
            }

            var entities = query
                .OrderByDescending(e => e.DateCreatedQuestion)
                .Select(o => new FaqDto()
                {
                    Id = o.Id,
                    Question = o.Question,
                    Answer = o.Answer,
                    CounterQuestion = o.CounterQuestion,
                    IsFrequently = o.IsFrequently,
                    State = o.State,
                    DateCreatedQuestion = o.DateCreatedQuestion,
                    IdAuthorQuestion = o.IdAuthorQuestion,
                    AurhorQuestionName = o.AuthorQuestion.MakeDisplayName(),
                    AurhorAnswerName = (o.AuthorAnswer != null) 
                        ? o.AuthorAnswer.MakeDisplayName() 
                        : string.Empty,
                });


            var result = await entities.AsNoTracking()
                .ToArrayAsync(token)
                .ConfigureAwait(false);

            return result;

        }

        public async Task<int> InsertAsync(FaqDto faqDto, CancellationToken token)
        {
            var entity = faqDto.Adapt<Faq>();
            entity.DateCreatedQuestion = entity.DateLastEditedQuestion = DateTimeOffset.UtcNow;
            entity.CounterQuestion = 1;
            entity.State = Faq.StateOpened;
            if (!string.IsNullOrEmpty(entity.Answer))
                entity.IdAuthorAnswer = entity.IdAuthorQuestion;


            db.Faqs.Add(entity);

            await db.SaveChangesAsync(token).ConfigureAwait(false);
            return entity.Id;
        }

        public async Task<int> MergeAsync(int sourceId1, int sourceId2, bool mergeQuestions, CancellationToken token)
        {
            var sourceFaqs = db.Faqs.Where(e => e.Id == sourceId1 || e.Id == sourceId2).ToArray();
            if (sourceFaqs.Count() < 2)
                throw new ArgumentInvalidException(nameof(sourceFaqs), "Questions with target ids don't exist");

            var newFaq = new Faq()
            {
                CounterQuestion = sourceFaqs.Sum(e => e.CounterQuestion),
                State = 0,
                IsFrequently = sourceFaqs.Any(e => e.IsFrequently),
                IdAuthorQuestion = sourceFaqs.Last().IdAuthorQuestion,
                DateCreatedQuestion = DateTimeOffset.UtcNow,
                DateLastEditedQuestion = DateTimeOffset.UtcNow,
                Answer = mergeQuestions == true
                    ? string.Join(Environment.NewLine, sourceFaqs.Select(e => e.Answer))
                    : sourceFaqs.Last().Answer,
                Question = mergeQuestions == true
                    ? string.Join(Environment.NewLine, sourceFaqs.Select(e => e.Question))
                    : sourceFaqs.Last().Question,
            };

            db.Faqs.Add(newFaq);
            await db.SaveChangesAsync(token).ConfigureAwait(false);

            if (newFaq.Id == 0)
                throw new ArgumentInvalidException(nameof(sourceId1), "Error creating new question");

            foreach (var sourceFaq in sourceFaqs)
            {
                sourceFaq.IdReplacementQuestion = newFaq.Id;
                sourceFaq.State = Faq.StateDeleted;
            }
            await db.SaveChangesAsync(token).ConfigureAwait(false);

            return newFaq.Id;
        }

        public async Task<int> UpdateAsync(FaqDto dto, CancellationToken token)
        {
            var entity = dto.Adapt<Faq>();
            entity.DateLastEditedQuestion = DateTimeOffset.UtcNow;
            db.Faqs.Update(entity);
            return await db.SaveChangesAsync(token)
                .ConfigureAwait(false);
        }

        public async Task<int> MarkAsDeletedAsync(int id, CancellationToken token)
        {
            var entity = db.Faqs.FirstOrDefault(e => e.Id == id)
                ?? throw new ArgumentInvalidException(nameof(id), "Question doesn't exist");

            entity.State = Faq.StateDeleted;
            entity.DateLastEditedQuestion = DateTimeOffset.UtcNow;

            return await db.SaveChangesAsync(token)
                .ConfigureAwait(false);
        }

        public async Task<int> DeleteAsync(int id, CancellationToken token)
        {
            var faq = db.Faqs.FirstOrDefault(f => f.Id == id)
                ?? throw new ArgumentInvalidException(nameof(id), "Question doesn't exist");

            db.Faqs.Remove(faq);
            return await db.SaveChangesAsync(token);
        }
    }
}