using OmsorgCore.Application.Abstractions;
using OmsorgCore.Domain.Entities;
using OmsorgCore.Domain.Enums;

namespace OmsorgCore.Tests.TestDoubles;

public class FakeUserRepository : IUserRepository
{
    private readonly List<User> _users;

    public FakeUserRepository(params User[] users)
    {
        _users = users.ToList();
    }

    public Task<User?> GetByUsernameAsync(string username, CancellationToken cancellationToken = default)
        => Task.FromResult(_users.FirstOrDefault(u => u.Username == username));

    public Task<User?> GetByIdWithPermissionsAsync(Guid id, CancellationToken cancellationToken = default)
        => Task.FromResult(_users.FirstOrDefault(u => u.Id == id));

    public Task<User?> GetByIdWithPermissionsNoTrackingAsync(Guid id, CancellationToken cancellationToken = default)
        => Task.FromResult(_users.FirstOrDefault(u => u.Id == id));

    public Task<User?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default)
        => Task.FromResult(_users.FirstOrDefault(u => u.Id == id));

    public Task<User?> GetByIdWithEmployeeAsync(Guid id, CancellationToken cancellationToken = default)
        => Task.FromResult(_users.FirstOrDefault(u => u.Id == id));

    public Task<IReadOnlyList<User>> GetAllAsync(CancellationToken cancellationToken = default)
        => Task.FromResult<IReadOnlyList<User>>(_users.ToList());

    public Task<IReadOnlyList<Guid>> GetUserIdsByRoleAsync(Guid roleId, CancellationToken cancellationToken = default)
        => Task.FromResult<IReadOnlyList<Guid>>(_users.Where(u => u.RoleId == roleId).Select(u => u.Id).ToList());

    public Task<User?> GetByEmployeeIdAsync(Guid employeeId, CancellationToken cancellationToken = default)
        => Task.FromResult(_users.FirstOrDefault(u => u.EmployeeId == employeeId));

    public Task<bool> ExistsByUsernameAsync(string username, CancellationToken cancellationToken = default)
        => Task.FromResult(_users.Any(u => u.Username == username));

    public Task AddAsync(User user, CancellationToken cancellationToken = default)
    {
        _users.Add(user);
        return Task.CompletedTask;
    }

    public Task AddPermissionOverrideAsync(UserPermissionOverride @override, CancellationToken cancellationToken = default)
    {
        var user = _users.First(u => u.Id == @override.UserId);
        user.PermissionOverrides.Add(@override);
        return Task.CompletedTask;
    }

    public Task SaveChangesAsync(CancellationToken cancellationToken = default)
        => Task.CompletedTask;
}

public class FakeRefreshTokenRepository : IRefreshTokenRepository
{
    private readonly Dictionary<string, RefreshToken> _tokensByHash = new();

    public Task AddAsync(RefreshToken token, CancellationToken cancellationToken = default)
    {
        _tokensByHash[token.TokenHash] = token;
        return Task.CompletedTask;
    }

    public Task<RefreshToken?> GetByTokenHashAsync(string tokenHash, CancellationToken cancellationToken = default)
        => Task.FromResult(_tokensByHash.GetValueOrDefault(tokenHash));

    public Task<RefreshToken?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default)
        => Task.FromResult(_tokensByHash.Values.FirstOrDefault(t => t.Id == id));

    public Task<IReadOnlyList<RefreshToken>> GetAllActiveAsync(CancellationToken cancellationToken = default)
    {
        var now = DateTime.UtcNow;
        IReadOnlyList<RefreshToken> active = _tokensByHash.Values
            .Where(t => t.RevokedAt == null && t.ExpiresAt > now)
            .ToList();
        return Task.FromResult(active);
    }

    public Task<IReadOnlyList<RefreshToken>> GetActiveByUserIdAsync(Guid userId, CancellationToken cancellationToken = default)
    {
        var now = DateTime.UtcNow;
        IReadOnlyList<RefreshToken> active = _tokensByHash.Values
            .Where(t => t.UserId == userId && t.RevokedAt == null && t.ExpiresAt > now)
            .ToList();
        return Task.FromResult(active);
    }

    public Task SaveChangesAsync(CancellationToken cancellationToken = default)
        => Task.CompletedTask;
}

public class FakeEmployeeRepository : IEmployeeRepository
{
    private readonly List<Employee> _employees = new();

