using MediatR;
using Sledgemapper.Api.Commands;
using Sledgemapper.Api.Infrastructure.Data;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System;
using Microsoft.EntityFrameworkCore;
using Sledgemapper.Api.Models;
using Sledgemapper.Api.Core.Entities;

namespace Sledgemapper.Api.Handlers
{
    public abstract class BaseCommandHandler<TRequest, TResponse> : IRequestHandler<TRequest, TResponse> where TRequest : BaseCommand<TResponse>
    {
        protected SledgemapperDbContext DbContext { get; }

        protected IMediator Mediator { get; }


        public abstract Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken);

        protected BaseCommandHandler(IMediator mediator, SledgemapperDbContext dbContext)
        {
            DbContext = dbContext;
            Mediator = mediator;
        }

        protected async Task CheckAuthorization(TRequest command)
        {
            var user = await DbContext.Users.FindAsync(command.UserId);
            DbContext.Attach(user);


            var campaign = await GetCampaignForUser(command);

            var maps = campaign.Maps.Any(s => s.SessionId == command.SessionId);

            if (!maps)
            {
                throw new Exception("Unauthorized");
            }
        }

        protected async Task<Campaign> GetCampaignForUser(TRequest command)
        {
            var user = await DbContext.Users.FindAsync(command.UserId);
            DbContext.Attach(user);
            var campaign = await DbContext
                .Campaigns
                .Where(campaign => campaign.CampaignId == command.Campaign)
                .Include(c => c.InvitedUsers)
                .Include(c => c.Maps)
                .Include(c => c.Owner)
                .Where(campaign => campaign.OwnerId == command.UserId || campaign.InvitedUsers.Contains(user)).FirstAsync();
            return campaign;
        }

        protected async Task<Session> SaveLog(TRequest command, string operation, string type, string data, CancellationToken cancellationToken)
        {
            var session = DbContext.Sessions.First(m => m.SessionId == command.SessionId);
            DbContext.MapLogs.Add(new MapLog
            {
                Operation = operation,
                SessionId = session.SessionId,
                Type = type,
                Timestamp = command.Timestamp,
                Object = data,
                UserId = command.UserId,
            });
            await DbContext.SaveChangesAsync(cancellationToken);
            return session;
        }

    }
}
