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);
    }
}