    public Task<Employee?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default)
        => Task.FromResult(_employees.FirstOrDefault(e => e.Id == id));

    public Task<IReadOnlyList<Employee>> GetAllAsync(CancellationToken cancellationToken = default)
        => Task.FromResult<IReadOnlyList<Employee>>(_employees.ToList());

    public Task<(IReadOnlyList<Employee> Items, int TotalCount)> GetPagedAsync(
        string? search,
        string? status,
        string? employmentType,
        int page,
        int pageSize,
        CancellationToken cancellationToken = default,
        Guid? restrictToEmployeeId = null)
    {
        IEnumerable<Employee> query = _employees.Where(e => !e.IsDeleted);

        if (restrictToEmployeeId.HasValue)
        {
            query = query.Where(e => e.Id == restrictToEmployeeId.Value);
        }

        if (!string.IsNullOrWhiteSpace(search))
        {
            var term = search.Trim();
            query = query.Where(e =>
                e.FirstName.Contains(term, StringComparison.OrdinalIgnoreCase) ||
                e.LastName.Contains(term, StringComparison.OrdinalIgnoreCase) ||
                (e.Email != null && e.Email.Contains(term, StringComparison.OrdinalIgnoreCase)));
        }

        if (!string.IsNullOrWhiteSpace(status))
        {
            query = query.Where(e => e.Status == status);
        }

        if (!string.IsNullOrWhiteSpace(employmentType))
        {
            query = query.Where(e => e.EmploymentType == employmentType);
        }

        var ordered = query.OrderBy(e => e.LastName).ThenBy(e => e.FirstName).ToList();
        var totalCount = ordered.Count;
        var items = ordered.Skip((page - 1) * pageSize).Take(pageSize).ToList();

        return Task.FromResult<(IReadOnlyList<Employee> Items, int TotalCount)>((items, totalCount));
    }

    public Task AddAsync(Employee employee, CancellationToken cancellationToken = default)
    {
        _employees.Add(employee);
        return Task.CompletedTask;
    }

    public Task UpdateAsync(Employee employee, CancellationToken cancellationToken = default)
        => Task.CompletedTask;

    public Task<bool> SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default)
    {
        var employee = _employees.FirstOrDefault(e => e.Id == id);
        if (employee is null)
        {
            return Task.FromResult(false);
        }

        employee.IsDeleted = true;
        employee.DeletedAt = DateTime.UtcNow;
        return Task.FromResult(true);
    }

    public Task<IReadOnlyList<Employee>> GetDeletedAsync(string? search, CancellationToken cancellationToken = default)
        => Task.FromResult<IReadOnlyList<Employee>>(
            _employees.Where(e => e.IsDeleted).OrderByDescending(e => e.DeletedAt).ToList());

    public Task<bool> RestoreAsync(Guid id, CancellationToken cancellationToken = default)
    {
        var employee = _employees.FirstOrDefault(e => e.Id == id && e.IsDeleted);
        if (employee is null)
        {
            return Task.FromResult(false);
        }

        employee.IsDeleted = false;
        employee.DeletedAt = null;
        return Task.FromResult(true);
    }

    public Task SaveChangesAsync(CancellationToken cancellationToken = default)
        => Task.CompletedTask;
}

public class FakeFacilityRepository : IFacilityRepository
{
    private readonly List<Facility> _facilities = new();

    public Task<Facility?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default)
        => Task.FromResult(_facilities.FirstOrDefault(f => f.Id == id));

    public Task<IReadOnlyList<Facility>> GetAllAsync(CancellationToken cancellationToken = default)
        => Task.FromResult<IReadOnlyList<Facility>>(_facilities.ToList());

    public Task<(IReadOnlyList<Facility> Items, int TotalCount)> GetPagedAsync(
        string? search,
        string? crmStatus,
        bool followUpDueOnly,
        int page,
        int pageSize,
        CancellationToken cancellationToken = default)
    {
        IEnumerable<Facility> query = _facilities.Where(f => !f.IsDeleted);

        if (!string.IsNullOrWhiteSpace(search))
        {
            var term = search.Trim();
            query = query.Where(f => f.Name.Contains(term, StringComparison.OrdinalIgnoreCase));
        }

        if (!string.IsNullOrWhiteSpace(crmStatus))
        {
            query = query.Where(f => f.CrmStatus == crmStatus);
        }

        if (followUpDueOnly)
        {
            query = query.Where(f => f.FollowUpDueDate != null);
        }

        var ordered = followUpDueOnly
            ? query.OrderBy(f => f.FollowUpDueDate).ToList()
            : query.OrderBy(f => f.Name).ToList();
        var totalCount = ordered.Count;
        var items = ordered.Skip((page - 1) * pageSize).Take(pageSize).ToList();

        return Task.FromResult<(IReadOnlyList<Facility> Items, int TotalCount)>((items, totalCount));
    }

    public Task AddAsync(Facility facility, CancellationToken cancellationToken = default)
    {
        _facilities.Add(facility);
        return Task.CompletedTask;
    }

    public Task UpdateAsync(Facility facility, CancellationToken cancellationToken = default)
        => Task.CompletedTask;

    public Task<bool> SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default)
    {
        var facility = _facilities.FirstOrDefault(f => f.Id == id);
        if (facility is null)
        {
            return Task.FromResult(false);
        }

        facility.IsDeleted = true;
        facility.DeletedAt = DateTime.UtcNow;
        return Task.FromResult(true);
    }

    public Task<IReadOnlyList<Facility>> GetDeletedAsync(string? search, CancellationToken cancellationToken = default)
        => Task.FromResult<IReadOnlyList<Facility>>(
            _facilities.Where(f => f.IsDeleted).OrderByDescending(f => f.DeletedAt).ToList());

    public Task<bool> RestoreAsync(Guid id, CancellationToken cancellationToken = default)
    {
        var facility = _facilities.FirstOrDefault(f => f.Id == id && f.IsDeleted);
        if (facility is null)
        {
            return Task.FromResult(false);
        }

        facility.IsDeleted = false;
        facility.DeletedAt = null;
        return Task.FromResult(true);
    }

    public Task SaveChangesAsync(CancellationToken cancellationToken = default)
        => Task.CompletedTask;
}

