using Microsoft.EntityFrameworkCore;

public class UserDirectoryService : IUserDirectoryService
{
    private readonly AppDbContext _db;

    public UserDirectoryService(AppDbContext db) => _db = db;

    public async Task<IReadOnlyList<UserSummaryResponse>> ListByRoleAsync(UserRole? role)
    {
        var query = _db.Users.AsNoTracking().Where(u => u.IsActive);

        if (role.HasValue)
            query = query.Where(u => u.Role == role.Value);

        return await query
            .OrderBy(u => u.FullName)
            .Select(u => new UserSummaryResponse(
                u.Id,
                u.Username,
                u.FullName,
                u.Role.ToDbString()))
            .ToListAsync();
    }

    public async Task<UserSummaryResponse> CreateAsync(CreateUserRequest request)
    {
        var exists = await _db.Users.AnyAsync(u => u.Username == request.Username);
        if (exists)
            throw new ConflictException(
                $"Username '{request.Username}' is already taken.",
                "USERNAME_TAKEN");

        var user = new User
        {
            Id = Guid.NewGuid(),
            Username = request.Username,
            PasswordHash = BCrypt.Net.BCrypt.HashPassword(request.Password),
            FullName = request.FullName,
            Role = UserRoleExtensions.FromDbString(request.Role),
            IsActive = true,
            CreatedAt = DateTimeOffset.UtcNow
        };

        _db.Users.Add(user);
        await _db.SaveChangesAsync();

        return new UserSummaryResponse(user.Id, user.Username, user.FullName, user.Role.ToDbString());
    }

    public async Task<UserSummaryResponse> UpdateAsync(Guid userId, UpdateUserRequest request)
    {
        var user = await _db.Users.FindAsync(userId);
        if (user is null)
            throw new NotFoundException("User not found.", "USER_NOT_FOUND");

        if (request.FullName is not null)
            user.FullName = request.FullName;

        if (request.Role is not null)
            user.Role = UserRoleExtensions.FromDbString(request.Role);

        if (request.IsActive.HasValue)
            user.IsActive = request.IsActive.Value;

        await _db.SaveChangesAsync();

        return new UserSummaryResponse(user.Id, user.Username, user.FullName, user.Role.ToDbString());
    }

    public async Task ResetPasswordAsync(Guid userId, string newPassword)
    {
        var user = await _db.Users.FindAsync(userId);
        if (user is null)
            throw new NotFoundException("User not found.", "USER_NOT_FOUND");

        user.PasswordHash = BCrypt.Net.BCrypt.HashPassword(newPassword);
        await _db.SaveChangesAsync();
    }

    public async Task ChangePasswordAsync(Guid userId, string currentPassword, string newPassword)
    {
        var user = await _db.Users.FindAsync(userId);
        if (user is null)
            throw new NotFoundException("User not found.", "USER_NOT_FOUND");

        if (!BCrypt.Net.BCrypt.Verify(currentPassword, user.PasswordHash))
            throw new ValidationException(
                "Current password is incorrect.",
                "INVALID_PASSWORD");

        user.PasswordHash = BCrypt.Net.BCrypt.HashPassword(newPassword);
        await _db.SaveChangesAsync();
    }
}
