using MediatR;
using Microsoft.AspNetCore.Http;
using Modules.User.Application.Errors;
using Modules.User.Application.Gateways;
using Modules.User.Application.Models;

namespace Modules.User.Application.Commands;

public class LoginCommand : IRequest<AuthenticationResult>
{
    public string Email { get; set; } = default!;
    public string Password { get; set; } = default!;
    public string? Ip { get; set; }

    public bool IsAdmin { get; set; }
    public HttpResponse Response { get; set; } = default!;
    public string? CookiePath { get; set; }
}

public class LoginCommandHandler(UserContext userContext, IUserRepository userRepository, IAccountAccessQueries accessQueries,
    IPasswordHasher passwordHasher, IUnitOfWork unitOfWork) : IRequestHandler<LoginCommand, AuthenticationResult>
{
    public async Task<AuthenticationResult> Handle(LoginCommand request, CancellationToken cancellationToken)
    {
        var hashed = await userRepository.TryGetHashedAccountAsync(request.Email, cancellationToken);
        
        var accountId = passwordHasher.VerifyPassword(hashed, request.Password);
        // var accountId = passwordHasher.VerifyPassword(await userGateway.TryGetAccount(request.Email), request.Password);
        if (!accountId.HasValue) throw new AccountException();
        
        var user = await userRepository.GetByAccountIdAsync(accountId.Value, cancellationToken)
                   ?? throw new AccountException();
        var now = DateTime.UtcNow;
        var status = user.Account.GetBanStatus(now);
        if (status.IsBanned) throw new AccountBannedException(status.ReleaseDate);

        // EnsureUserType(user, adminLogin)
        //
        // var accountRoles = (await accountGateway.GetAccountRoles(accountId.Value)).ToList();
        // var accountPermissions = await accountGateway.GetAccountPermissions(accountId.Value);
        
        var jwtSettings = userContext.GetJwtSettings();

        var userAgent = userContext.GetUserAgent();
        var location = await userContext.GetLocation(request.Ip);
        var refreshToken = TokenGenerator.GenerateRefreshToken();
        var expiresAt = now.Add(jwtSettings.RefreshTokenExpireTime);
        var clientInfo = Domain.Entities.User.ClientInfo.Create(userAgent, location?.Country, location?.Region);
        
        var session = user.AddOrUpdateSession(null, refreshToken, expiresAt, clientInfo);
        await userRepository.SaveAsync(user, cancellationToken);
        await unitOfWork.SaveChangesAsync(cancellationToken);
        
        var roleNames = await accessQueries.GetRoleNamesAsync(user.Account.Id, cancellationToken);
        var permissionNames = await accessQueries.GetPermissionNamesAsync(user.Account.Id, cancellationToken);
        var permissionCodes = await accessQueries.GetEffectivePermissionCodesAsync(user.Account.Id, cancellationToken);
        
        var accessToken = TokenGenerator.GenerateAccessToken(user, session, jwtSettings, 
            roleNames, permissionNames, permissionCodes);
        
        TokenGenerator.SetRefreshTokenInCookie(request.Response, request.CookiePath,
            session.RefreshToken, session.ExpiredDateUtc);
        

        return new AuthenticationResult
        {
            AccessToken = accessToken,
            SessionExpireDate = session.ExpiredDateUtc,
        };
    }

    // private static void EnsureUserType(User user, bool adminLogin)
    // {
    //     //проверка на соответствие типа входа. клиент для клиентского приложения, сотрудник для внутреннего приложения
    //     if (adminLogin)
    //     {
    //         if (!user.IsAdmin) throw await ThrowError(permissionDenied);
    //     }
    //     else if (request.ApplicationTypeForUserType == UserType.Сотрудник)
    //     else
    //     {
    //         if (user.IsAdmin) throw await ThrowError(permissionDenied);
    //     }
    // }
}