public class FakeFacilityContactRepository : IFacilityContactRepository
{
    private readonly List<FacilityContact> _contacts = new();

    public Task<FacilityContact?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default)
        => Task.FromResult(_contacts.FirstOrDefault(c => c.Id == id));

    public Task<IReadOnlyList<FacilityContact>> GetByFacilityIdAsync(Guid facilityId, CancellationToken cancellationToken = default)
        => Task.FromResult<IReadOnlyList<FacilityContact>>(
            _contacts.Where(c => c.FacilityId == facilityId).OrderBy(c => c.Name).ToList());

    public Task AddAsync(FacilityContact contact, CancellationToken cancellationToken = default)
    {
        _contacts.Add(contact);
        return Task.CompletedTask;
    }

    public Task UpdateAsync(FacilityContact contact, CancellationToken cancellationToken = default)
        => Task.CompletedTask;

    public Task<bool> SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default)
    {
        var contact = _contacts.FirstOrDefault(c => c.Id == id);
        if (contact is null)
        {
            return Task.FromResult(false);
        }

        contact.IsDeleted = true;
        contact.DeletedAt = DateTime.UtcNow;
        return Task.FromResult(true);
    }

    public Task<IReadOnlyList<FacilityContact>> GetDeletedAsync(string? search, CancellationToken cancellationToken = default)
        => Task.FromResult<IReadOnlyList<FacilityContact>>(
            _contacts.Where(c => c.IsDeleted).OrderByDescending(c => c.DeletedAt).ToList());

    public Task<bool> RestoreAsync(Guid id, CancellationToken cancellationToken = default)
    {
        var contact = _contacts.FirstOrDefault(c => c.Id == id && c.IsDeleted);
        if (contact is null)
        {
            return Task.FromResult(false);
        }

        contact.IsDeleted = false;
        contact.DeletedAt = null;
        return Task.FromResult(true);
    }

    public Task SaveChangesAsync(CancellationToken cancellationToken = default)
        => Task.CompletedTask;
}

public class FakeFacilityQualificationRateRepository : IFacilityQualificationRateRepository
{
    private readonly List<FacilityQualificationRate> _rates = new();

    public Task<FacilityQualificationRate?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default)
        => Task.FromResult(_rates.FirstOrDefault(r => r.Id == id));

    public Task<IReadOnlyList<FacilityQualificationRate>> GetByFacilityIdAsync(Guid facilityId, CancellationToken cancellationToken = default)
        => Task.FromResult<IReadOnlyList<FacilityQualificationRate>>(
            _rates.Where(r => r.FacilityId == facilityId).OrderBy(r => r.Qualification).ToList());

    public Task AddAsync(FacilityQualificationRate rate, CancellationToken cancellationToken = default)
    {
        _rates.Add(rate);
        return Task.CompletedTask;
    }

    public Task UpdateAsync(FacilityQualificationRate rate, CancellationToken cancellationToken = default)
        => Task.CompletedTask;

    public Task<bool> SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default)
    {
        var rate = _rates.FirstOrDefault(r => r.Id == id);
        if (rate is null)
        {
            return Task.FromResult(false);
        }

        rate.IsDeleted = true;
        rate.DeletedAt = DateTime.UtcNow;
        return Task.FromResult(true);
    }

    public Task<IReadOnlyList<FacilityQualificationRate>> GetDeletedAsync(string? search, CancellationToken cancellationToken = default)
        => Task.FromResult<IReadOnlyList<FacilityQualificationRate>>(
            _rates.Where(r => r.IsDeleted).OrderByDescending(r => r.DeletedAt).ToList());

    public Task<bool> RestoreAsync(Guid id, CancellationToken cancellationToken = default)
    {
        var rate = _rates.FirstOrDefault(r => r.Id == id && r.IsDeleted);
        if (rate is null)
        {
            return Task.FromResult(false);
        }

        rate.IsDeleted = false;
        rate.DeletedAt = null;
        return Task.FromResult(true);
    }

    public Task SaveChangesAsync(CancellationToken cancellationToken = default)
        => Task.CompletedTask;
}

