using SameBeat.Domain.Enumy;
using SameBeat.Domain.Wyjatki;
namespace SameBeat.Domain.Encje;
///
/// Konto uzytkownika systemu -- widz, uczestnik na scenie, host imprezy albo moderator.
///
public class Uzytkownik
{
public Guid Id { get; private set; }
public string Nick { get; private set; } = string.Empty;
///
/// Hash hasla (Microsoft.AspNetCore.Identity.PasswordHasher), nigdy jawne haslo.
/// Puste tylko dla kont, ktore jeszcze nie maja ustawionego hasla logowania.
///
public string? HasloHash { get; private set; }
public RolaUzytkownika Rola { get; private set; }
public DateTime UtworzonyUtc { get; private set; }
public bool Zbanowany { get; private set; }
///
/// Liczba kolejnych nieudanych prob logowania NA TO KONTO (nie na IP -- to osobna warstwa,
/// patrz rate limiting /api/auth/login w Program.cs). Zerowana po kazdym udanym logowaniu.
///
public int NieudaneProbyLogowania { get; private set; }
/// Konto zablokowane do tego momentu (ETAP 7, ochrona przed brute force per-konto). Null = brak blokady.
public DateTime? ZablokowanyDoUtc { get; private set; }
// Prywatny konstruktor bez parametrow -- wymagany przez EF Core do materializacji encji z bazy.
private Uzytkownik()
{
}
private Uzytkownik(Guid id, string nick, RolaUzytkownika rola, DateTime utworzonyUtc)
{
Id = id;
Nick = nick;
Rola = rola;
UtworzonyUtc = utworzonyUtc;
Zbanowany = false;
}
///
/// Tworzy nowego uzytkownika. Walidacja nicku tutaj chroni przed powstaniem encji w niepoprawnym
/// stanie niezaleznie od tego, ktory serwis aplikacji jej uzyje.
///
public static Uzytkownik Utworz(string nick, RolaUzytkownika rola)
{
if (string.IsNullOrWhiteSpace(nick))
{
throw new NieprawidlowyStanEncjiWyjatek("Nick uzytkownika nie moze byc pusty.");
}
return new Uzytkownik(Guid.NewGuid(), nick.Trim(), rola, DateTime.UtcNow);
}
public void UstawHasloHash(string hasloHash)
{
if (string.IsNullOrWhiteSpace(hasloHash))
{
throw new NieprawidlowyStanEncjiWyjatek("Hash hasla nie moze byc pusty.");
}
HasloHash = hasloHash;
}
public void Zbanuj() => Zbanowany = true;
public void OdbanujOsobe() => Zbanowany = false;
/// Czy konto jest w tej chwili zablokowane po zbyt wielu nieudanych probach logowania.
public bool CzyZablokowany(DateTime terazUtc) => ZablokowanyDoUtc is DateTime blokadaDoUtc && blokadaDoUtc > terazUtc;
///
/// Odnotowuje nieudana probe logowania. Po osiagnieciu
/// konto zostaje zablokowane na -- niezaleznie od tego, z jakiego
/// adresu IP przychodza kolejne proby (to chroni PRZED atakiem rozproszonym na wiele IP, w
/// odroznieniu od rate limitingu per-IP na endpoincie /api/auth/login).
///
public void ZanotujNieudaneLogowanie(int maksymalnaLiczbaProb, TimeSpan czasBlokady)
{
NieudaneProbyLogowania++;
if (NieudaneProbyLogowania >= maksymalnaLiczbaProb)
{
ZablokowanyDoUtc = DateTime.UtcNow.Add(czasBlokady);
}
}
/// Udane logowanie zeruje licznik i zdejmuje ewentualna blokade.
public void ZanotujUdaneLogowanie()
{
NieudaneProbyLogowania = 0;
ZablokowanyDoUtc = null;
}
}