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

namespace AsbCloudInfrastructure.Services
{
    public class TelemetryTracker : ITelemetryTracker
    {
        class TrackerStat
        {
            //public int Id { get; set; }

            public string RemoteUid { get; set; }

            /// <summary>
            /// Время последнего запроса (по времени сервера)
            /// </summary>
            public DateTime LastTimeServer { get; set; }

            /// <summary>
            /// Время указанное в данных последнего запроса (из удаленного источника)
            /// </summary>
            public DateTime LastTimeRemote { get; set; }
        }

        private readonly ConcurrentDictionary<string, TrackerStat> requests;

        public TelemetryTracker(CacheDb cacheDb)
        {
            var options = new DbContextOptionsBuilder<AsbCloudDbContext>()
                .UseNpgsql("Host=localhost;Database=postgres;Username=postgres;Password=q;Persist Security Info=True")
                .Options;
            using var db = new AsbCloudDbContext(options);

            var cacheTelemetry = cacheDb.GetCachedTable<Telemetry>(db);
            var keyValuePairs = new Dictionary<string, TrackerStat>(cacheTelemetry.Count());
            foreach (var telemetry in cacheTelemetry)
            {
                var date = telemetry.Info?.DrillingStartDate 
                    ?? GetDateByUidOrDefault(telemetry.RemoteUid, DateTime.MinValue);

                keyValuePairs[telemetry.RemoteUid] = new TrackerStat { 
                    RemoteUid = telemetry.RemoteUid,
                    LastTimeRemote = date,
                    LastTimeServer = date,
                };
            }
            requests = new ConcurrentDictionary<string, TrackerStat>(keyValuePairs);

            Task.Run(async() => {
                db.Database.SetCommandTimeout(2 * 60);
                var dates = await db.TelemetryDataSaub
                    .GroupBy(d => d.IdTelemetry)
                    .Select(g => new
                    {
                        IdTelemetry = g.Key,
                        Date = g.Max(d=>d.Date)
                    })
                    .AsNoTracking()
                    .ToListAsync()
                    .ConfigureAwait(false);
                db.Dispose();

                var oldReqs = dates.Select(t => new
                {
                    Uid = cacheTelemetry.FirstOrDefault(c => c.Id == t.IdTelemetry).RemoteUid,
                    t.Date,
                });

                foreach (var oldReq in oldReqs)
                {
                    var request = requests.GetValueOrDefault(oldReq.Uid);
                    if(request is not null)
                    {
                        if (request.LastTimeRemote < oldReq.Date)
                            request.LastTimeRemote = oldReq.Date;
                        if (request.LastTimeServer < oldReq.Date)
                            request.LastTimeServer = oldReq.Date;
                    }
                }
            });
        }

        private static DateTime GetDateByUidOrDefault(string remoteUid, DateTime defaultValue = default)
        {
            //eg: uid = 20211102_173407926
            if (string.IsNullOrEmpty(remoteUid) || (remoteUid.Length != 18))
                return defaultValue;

            if (DateTime.TryParseExact(remoteUid, "yyyyMMdd_HHmmssfff",
                                   System.Globalization.CultureInfo.InvariantCulture,
                                   System.Globalization.DateTimeStyles.AssumeUniversal,
                                   out DateTime parsedDate))
                return parsedDate;

            return defaultValue;
        }

        public void SaveRequestDate(string uid, DateTime remoteDate)
        {
            var stat = requests.GetValueOrDefault(uid);
            if(stat is null)
            {
                stat = new TrackerStat{ RemoteUid = uid };
                requests[uid] = stat;
            }
            stat.LastTimeServer = DateTime.Now;
            stat.LastTimeRemote = remoteDate;
        }

        public DateTime GetLastTelemetryDateByUid(string uid) =>
            requests.GetValueOrDefault(uid)?.LastTimeRemote ?? default;

        public IEnumerable<string> GetTransmittingTelemetriesUids() =>
            requests.Keys;
    }
}