public class FakeEmployeeFacilityDistanceRepository : IEmployeeFacilityDistanceRepository
{
    private readonly List<EmployeeFacilityDistance> _distances = new();

    public Task<EmployeeFacilityDistance?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default)
        => Task.FromResult(_distances.FirstOrDefault(d => d.Id == id));

    public Task<IReadOnlyList<EmployeeFacilityDistance>> GetByFacilityIdAsync(Guid facilityId, CancellationToken cancellationToken = default)
        => Task.FromResult<IReadOnlyList<EmployeeFacilityDistance>>(
            _distances.Where(d => d.FacilityId == facilityId).ToList());

    public Task<EmployeeFacilityDistance?> GetByFacilityAndEmployeeAsync(Guid facilityId, Guid employeeId, CancellationToken cancellationToken = default)
        => Task.FromResult(_distances.FirstOrDefault(d => d.FacilityId == facilityId && d.EmployeeId == employeeId));

    public Task AddAsync(EmployeeFacilityDistance distance, CancellationToken cancellationToken = default)
    {
        _distances.Add(distance);
        return Task.CompletedTask;
    }

    public Task UpdateAsync(EmployeeFacilityDistance distance, CancellationToken cancellationToken = default)
        => Task.CompletedTask;

    public Task<bool> SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default)
    {
        var distance = _distances.FirstOrDefault(d => d.Id == id);
        if (distance is null)
        {
            return Task.FromResult(false);
        }

        distance.IsDeleted = true;
        distance.DeletedAt = DateTime.UtcNow;
        return Task.FromResult(true);
    }

    public Task<IReadOnlyList<EmployeeFacilityDistance>> GetDeletedAsync(string? search, CancellationToken cancellationToken = default)
        => Task.FromResult<IReadOnlyList<EmployeeFacilityDistance>>(
            _distances.Where(d => d.IsDeleted).OrderByDescending(d => d.DeletedAt).ToList());

    public Task<bool> RestoreAsync(Guid id, CancellationToken cancellationToken = default)
    {
        var distance = _distances.FirstOrDefault(d => d.Id == id && d.IsDeleted);
        if (distance is null)
        {
            return Task.FromResult(false);
        }

        distance.IsDeleted = false;
        distance.DeletedAt = null;
        return Task.FromResult(true);
    }

    public Task SaveChangesAsync(CancellationToken cancellationToken = default)
        => Task.CompletedTask;
}

public class FakeContractRepository : IContractRepository
{
    private readonly List<Contract> _contracts = new();

