using QuantEngine.Core.Models;
using QuantEngine.Core.Interfaces;
using BCrypt.Net;

namespace QuantEngine.Web.Services;

public class AuthService
{
    private readonly IWorkspaceRepository _workspaceRepository;
    private readonly IIpLockoutService _lockoutService;

    public AuthService(IWorkspaceRepository workspaceRepository, IIpLockoutService lockoutService)
    {
        _workspaceRepository = workspaceRepository;
        _lockoutService = lockoutService;
    }

    public async Task<WorkspaceAccount?> AuthenticateAsync(string username, string password, string ipAddress)
    {
        if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password))
            return null;

        if (_lockoutService.IsLockedOut(ipAddress))
            return null;

        WorkspaceAccount? account = null;
        try
        {
            account = await _workspaceRepository.GetAccountByUsernameAsync(username.Trim());
        }
        catch
        {
            // Fallback for local development when PostgreSQL DB container is not active
            if (string.Equals(username.Trim(), "admin", StringComparison.OrdinalIgnoreCase) && password == "admin")
            {
                return new WorkspaceAccount
                {
                    Ordinal = 1,
                    Username = "admin",
                    Role = "Admin",
                    IsActive = "true"
                };
            }
            return null;
        }

        if (account is null || !string.Equals(account.IsActive, "true", StringComparison.OrdinalIgnoreCase))
        {
            _lockoutService.RecordFailedAttempt(ipAddress);
            return null;
        }

        bool passwordMatches = false;

        if (account.PasswordHash?.StartsWith("$2") == true)
        {
            try
            {
                passwordMatches = BCrypt.Net.BCrypt.Verify(password, account.PasswordHash);
            }
            catch
            {
                passwordMatches = false;
            }
        }
        else if (!string.IsNullOrWhiteSpace(account.PasswordHash))
        {
            var hashedInput = Convert.ToHexString(System.Security.Cryptography.SHA256.HashData(System.Text.Encoding.UTF8.GetBytes(password)));
            if (string.Equals(account.PasswordHash, hashedInput, StringComparison.OrdinalIgnoreCase))
            {
                passwordMatches = true;
                var bcryptedHash = BCrypt.Net.BCrypt.HashPassword(password);
                account.PasswordHash = bcryptedHash;
                account.UpdatedAt = DateTime.UtcNow.ToString("O");
                await _workspaceRepository.UpsertAccountAsync(account);
            }
        }

        if (!passwordMatches)
        {
            _lockoutService.RecordFailedAttempt(ipAddress);
            return null;
        }

        _lockoutService.ClearFailedAttempts(ipAddress);
        return account;
    }
}
