using e_suite.API.Common;
using e_suite.API.Common.models;
using e_suite.API.Common.repository;
using e_suite.Database.Audit;
using e_suite.Modules.BlockedIPsManager.Extensions;
using e_suite.Utilities.Pagination;
using eSuite.Core.Clock;
using Microsoft.Extensions.Configuration;
using System.Linq.Expressions;

namespace e_suite.Modules.BlockedIPsManager;

public class BlockedIPsManager : IBlockedIPsManager
{
    private readonly IClock _clock;
    private readonly IConfiguration _configuration;
    private readonly IBlockedIPsManagerRepository _blockedIPsManagerRepository;

    public BlockedIPsManager(IBlockedIPsManagerRepository blockedIPsManagerRepository, IClock clock, IConfiguration configuration)
    {
        _blockedIPsManagerRepository = blockedIPsManagerRepository;
        _clock = clock;
        _configuration = configuration;
    }

    public async Task<IPaginatedData<BlockedIPs>> GetBlockedIPs(Paging paging, CancellationToken cancellationToken)
    {
        //Can I make this move, its a copied code from the sentinal
        var loginAttemptTimeoutMinutes = _configuration.GetValue<int>("Sentinel:LoginAttemptTimeoutMinutes");
        var maxLoginAttempts = _configuration.GetValue<int>("Sentinel:MaxLoginAttempts");

        var earliestAttemptTime = _clock.GetNow.AddMinutes(-loginAttemptTimeoutMinutes);

        var blockedIPs = _blockedIPsManagerRepository.GetBlockedIPs(earliestAttemptTime);

        var dateTimeNow = _clock.GetNow;

        var data = blockedIPs.GroupBy(g => g.IPAddress)
            .Select(x => new BlockedIPs
            {
                IpAddress = x.First().IPAddress,
                NumberOfAttempts = x.Count(),
                BlockedAt = x.Max(t => t.AttemptedTime),
                UnblockedIn = GetUnlockedMinutes(x.Min(t => t.AttemptedTime), loginAttemptTimeoutMinutes, dateTimeNow)
                    .ToInteger()
            });

        var paginatedData = await PaginatedData.Paginate<BlockedIPs, BlockedIPs>(data, paging,
            KeySelector, cancellationToken);

        return paginatedData;
    }

    public static double GetUnlockedMinutes(DateTimeOffset attemptedTime, int loginAttemptTimeoutMinutes, DateTimeOffset dateTimeNow)
    {
        return Math.Floor(attemptedTime.AddMinutes(loginAttemptTimeoutMinutes).Subtract(dateTimeNow).TotalMinutes);
    }

    private Expression<Func<BlockedIPs, object>> KeySelector(string? sortKey)
    {
        return sortKey?.ToLowerInvariant() switch
        {
            "ipaddress" => x => x.IpAddress,
            "numberofattempts" => x => x.NumberOfAttempts,
            "blockedat" => x => x.BlockedAt,
            "unblockedin" => x => x.UnblockedIn,
            _ => x => x.IpAddress
        };
    }

    public async Task UnblockIPAddress(AuditUserDetails auditUserDetails, string ipaddress, CancellationToken cancellationToken)
    {
        await _blockedIPsManagerRepository.UnBlockIP(auditUserDetails, ipaddress, cancellationToken);
    }
}
