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, IUserGateway userGateway, IAccountGateway accountGateway,
    IPasswordHasher passwordHasher, TokenGenerator tokenGenerator) : IRequestHandler<LoginCommand, AuthenticationResult>
{
    public async Task<AuthenticationResult> Handle(LoginCommand request, CancellationToken cancellationToken)
    {
        //await InitLocalization();
        //await new CredentialsValidator(_dbContext, this, request).Validate();

        var accountId = passwordHasher.VerifyPassword(await userGateway.TryGetAccount(request.Email), request.Password);
        if (!accountId.HasValue) throw new AccountException();

        var user = await userGateway.Get(accountId.Value) ?? throw new AccountException();


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

        //get roles
        var jwtSettings = userContext.GetJwtSettings();

        var userAgent = userContext.GetUserAgent();
        var location = await userContext.GetLocation(request.Ip);
        var session = user.Account.AddSession(jwtSettings.RefreshTokenExpireTime, tokenGenerator, userAgent, location?.Country, location?.Region);

        await accountGateway.UpdateSessions(user.Account.Id, user.Account.Sessions.Select(q => new Session
        {
            Id = q.Id,
            RefreshToken = q.RefreshToken,
            ClientInfo = new ClientInfo
            {
                UserAgent = q.ClientInfo.UserAgent,
                Location = new Location
                {
                    Country = q.ClientInfo.Country,
                    Region = q.ClientInfo.Region,
                }
            },
            AccountId = q.AccountId,
            ExpiredDate = q.ExpiredDate,
        }));
        if (session.Id == Guid.Empty)
        {
            var newSession = (await accountGateway.TryGetSession(session.RefreshToken)) ?? throw new Exception("Could not create new session");
            session = new Domain.Entities.Account.Session(new Domain.Models.Session
            {
                Id = newSession.Id,
                AccountId = newSession.AccountId,
                RefreshToken = newSession.RefreshToken,
                ExpiredDate = newSession.ExpiredDate,
                ClientInfo = new Domain.Models.ClientInfo
                {
                    UserAgent = newSession.ClientInfo.UserAgent,
                    Country = newSession.ClientInfo.Location.Country,
                    Region = newSession.ClientInfo.Location.Region,
                }
            });
        }
        var accessToken = tokenGenerator.GenerateAccessToken(user, session, jwtSettings);
        TokenGenerator.SetRefreshTokenInCookie(request.Response, request.CookiePath, session.RefreshToken, session.ExpiredDate);
        return new() { AccessToken = accessToken, SessionExpireDate = session.ExpiredDate, };
    }
}