    public Task<Contract?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default)
        => Task.FromResult(_contracts.FirstOrDefault(c => c.Id == id));

    public Task<IReadOnlyList<Contract>> GetAllAsync(CancellationToken cancellationToken = default)
        => Task.FromResult<IReadOnlyList<Contract>>(_contracts.ToList());

    public Task<(IReadOnlyList<Contract> Items, int TotalCount)> GetPagedAsync(
        string? search,
        string? status,
        Guid? employeeId,
        Guid? facilityId,
        int page,
        int pageSize,
        CancellationToken cancellationToken = default)
    {
        IEnumerable<Contract> query = _contracts.Where(c => !c.IsDeleted);

        if (!string.IsNullOrWhiteSpace(search))
        {
            var term = search.Trim();
            query = query.Where(c => c.ContractType.Contains(term, StringComparison.OrdinalIgnoreCase));
        }

        if (!string.IsNullOrWhiteSpace(status))
        {
            query = query.Where(c => c.Status == status);
        }

        if (employeeId.HasValue)
        {
            query = query.Where(c => c.EmployeeId == employeeId.Value);
        }

        if (facilityId.HasValue)
        {
            query = query.Where(c => c.FacilityId == facilityId.Value);
        }

        var ordered = query.OrderBy(c => c.StartDate).ToList();
        var totalCount = ordered.Count;
        var items = ordered.Skip((page - 1) * pageSize).Take(pageSize).ToList();

        return Task.FromResult<(IReadOnlyList<Contract> Items, int TotalCount)>((items, totalCount));
    }

    public Task<Contract?> GetActiveForEmployeeCoveringRangeAsync(
        Guid employeeId,
        DateOnly startDate,
        DateOnly endDate,
        CancellationToken cancellationToken = default)
        => Task.FromResult(_contracts.FirstOrDefault(c =>
            !c.IsDeleted && c.EmployeeId == employeeId && c.Status != "Entwurf" &&
            c.StartDate <= startDate && (c.EndDate == null || c.EndDate >= endDate)));

    public Task AddAsync(Contract contract, CancellationToken cancellationToken = default)
    {
        _contracts.Add(contract);
        return Task.CompletedTask;
    }

    public Task UpdateAsync(Contract contract, CancellationToken cancellationToken = default)
        => Task.CompletedTask;

    public Task<bool> SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default)
    {
        var contract = _contracts.FirstOrDefault(c => c.Id == id);
        if (contract is null)
        {
            return Task.FromResult(false);
        }

        contract.IsDeleted = true;
        contract.DeletedAt = DateTime.UtcNow;
        return Task.FromResult(true);
    }

    public Task<IReadOnlyList<Contract>> GetDeletedAsync(string? search, CancellationToken cancellationToken = default)
        => Task.FromResult<IReadOnlyList<Contract>>(
            _contracts.Where(c => c.IsDeleted).OrderByDescending(c => c.DeletedAt).ToList());

    public Task<bool> RestoreAsync(Guid id, CancellationToken cancellationToken = default)
    {
        var contract = _contracts.FirstOrDefault(c => c.Id == id && c.IsDeleted);
        if (contract is null)
        {
            return Task.FromResult(false);
        }

        contract.IsDeleted = false;
        contract.DeletedAt = null;
        return Task.FromResult(true);
    }

    public Task SaveChangesAsync(CancellationToken cancellationToken = default)
        => Task.CompletedTask;
}

public class FakeRoleRepository : IRoleRepository
{
    private readonly List<Role> _roles;

    public FakeRoleRepository(params Role[] roles)
    {
        _roles = roles.ToList();
    }

    public Task<IReadOnlyList<Role>> GetAllAsync(CancellationToken cancellationToken = default)
        => Task.FromResult<IReadOnlyList<Role>>(_roles.ToList());

    public Task<Role?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default)
        => Task.FromResult(_roles.FirstOrDefault(r => r.Id == id));

    public Task<Role?> GetByIdWithPermissionsAsync(Guid id, CancellationToken cancellationToken = default)
        => Task.FromResult(_roles.FirstOrDefault(r => r.Id == id));

    public Task<bool> ExistsByNameAsync(string name, CancellationToken cancellationToken = default)
        => Task.FromResult(_roles.Any(r => r.Name == name));

    public Task AddAsync(Role role, CancellationToken cancellationToken = default)
    {
        _roles.Add(role);
        return Task.CompletedTask;
    }

    public Task RemoveAsync(Role role, CancellationToken cancellationToken = default)
    {
        _roles.RemoveAll(r => r.Id == role.Id);
        return Task.CompletedTask;
    }

    public Task AddPermissionRangeAsync(IEnumerable<RolePermission> permissions, CancellationToken cancellationToken = default)
    {
        foreach (var permission in permissions)
        {
            var role = _roles.First(r => r.Id == permission.RoleId);
            role.RolePermissions.Add(permission);
        }
        return Task.CompletedTask;
    }

    public Task SaveChangesAsync(CancellationToken cancellationToken = default)
        => Task.CompletedTask;
}

public class FakePasswordHasher : IPasswordHasher
{
    public string Hash(string plainPassword) => $"hashed:{plainPassword}";
    public bool Verify(string plainPassword, string hash) => hash == $"hashed:{plainPassword}";
}

public class FakeJwtTokenGenerator : IJwtTokenGenerator
{
    public (string Token, DateTime ExpiresAt) GenerateToken(User user)
        => ($"access-token-for-{user.Id}-{Guid.NewGuid()}", DateTime.UtcNow.AddMinutes(60));
}

public class FakeRefreshTokenGenerator : IRefreshTokenGenerator
{
    public (string RawToken, string TokenHash, DateTime ExpiresAt) GenerateToken()
    {
        var rawToken = Guid.NewGuid().ToString();
        return (rawToken, HashToken(rawToken), DateTime.UtcNow.AddDays(60));
    }

    public string HashToken(string rawToken) => $"hash:{rawToken}";
}

public class FakePasswordResetCodeRepository : IPasswordResetCodeRepository
{
    private readonly List<PasswordResetCode> _codes = new();

