using MediatR;
using Sledgemapper.Api.Data;
using Sledgemapper.Shared.Entities;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using System.Linq;
using Sledgemapper.Api.Models;

namespace Sledgemapper.Api.Commands
{
    public class GetMapSnapshotCommandHandler : IRequestHandler<GetMapSnapshotCommand, Sledgemapper.Shared.Entities.Session>
    {
        private readonly MyDbContext _dbcontext;

        public GetMapSnapshotCommandHandler(MyDbContext dbcontext) { _dbcontext = dbcontext; }

        public async Task<Sledgemapper.Shared.Entities.Session> Handle(GetMapSnapshotCommand notification, CancellationToken cancellationToken)
        {
            Snapshot snapshot;
            double timestamp;
            Sledgemapper.Shared.Entities.Session mapSession;
            var session = _dbcontext.Sessions.First(m => m.SessionName == notification.SessionName);
            snapshot = _dbcontext.Snapshots.OrderByDescending(s => s.Timestamp).FirstOrDefault(m => m.SessionId == session.SessionId);
            if (snapshot is null)
            {

                timestamp = 0;
                mapSession = new Shared.Entities.Session();
            }
            else
            {
                mapSession = JsonSerializer.Deserialize<Sledgemapper.Shared.Entities.Session>(snapshot.Object);
                timestamp = snapshot.Timestamp;
            }

            var mapUpdates = _dbcontext.MapLogs.Where(m => m.SessionId == session.SessionId && m.Timestamp > timestamp).OrderBy(m => m.Timestamp).ToList();
            foreach (var mapUpdate in mapUpdates)
            {
                if (mapUpdate.Operation == "N")
                {
                    switch (mapUpdate.Type)
                    {
                        case "T":
                            var tile = JsonSerializer.Deserialize<Tile>(mapUpdate.Object);
                            mapSession.NewTile(tile, tile.ID);
                            break;
                        case "W":
                            var wall = JsonSerializer.Deserialize<Wall>(mapUpdate.Object);
                            mapSession.NewWall(wall, wall.ID);
                            break;
                        case "O":
                            var overlay = JsonSerializer.Deserialize<Overlay>(mapUpdate.Object);
                            mapSession.NewOverlay(overlay, overlay.ID);
                            break;

                    }

                }
                else if (mapUpdate.Operation == "D")
                {
                    switch (mapUpdate.Type)
                    {
                        case "T":
                            var tile = JsonSerializer.Deserialize<Tile>(mapUpdate.Object);
                            mapSession.DeleteTile(tile);
                            break;
                        case "W":
                            var wall = JsonSerializer.Deserialize<Wall>(mapUpdate.Object);
                            mapSession.DeleteWall(wall);
                            break;
                        case "O":
                            var overlay = JsonSerializer.Deserialize<Overlay>(mapUpdate.Object);
                            mapSession.DeleteOverlay(overlay);
                            break;
                    }
                }
            }
            if (mapUpdates.Any())
            {
                var newSnapshot = new Snapshot
                {
                    SessionId = session.SessionId,
                    Timestamp = mapUpdates.Max(mapSession => mapSession.Timestamp),
                    Object = JsonSerializer.Serialize<Sledgemapper.Shared.Entities.Session>(mapSession)
                };
                await _dbcontext.Snapshots.AddAsync(newSnapshot);
                await _dbcontext.SaveChangesAsync();
            }
            return mapSession;
        }
    }
}
