using AuthorBuddy.Web.Data;
using LiteDB;

namespace AuthorBuddy.Web.Services;

public class AuthService : IAuthService
{
    private readonly ILiteDatabase _db;
    private readonly ISettingsService _settingsService;
    private readonly ILocalizationService _loc;
    private UserEntity? _currentUser;
    private string? _pending2FASecret;
    private bool _requires2FA;

    public AuthService(ILiteDatabase db, ISettingsService settingsService, ILocalizationService loc)
    {
        _db = db;
        _settingsService = settingsService;
        _loc = loc;
    }

    public bool IsLoggedIn => _currentUser != null;
    public bool Is2FARequired => _requires2FA;
    public UserEntity? CurrentUser => _currentUser;

    public async Task<bool> LoginAsync(string username, string password)
    {
        var col = _db.GetCollection<UserEntity>("users");
        var user = col.FindOne(u => u.Name == username);

        if (user == null || string.IsNullOrEmpty(user.PasswordHash))
            return false;

        if (!user.IsActive)
            return false;

        if (!TotpHelper.VerifyPassword(password, user.PasswordHash))
            return false;

        if (user.TwoFactorEnabled)
        {
            _pending2FASecret = user.TwoFactorSecret;
            _requires2FA = true;
            return true;
        }

        await SetAuthenticatedUser(user);
        return true;
    }

    public async Task LogoutAsync()
    {
        _currentUser = null;
        _requires2FA = false;
        _pending2FASecret = null;
        await Task.CompletedTask;
    }

    public async Task AdminBypassLoginAsync(int userId)
    {
        var col = _db.GetCollection<UserEntity>("users");
        var user = col.FindById(userId);
        if (user != null)
            await SetAuthenticatedUser(user);
    }

    public async Task<bool> RegisterAsync(string username, string password)
    {
        var col = _db.GetCollection<UserEntity>("users");
        var existing = col.FindOne(u => u.Name == username);
        if (existing != null) return false;

        var hasUsers = col.FindAll().Any();
        var user = new UserEntity
        {
            Name = username,
            PasswordHash = TotpHelper.HashPassword(password),
            TwoFactorEnabled = false,
            TwoFactorSecret = string.Empty,
            Role = hasUsers ? UserRole.Author : UserRole.Administrator
        };
        col.Insert(user);
        await SetAuthenticatedUser(user);
        return true;
    }

    public Task<UserEntity?> GetCurrentUserAsync()
    {
        return Task.FromResult(_currentUser);
    }

    public async Task<bool> Verify2FAAsync(string code)
    {
        if (string.IsNullOrEmpty(_pending2FASecret)) return false;

        if (!TotpHelper.VerifyCode(_pending2FASecret, code))
            return false;

        var col = _db.GetCollection<UserEntity>("users");
        var user = col.FindOne(u => u.TwoFactorSecret == _pending2FASecret);
        if (user == null) return false;

        await SetAuthenticatedUser(user);
        return true;
    }

    public Task<string> Generate2FASecretAsync()
    {
        return Task.FromResult(TotpHelper.GenerateSecret());
    }

    public string Get2FAProvisioningUri(string secret, string username)
    {
        return TotpHelper.GetProvisioningUri(secret, username);
    }

    public async Task<bool> Enable2FAAsync(string secret)
    {
        if (_currentUser == null) return false;

        var col = _db.GetCollection<UserEntity>("users");
        var user = col.FindById(_currentUser.Id);
        if (user == null) return false;

        user.TwoFactorSecret = secret;
        user.TwoFactorEnabled = true;
        col.Update(user);
        _currentUser.TwoFactorSecret = secret;
        _currentUser.TwoFactorEnabled = true;
        return true;
    }

    public async Task<bool> Disable2FAAsync()
    {
        if (_currentUser == null) return false;

        var col = _db.GetCollection<UserEntity>("users");
        var user = col.FindById(_currentUser.Id);
        if (user == null) return false;

        user.TwoFactorSecret = string.Empty;
        user.TwoFactorEnabled = false;
        col.Update(user);
        _currentUser.TwoFactorSecret = string.Empty;
        _currentUser.TwoFactorEnabled = false;
        return true;
    }

    public async Task<bool> Is2FAEnabledAsync()
    {
        if (_currentUser == null) return false;
        var col = _db.GetCollection<UserEntity>("users");
        var user = col.FindById(_currentUser.Id);
        return user?.TwoFactorEnabled ?? false;
    }

    public async Task<string?> GetCurrent2FASecretAsync()
    {
        if (_currentUser == null) return null;
        var col = _db.GetCollection<UserEntity>("users");
        var user = col.FindById(_currentUser.Id);
        return user?.TwoFactorSecret;
    }

    private async Task SetAuthenticatedUser(UserEntity user)
    {
        _currentUser = user;
        _requires2FA = false;
        _pending2FASecret = null;
        _settingsService.CurrentProjectId = null;
        _settingsService.CurrentFilePath = null;
        _settingsService.SetCurrentUser(user.Id);

        await _settingsService.InitializeAsync();
        await _loc.InitAsync();
    }

    public Task<List<UserEntity>> GetAllUsersAsync()
    {
        var col = _db.GetCollection<UserEntity>("users");
        return Task.FromResult(col.FindAll().OrderBy(u => u.Name).ToList());
    }

    public async Task<bool> SetUserActiveAsync(int userId, bool active)
    {
        if (_currentUser != null && userId == _currentUser.Id)
            return false;

        var col = _db.GetCollection<UserEntity>("users");
        var user = col.FindById(userId);
        if (user == null) return false;

        user.IsActive = active;
        col.Update(user);
        return true;
    }

    public async Task<bool> DeleteUserAsync(int userId)
    {
        if (_currentUser != null && userId == _currentUser.Id)
            return false;

        var col = _db.GetCollection<UserEntity>("users");
        var user = col.FindById(userId);
        if (user == null) return false;

        col.Delete(userId);
        return true;
    }
}
