using AsbCloudApp.Data;
using AsbCloudApp.Data.SAUB;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using AsbCloudDb;
using AsbCloudDb.Model;
using Mapster;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;


namespace AsbCloudInfrastructure.Services.SAUB
{
    public class MessageService : IMessageService
    {
        private readonly IAsbCloudDbContext db;
        private readonly IMemoryCache memoryCache;
        private readonly ITelemetryService telemetryService;

        public MessageService(IAsbCloudDbContext db, IMemoryCache memoryCache, ITelemetryService telemetryService)
        {
            this.db = db;
            this.memoryCache = memoryCache;
            this.telemetryService = telemetryService;
        }

        public async Task<PaginationContainer<MessageDto>> GetMessagesAsync(MessageRequest request, CancellationToken token)
        {
            var result = new PaginationContainer<MessageDto>
            {
                Skip = request.Skip ?? 0,
                Take = request.Take ?? 32,
            };

            var telemetry = telemetryService.GetOrDefaultTelemetryByIdWell(request.IdWell);
            if (telemetry is null)
                return result;

            var allEvents = await memoryCache.GetOrCreateBasicAsync(db.Set<TelemetryEvent>(), token);
            var events = allEvents.Where(e => e.IdTelemetry == telemetry.Id);

            if (!events.Any())
                return result;

            var query = db.TelemetryMessages.Where(m => m.IdTelemetry == telemetry.Id)
                .OrderBy(m => m.DateTime).AsNoTracking();

            if (request.Categoryids?.Any() == true || !string.IsNullOrEmpty(request.SearchString))
            {
                if (!string.IsNullOrEmpty(request.SearchString))
                    events = events.Where(e => e.MessageTemplate.Contains(request.SearchString, StringComparison.OrdinalIgnoreCase));

                if (request.Categoryids?.Any() == true)
                    events = events.Where(e => request.Categoryids.ToList().Contains(e.IdCategory));

                var eventIds = events.Select(e => e.IdEvent);

                if (!eventIds.Any())
                    return result;

                query = query.Where(m => eventIds.Contains(m.IdEvent));
            }

            query = query.OrderByDescending(m => m.DateTime);

            if (request.Begin is not null)
            {
                var beginUtc = request.Begin.Value.ToUniversalTime();
                query = query.Where(m => m.DateTime >= beginUtc);
            }

            if (request.End is not null)
            {
                var endUtc = request.End.Value.ToUniversalTime();
                query = query.Where(m => m.DateTime <= endUtc);
            }

            result.Count = query.Count();

            if (request.SortFields?.Any() == true)
            {
                query = query.SortBy(request.SortFields);
            }
            var messagesList = await query.Skip(result.Skip)
                    .Take(result.Take).AsNoTracking()
                    .ToListAsync(token).ConfigureAwait(false);

            if (messagesList.Count == 0)
                return result;

            var allUsers = await memoryCache.GetOrCreateBasicAsync(db.Set<TelemetryUser>(), token);
            var users = allUsers.Where(u => u.IdTelemetry == telemetry.Id);

            var eventsDict = events.ToDictionary(x=>x.IdEvent, x => x);
            var usersDict = users.ToDictionary(x => x.IdUser, x => x);

            var messagesDtoList = new List<MessageDto>();
            var timezone = telemetryService.GetTimezone(telemetry.Id);

            foreach (var message in messagesList)
            {
                var messageDto = new MessageDto
                {
                    Id = message.Id,
                    WellDepth = message.WellDepth
                };

                messageDto.DateTime = message.DateTime.ToOffset(TimeSpan.FromHours(timezone.Hours));

                if (message.IdTelemetryUser is not null)
                {
                    if (usersDict.TryGetValue((int)message.IdTelemetryUser, out TelemetryUser? user))
                    {
                        messageDto.User = user.MakeDisplayName();
                    }
                    else
                        messageDto.User = message.IdTelemetryUser.ToString();
                }

                if (eventsDict.TryGetValue(message.IdEvent, out TelemetryEvent? e))
                {
                    messageDto.CategoryId = e.IdCategory;
                    messageDto.Message = e.MakeMessageText(message);
                }
                messagesDtoList.Add(messageDto);
            }

            result.Items = result.Items.Concat(messagesDtoList);

            return result;
        }

        public Task InsertAsync(string uid, IEnumerable<TelemetryMessageDto> dtos,
            CancellationToken token = default)
        {
            if (!dtos.Any())
                return Task.CompletedTask;

            var telemetry = telemetryService.GetOrCreateTelemetryByUid(uid);
            
            foreach (var dto in dtos)
            {
                var entity = dto.Adapt<TelemetryMessage>();
                entity.Id = 0;
                entity.IdTelemetry = telemetry.Id;
                entity.DateTime = dto.Date.ToUniversalTime();
                db.TelemetryMessages.Add(entity);
            }

            return db.SaveChangesAsync(token);
        }
    }
}