using AsbCloudApp.Data;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudInfrastructure.Services.Cache;
using Mapster;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace AsbCloudInfrastructure.Services
{
    public class MessageService : IMessageService
    {
        private readonly IAsbCloudDbContext db;
        private readonly ITelemetryService telemetryService;
        private readonly CacheTable<TelemetryEvent> cacheEvents;
        private readonly CacheTable<TelemetryUser> cacheTUsers;

        public MessageService(IAsbCloudDbContext db, CacheDb cacheDb, ITelemetryService telemetryService)
        {
            this.db = db;
            this.telemetryService = telemetryService;
            cacheEvents = cacheDb.GetCachedTable<TelemetryEvent>((AsbCloudDbContext)db);
            cacheTUsers = cacheDb.GetCachedTable<TelemetryUser>((AsbCloudDbContext)db);
        }

        public async Task<PaginationContainer<MessageDto>> GetMessagesAsync(
            int idWell,
            IEnumerable<int> categoryids = default,
            DateTime begin = default,
            DateTime end = default,
            string searchString = default,
            int skip = 0,
            int take = 32,
            bool isUtc = true,
            CancellationToken token = default)
        {
            var idTelemetry = telemetryService.GetIdTelemetryByIdWell(idWell);
            if (idTelemetry is null)
                return null;

            var events = cacheEvents.Where(e => e.IdTelemetry == idTelemetry);

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

            var query = db.TelemetryMessages.Where(m => m.IdTelemetry == idTelemetry)
                .OrderBy(m => m.Date).AsNoTracking();

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

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

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

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

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

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


            var timeOffset = await telemetryService.GetTelemetryTimeZoneOffsetAsync(idTelemetry??default, token)
                .ConfigureAwait(false);

            if (timeOffset is not null)
            {
                begin = telemetryService.TimeZoneService.DateToUtc(begin, timeOffset ?? default);
                end = telemetryService.TimeZoneService.DateToUtc(end, timeOffset ?? default);
            }
            
            if (begin != default)
                query = query.Where(m => m.Date >= begin);

            if (end != default)
                query = query.Where(m => m.Date <= end);

            var result = new PaginationContainer<MessageDto>
            {
                Skip = skip,
                Take = take,
                Count = query.Count()
            };

            if (skip > 0)
                query = query.Skip(skip);

            var messagesList = await query.Take(take).AsNoTracking()
                .ToListAsync(token).ConfigureAwait(false);

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

            var users = cacheTUsers.Where(u => u.IdTelemetry == idTelemetry);

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

                if (message.IdTelemetryUser is not null)
                {
                    if (users.Any())
                    {
                        var user = users.FirstOrDefault(u => u.IdUser == message.IdTelemetryUser);
                        messageDto.User = user.MakeDisplayName();
                    }
                    else
                        messageDto.User = message.IdTelemetryUser.ToString();
                }

                var e = events.FirstOrDefault(e => e.IdEvent == message.IdEvent);
                if (e != null)
                {
                    messageDto.CategoryId = e.IdCategory;
                    messageDto.Message = e.MakeMessageText(message);
                }

                result.Items.Add(messageDto);
            }

            if (isUtc && timeOffset is not null)
                return result;
            
            result.Items = result.Items.Select(m =>
            {
                m.Date = telemetryService.TimeZoneService.DateToTimeZone( m.Date, timeOffset ?? default);
                return m;
            }).ToList();
            
            return result;
        }

        [Obsolete("Use telemetryService.GetDatesRangeAsync instead", false)]
        public Task<DatesRangeDto> GetMessagesDatesRangeAsync(int idWell, bool isUtc,
            CancellationToken token = default)
            => telemetryService.GetDatesRangeAsync(idWell, isUtc, token);

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

            var telemetryId = telemetryService.GetOrCreateTelemetryIdByUid(uid);
            
            var maxDateDto = dtos.Max(m => m.Date);            
            telemetryService.SaveRequestDate(uid, maxDateDto);

            foreach (var dto in dtos)
            {
                var entity = dto.Adapt<TelemetryMessage>();
                entity.Id = 0;
                entity.IdTelemetry = telemetryId;
                db.TelemetryMessages.Add(entity);
            }

            return db.SaveChangesAsync(token);
        }
    }
}