    public Task AddAsync(PasswordResetCode code, CancellationToken cancellationToken = default)
    {
        _codes.Add(code);
        return Task.CompletedTask;
    }

    public Task<PasswordResetCode?> GetActiveByUserIdAsync(Guid userId, int maxAttempts, CancellationToken cancellationToken = default)
    {
        var now = DateTime.UtcNow;
        var active = _codes
            .Where(c => c.UserId == userId && c.InvalidatedAt == null && c.ConsumedAt == null
                && c.ExpiresAt > now && c.AttemptCount < maxAttempts)
            .OrderByDescending(c => c.CreatedAt)
            .FirstOrDefault();
        return Task.FromResult(active);
    }

    public Task<PasswordResetCode?> GetLatestByUserIdAsync(Guid userId, CancellationToken cancellationToken = default)
        => Task.FromResult(_codes.Where(c => c.UserId == userId).OrderByDescending(c => c.CreatedAt).FirstOrDefault());

    public Task<PasswordResetCode?> GetByResetTokenHashAsync(string resetTokenHash, CancellationToken cancellationToken = default)
        => Task.FromResult(_codes.FirstOrDefault(c => c.ResetTokenHash == resetTokenHash));

    public Task InvalidateActiveForUserAsync(Guid userId, CancellationToken cancellationToken = default)
    {
        var now = DateTime.UtcNow;
        foreach (var code in _codes.Where(c => c.UserId == userId && c.InvalidatedAt == null && c.ConsumedAt == null && c.ExpiresAt > now))
        {
            code.InvalidatedAt = now;
        }
        return Task.CompletedTask;
    }

    public Task SaveChangesAsync(CancellationToken cancellationToken = default)
        => Task.CompletedTask;
}

public class FakePasswordResetCodeGenerator : IPasswordResetCodeGenerator
{
    public TimeSpan RequestCooldown { get; set; } = TimeSpan.Zero;
    public int MaxAttempts { get; set; } = 3;

    public (string RawPin, string PinHash, DateTime ExpiresAt) GeneratePin()
        => GeneratePin(TimeSpan.FromMinutes(5));

    public (string RawPin, string PinHash, DateTime ExpiresAt) GeneratePin(TimeSpan validity)
    {
        var rawPin = "123456";
        return (rawPin, HashPin(rawPin), DateTime.UtcNow.Add(validity));
    }

    public (string RawResetToken, string ResetTokenHash, DateTime ExpiresAt) GenerateResetToken()
    {
        var rawResetToken = Guid.NewGuid().ToString();
        return (rawResetToken, HashResetToken(rawResetToken), DateTime.UtcNow.AddMinutes(10));
    }

    public string HashPin(string rawPin) => $"pinhash:{rawPin}";

    public string HashResetToken(string rawResetToken) => $"resethash:{rawResetToken}";
}

public class FakeLoginAttemptRepository : ILoginAttemptRepository
{
    private readonly List<LoginAttempt> _attempts = new();

    public Task<int> CountRecentFailuresAsync(string ipAddress, DateTime since, CancellationToken cancellationToken = default)
        => Task.FromResult(_attempts.Count(a => a.IpAddress == ipAddress && !a.Succeeded && a.AttemptedAt > since));

    public Task AddAsync(LoginAttempt attempt, CancellationToken cancellationToken = default)
    {
        _attempts.Add(attempt);
        return Task.CompletedTask;
    }

    public Task ClearFailuresAsync(string ipAddress, CancellationToken cancellationToken = default)
    {
        _attempts.RemoveAll(a => a.IpAddress == ipAddress && !a.Succeeded);
        return Task.CompletedTask;
    }

    public Task SaveChangesAsync(CancellationToken cancellationToken = default)
        => Task.CompletedTask;
}

public class FakeLoginLockoutPolicy : ILoginLockoutPolicy
{
    public int MaxFailures { get; set; } = 5;
    public TimeSpan LockoutWindow { get; set; } = TimeSpan.FromMinutes(10);
}

public class FakePasswordPolicy : IPasswordPolicy
{
    public int MinLength { get; set; } = 8;

    public bool IsValid(string? password) => !string.IsNullOrEmpty(password) && password.Length >= MinLength;
}

public class FakeAuditLogRepository : IAuditLogRepository
{
    public List<AuditLogEntry> Entries { get; } = new();

    public Task AddAsync(AuditLogEntry entry, CancellationToken cancellationToken = default)
    {
        Entries.Add(entry);
        return Task.CompletedTask;
    }

