using Microsoft.EntityFrameworkCore;

public class UserService : IUserService
{
    private readonly AppDbContext _db;
    private readonly ICurrentUserService _currentUser;

    public UserService(AppDbContext db, ICurrentUserService currentUser)
    {
        _db = db;
        _currentUser = currentUser;
    }

    public async Task<List<ClinicalUserResponse>> ListAsync()
    {
        var users = await _db.ClinicalUsers
            .AsNoTracking()
            .OrderBy(u => u.Username)
            .ToListAsync();
        return users.Select(Map).ToList();
    }

    public async Task<ClinicalUserResponse> CreateAsync(CreateUserRequest req)
    {
        ValidateUsername(req.Username);
        ValidatePassword(req.Password);
        ValidateDisplayName(req.DisplayName);
        var role = ParseRole(req.Role);

        var exists = await _db.ClinicalUsers.AnyAsync(u => u.Username == req.Username);
        if (exists)
            throw new ConflictException(
                $"Username '{req.Username}' is already taken.",
                "USERNAME_CONFLICT");

        var user = new ClinicalUser
        {
            Id = Guid.NewGuid(),
            Username = req.Username.Trim(),
            PasswordHash = BCrypt.Net.BCrypt.HashPassword(req.Password),
            DisplayName = req.DisplayName.Trim(),
            Role = role,
            IsActive = true,
            CreatedAt = DateTimeOffset.UtcNow,
        };
        _db.ClinicalUsers.Add(user);
        await _db.SaveChangesAsync();
        return Map(user);
    }

    public async Task<ClinicalUserResponse> UpdateAsync(Guid id, UpdateUserRequest req)
    {
        var user = await _db.ClinicalUsers.FindAsync(id);
        if (user is null)
            throw new NotFoundException("User not found.", "USER_NOT_FOUND");

        if (req.DisplayName is not null)
        {
            ValidateDisplayName(req.DisplayName);
            user.DisplayName = req.DisplayName.Trim();
        }

        if (req.Role is not null)
            user.Role = ParseRole(req.Role);

        if (req.IsActive is not null)
        {
            if (!req.IsActive.Value && _currentUser.UserId == id)
                throw new ValidationException(
                    "You cannot deactivate your own account.",
                    "SELF_DEACTIVATION_DENIED");
            user.IsActive = req.IsActive.Value;
        }

        await _db.SaveChangesAsync();
        return Map(user);
    }

    private static ClinicalUserResponse Map(ClinicalUser u) =>
        new(u.Id, u.Username, u.DisplayName, u.Role.ToDbString(), u.IsActive, u.CreatedAt, u.LastLoginAt);

    private static void ValidateUsername(string username)
    {
        if (string.IsNullOrWhiteSpace(username))
            throw new ValidationException("Username is required.", "USERNAME_REQUIRED");
        if (username.Trim().Length > 100)
            throw new ValidationException("Username must be 100 characters or fewer.", "USERNAME_TOO_LONG");
    }

    private static void ValidatePassword(string password)
    {
        if (string.IsNullOrWhiteSpace(password))
            throw new ValidationException("Password is required.", "PASSWORD_REQUIRED");
        if (password.Length < 8)
            throw new ValidationException("Password must be at least 8 characters.", "PASSWORD_TOO_SHORT");
    }

    private static void ValidateDisplayName(string displayName)
    {
        if (string.IsNullOrWhiteSpace(displayName))
            throw new ValidationException("Display name is required.", "DISPLAY_NAME_REQUIRED");
        if (displayName.Trim().Length > 200)
            throw new ValidationException("Display name must be 200 characters or fewer.", "DISPLAY_NAME_TOO_LONG");
    }

    private static ClinicalRole ParseRole(string role)
    {
        if (string.IsNullOrWhiteSpace(role))
            throw new ValidationException("Role is required.", "ROLE_REQUIRED");
        try
        {
            return ClinicalRoleExtensions.FromDbString(role.Trim().ToUpperInvariant());
        }
        catch (ArgumentOutOfRangeException)
        {
            throw new ValidationException(
                "Role must be one of: NURSE, PHYSICIAN, ADMIN, INTEGRATION.",
                "INVALID_ROLE");
        }
    }
}
