using ERPCore.Dtos.Auth;
using ERPCore.Infra.Auth.AuthHex;
using ERPCore.Services.Interfaces;
using ERPCore.System.Errors;

namespace ERPCore.Services.Auth;

/// <inheritdoc cref="IAuthUserService"/>
public sealed class AuthUserService : IAuthUserService
{
    private readonly IAuthHexClient _authHex;

    public AuthUserService(IAuthHexClient authHex) => _authHex = authHex;

    public async Task<AuthSessionResult> RegisterAsync(RegisterRequest request, CancellationToken ct = default)
    {
        request.UserId ??= Guid.NewGuid();
        var result = await _authHex.RegisterAsync(request, ct);
        return ToSessionResult(result);
    }

    public async Task<AuthSessionResult> LoginAsync(LoginRequest request, CancellationToken ct = default)
    {
        var result = await _authHex.LoginAsync(request, ct);
        return ToSessionResult(result);
    }

    public async Task<OtpAuthSessionResult> VerifyOtpForLoginAsync(VerifyOtpForLoginRequest request, CancellationToken ct = default)
    {
        var result = await _authHex.VerifyOtpForLoginAsync(request, ct);
        return ToOtpSessionResult(result);
    }

    public async Task<AuthSessionResult> RefreshTokenAsync(string refreshToken, RefreshTokenRequest request, CancellationToken ct = default)
    {
        var result = await _authHex.RefreshTokenAsync(refreshToken, request.DeviceName, ct);
        return ToSessionResult(result);
    }

    public Task<GetUserDetailsResponse> GetUserDetailsAsync(Guid userId, CancellationToken ct = default)
        => _authHex.GetUserDetailsAsync(userId, ct);

    public Task<List<SessionDto>> GetUserSessionsAsync(string bearerToken, CancellationToken ct = default)
        => _authHex.GetUserSessionsAsync(bearerToken, ct);

    public Task ChangeUserStatusAsync(ChangeUserStatusRequest request, string bearerToken, CancellationToken ct = default)
        => _authHex.ChangeUserStatusAsync(request.IsActive, bearerToken, ct);

    public Task LockUserAccountAsync(LockUserAccountRequest request, string bearerToken, CancellationToken ct = default)
        => _authHex.LockUserAccountAsync(request.IsLocked, bearerToken, ct);

    public Task ChangeUserPasswordAsync(ChangeUserPasswordRequest request, string bearerToken, CancellationToken ct = default)
        => _authHex.ChangeUserPasswordAsync(request, bearerToken, ct);

    public Task<VerifyPasswordResponse> VerifyPasswordAsync(VerifyPasswordRequest request, string bearerToken, CancellationToken ct = default)
        => _authHex.VerifyPasswordAsync(request, bearerToken, ct);

    /// <summary>The controller resolves the id (from body or token claim) before calling here.</summary>
    public Task LogoutUserAsync(LogoutRequest request, CancellationToken ct = default)
        => request.UserId is null
            ? Task.CompletedTask
            : _authHex.LogoutUserAsync(request.UserId.Value, ct);

    public Task<UserSummaryDto?> UpdateUserAsync(UpdateUserRequest request, string bearerToken, CancellationToken ct = default)
        => _authHex.UpdateUserAsync(request, bearerToken, ct);

    public Task<TwoFaSetupResponse> InitiateTwoFaSetupAsync(string bearerToken, CancellationToken ct = default)
        => _authHex.InitiateTwoFaSetupAsync(bearerToken, ct);

    public Task<CompleteTwoFaSetupResponse> CompleteTwoFaSetupAsync(CompleteTwoFaSetupRequest request, string bearerToken, CancellationToken ct = default)
        => _authHex.CompleteTwoFaSetupAsync(request, bearerToken, ct);

    public Task VerifyTwoFaAsync(VerifyTwoFaRequest request, string bearerToken, CancellationToken ct = default)
        => _authHex.VerifyTwoFaAsync(request, bearerToken, ct);

    public Task DisableTwoFaAsync(DisableTwoFaRequest request, string bearerToken, CancellationToken ct = default)
        => _authHex.DisableTwoFaAsync(request, bearerToken, ct);

    public Task<TwoFaStatusResponse> GetTwoFaStatusAsync(string bearerToken, CancellationToken ct = default)
        => _authHex.GetTwoFaStatusAsync(bearerToken, ct);

    private static AuthSessionResult ToSessionResult(AuthHexSessionResult? result)
    {
        if (result is null || string.IsNullOrEmpty(result.AccessToken) || string.IsNullOrEmpty(result.RefreshToken))
            throw new DomainException(ErrorCodes.AuthUpstreamError, "The authentication service did not return a session.", 502);

        return new AuthSessionResult
        {
            AccessToken = result.AccessToken,
            RefreshToken = result.RefreshToken,
            Body = new AuthSessionResponse { User = result.User, ExpiresIn = result.ExpiresIn }
        };
    }

    private static OtpAuthSessionResult ToOtpSessionResult(AuthHexSessionResult? result)
    {
        if (result is null || string.IsNullOrEmpty(result.AccessToken) || string.IsNullOrEmpty(result.RefreshToken))
            throw new DomainException(ErrorCodes.AuthUpstreamError, "The authentication service did not return a session.", 502);

        return new OtpAuthSessionResult
        {
            AccessToken = result.AccessToken,
            RefreshToken = result.RefreshToken,
            Body = new OtpLoginVerifiedResponse
            {
                ReferenceNumber = result.ReferenceNumber,
                UserId = result.UserId,
                Verified = result.Verified ?? true,
                User = result.User,
                ExpiresIn = result.ExpiresIn
            }
        };
    }
}