    public Task<(IReadOnlyList<AuditLogEntry> Items, int TotalCount)> GetPagedAsync(
        string? entityType,
        Guid? entityId,
        Guid? actorUserId,
        AuditEventCategory? category,
        DateTime? fromUtc,
        DateTime? toUtc,
        int page,
        int pageSize,
        CancellationToken cancellationToken = default)
    {
        IReadOnlyList<AuditLogEntry> items = Entries.ToList();
        return Task.FromResult((items, items.Count));
    }

    public Task SaveChangesAsync(CancellationToken cancellationToken = default)
        => Task.CompletedTask;
}

public class FakeOrderRepository : IOrderRepository
{
    private readonly List<Order> _orders = new();

    public Task<Order?> GetByIdAsync(Guid id, CancellationToken cancellationToken = default)
        => Task.FromResult(_orders.FirstOrDefault(o => o.Id == id));

    public Task<IReadOnlyList<Order>> GetAllAsync(CancellationToken cancellationToken = default)
        => Task.FromResult<IReadOnlyList<Order>>(_orders.ToList());

    public Task<(IReadOnlyList<Order> Items, int TotalCount)> GetPagedAsync(
        string? search,
        Guid? statusId,
        Guid? facilityId,
        string? priority,
        string? requiredQualification,
        string? shiftType,
        int page,
        int pageSize,
        CancellationToken cancellationToken = default)
    {
        IEnumerable<Order> query = _orders.Where(o => !o.IsDeleted);

        if (!string.IsNullOrWhiteSpace(search))
        {
            var term = search.Trim();
            query = query.Where(o => o.RequiredQualification != null && o.RequiredQualification.Contains(term, StringComparison.OrdinalIgnoreCase));
        }

        if (statusId.HasValue)
        {
            query = query.Where(o => o.StatusId == statusId.Value);
        }

        if (facilityId.HasValue)
        {
            query = query.Where(o => o.FacilityId == facilityId.Value);
        }

        if (!string.IsNullOrWhiteSpace(priority))
        {
            query = query.Where(o => o.Priority == priority);
        }

        if (!string.IsNullOrWhiteSpace(requiredQualification))
        {
            query = query.Where(o => o.RequiredQualification == requiredQualification);
        }

        if (!string.IsNullOrWhiteSpace(shiftType))
        {
            query = query.Where(o => o.ShiftType == shiftType);
        }

        var ordered = query.OrderBy(o => o.StartDate).ToList();
        var totalCount = ordered.Count;
        var items = ordered.Skip((page - 1) * pageSize).Take(pageSize).ToList();

        return Task.FromResult<(IReadOnlyList<Order> Items, int TotalCount)>((items, totalCount));
    }

    public Task AddAsync(Order order, CancellationToken cancellationToken = default)
    {
        _orders.Add(order);
        return Task.CompletedTask;
    }

    public Task UpdateAsync(Order order, CancellationToken cancellationToken = default)
        => Task.CompletedTask;

    public Task<bool> SoftDeleteAsync(Guid id, CancellationToken cancellationToken = default)
    {
        var order = _orders.FirstOrDefault(o => o.Id == id);
        if (order is null)
        {
            return Task.FromResult(false);
        }

        order.IsDeleted = true;
        order.DeletedAt = DateTime.UtcNow;
        return Task.FromResult(true);
    }

    public Task<IReadOnlyList<Order>> GetDeletedAsync(string? search, CancellationToken cancellationToken = default)
        => Task.FromResult<IReadOnlyList<Order>>(
            _orders.Where(o => o.IsDeleted).OrderByDescending(o => o.DeletedAt).ToList());

    public Task<bool> RestoreAsync(Guid id, CancellationToken cancellationToken = default)
    {
        var order = _orders.FirstOrDefault(o => o.Id == id && o.IsDeleted);
        if (order is null)
        {
            return Task.FromResult(false);
        }

        order.IsDeleted = false;
        order.DeletedAt = null;
        return Task.FromResult(true);
    }

    public Task SaveChangesAsync(CancellationToken cancellationToken = default)
        => Task.CompletedTask;
}

public class FakeValueListRepository : IValueListRepository
{
    private readonly List<ValueList> _lists = new();
    private readonly List<ValueListItem> _items = new();
    private readonly List<ValueListItemTransition> _transitions = new();

    public ValueListItem AddStatus(string name, bool isInitial = false, bool isTerminal = false, int? sortOrder = null, string key = "OrderStatus")
    {
        var list = _lists.FirstOrDefault(l => l.Key == key);
        if (list is null)
        {
            list = new ValueList { Key = key, DisplayName = key };
            _lists.Add(list);
        }

        var item = new ValueListItem
        {
            ValueListId = list.Id,
            ValueList = list,
            Value = name,
            IsInitial = isInitial,
            IsTerminal = isTerminal,
            SortOrder = sortOrder ?? _items.Count(i => i.ValueListId == list.Id)
        };
        _items.Add(item);
        return item;
    }

    public void AllowTransition(Guid fromItemId, Guid toItemId, bool requiresApproval = true)
        => _transitions.Add(new ValueListItemTransition { FromItemId = fromItemId, ToItemId = toItemId, RequiresApproval = requiresApproval });

    public Task<IReadOnlyList<ValueList>> GetAllListsAsync(CancellationToken cancellationToken = default)
        => Task.FromResult<IReadOnlyList<ValueList>>(_lists.ToList());

    public Task<ValueList?> GetListByKeyAsync(string key, CancellationToken cancellationToken = default)
        => Task.FromResult(_lists.FirstOrDefault(l => l.Key == key));

    public Task<IReadOnlyList<ValueListItem>> GetItemsAsync(string key, CancellationToken cancellationToken = default)
        => Task.FromResult<IReadOnlyList<ValueListItem>>(_items.Where(i => i.ValueList.Key == key).OrderBy(i => i.SortOrder).ToList());

    public Task<IReadOnlyList<string>> GetActiveValuesAsync(string key, CancellationToken cancellationToken = default)
        => Task.FromResult<IReadOnlyList<string>>(_items.Where(i => i.ValueList.Key == key).OrderBy(i => i.SortOrder).Select(i => i.Value).ToList());

    public Task<ValueListItem?> GetItemByIdAsync(Guid id, CancellationToken cancellationToken = default)
        => Task.FromResult(_items.FirstOrDefault(i => i.Id == id));

    public Task<ValueListItem?> GetInitialItemAsync(string key, CancellationToken cancellationToken = default)
        => Task.FromResult(_items.FirstOrDefault(i => i.ValueList.Key == key && i.IsInitial));

    public Task AddItemAsync(ValueListItem item, CancellationToken cancellationToken = default)
    {
        _items.Add(item);
        return Task.CompletedTask;
    }

    public Task UpdateItemAsync(ValueListItem item, CancellationToken cancellationToken = default)
        => Task.CompletedTask;

    public Task RemoveItemAsync(ValueListItem item, CancellationToken cancellationToken = default)
    {
        _items.Remove(item);
        return Task.CompletedTask;
    }

    public Task<bool> CanTransitionAsync(Guid fromItemId, Guid toItemId, CancellationToken cancellationToken = default)
    {
        if (fromItemId == toItemId)
        {
            return Task.FromResult(true);
        }

        return Task.FromResult(_transitions.Any(t => t.FromItemId == fromItemId && t.ToItemId == toItemId));
    }

    public Task<IReadOnlyList<ValueListItemTransition>> GetTransitionsAsync(string key, CancellationToken cancellationToken = default)
        => Task.FromResult<IReadOnlyList<ValueListItemTransition>>(_transitions.Where(t => t.FromItem.ValueList.Key == key).ToList());

    public Task ReplaceTransitionsAsync(string key, IEnumerable<(Guid FromItemId, Guid ToItemId)> transitions, CancellationToken cancellationToken = default)
    {
        _transitions.RemoveAll(t => t.FromItem?.ValueList.Key == key);
        foreach (var (fromItemId, toItemId) in transitions)
        {
            _transitions.Add(new ValueListItemTransition { FromItemId = fromItemId, ToItemId = toItemId });
        }
        return Task.CompletedTask;
    }

    public Task<ValueListItemTransition?> GetSelfServiceTransitionAsync(Guid fromItemId, CancellationToken cancellationToken = default)
        => Task.FromResult(_transitions.FirstOrDefault(t => t.FromItemId == fromItemId && !t.RequiresApproval));

    public Task<ValueListItemTransition?> GetTransitionAsync(Guid fromItemId, Guid toItemId, CancellationToken cancellationToken = default)
        => Task.FromResult(_transitions.FirstOrDefault(t => t.FromItemId == fromItemId && t.ToItemId == toItemId));

    public Task SaveChangesAsync(CancellationToken cancellationToken = default)
        => Task.CompletedTask;
}

/// <summary>Konfigurierbarer Fake für ICurrentUserService - default: kein eingeloggter User (UserId null), damit scope-abhängige Services ohne Einschränkung testbar bleiben, wenn UserId nicht explizit gesetzt wird.</summary>
public class FakeCurrentUserService : ICurrentUserService
{
    public Guid? UserId { get; set; }
    public bool IsAuthenticated { get; set; }
    public string? Username { get; set; }
    public string? RoleName { get; set; }
    public string? IpAddress { get; set; }
    public Guid? EmployeeId { get; set; }
}
