// EN: POS commands for PIN auth and device registration.
// VI: Commands POS cho xác thực PIN và đăng ký thiết bị.

using MediatR;
using MerchantService.Domain.AggregatesModel.MerchantStaffAggregate;
using MerchantService.Domain.Exceptions;

namespace MerchantService.API.Application.Commands.Pos;

#region PIN Auth Command

/// <summary>
/// EN: Command to authenticate staff using PIN for POS.
/// VI: Command để xác thực nhân viên sử dụng PIN cho POS.
/// </summary>
public record PinAuthCommand : IRequest<PinAuthResult>
{
    public Guid StaffId { get; init; }
    public string PinCode { get; init; } = null!;
    public string? DeviceId { get; init; }
}

public record PinAuthResult(
    bool IsAuthenticated,
    Guid? StaffId,
    string? StaffEmail,
    string? Role,
    Guid? ShopId,
    string? ErrorMessage);

/// <summary>
/// EN: Handler for PinAuthCommand.
/// VI: Handler cho PinAuthCommand.
/// </summary>
public class PinAuthCommandHandler : IRequestHandler<PinAuthCommand, PinAuthResult>
{
    private readonly IMerchantStaffRepository _staffRepository;
    private readonly ILogger<PinAuthCommandHandler> _logger;

    public PinAuthCommandHandler(
        IMerchantStaffRepository staffRepository,
        ILogger<PinAuthCommandHandler> logger)
    {
        _staffRepository = staffRepository;
        _logger = logger;
    }

    public async Task<PinAuthResult> Handle(PinAuthCommand request, CancellationToken cancellationToken)
    {
        var staff = await _staffRepository.GetByIdAsync(request.StaffId, cancellationToken);
        
        if (staff == null)
        {
            return new PinAuthResult(false, null, null, null, null, "Staff not found");
        }

        if (staff.Status != StaffStatus.Active)
        {
            return new PinAuthResult(false, null, null, null, null, "Staff is not active");
        }

        if (!staff.VerifyPinCode(request.PinCode))
        {
            _logger.LogWarning("PIN verification failed for staff {StaffId}", request.StaffId);
            return new PinAuthResult(false, null, null, null, null, "Invalid PIN");
        }

        // EN: Get first shop assignment if available
        // VI: Lấy shop đầu tiên nếu có
        var shopId = staff.ShopAssignments.FirstOrDefault()?.ShopId;

        _logger.LogInformation("POS authentication successful for staff {StaffId}", staff.Id);

        return new PinAuthResult(
            true,
            staff.Id,
            staff.Email,
            staff.Role.Name,
            shopId,
            null);
    }
}

#endregion

#region Register Device Command

/// <summary>
/// EN: Command to register a POS device.
/// VI: Command để đăng ký thiết bị POS.
/// </summary>
public record RegisterDeviceCommand : IRequest<RegisterDeviceResult>
{
    public string DeviceId { get; init; } = null!;
    public string DeviceName { get; init; } = null!;
    public string Platform { get; init; } = null!;
    public string? PushToken { get; init; }
}

public record RegisterDeviceResult(Guid StaffId, string DeviceId, bool IsRegistered);

/// <summary>
/// EN: Handler for RegisterDeviceCommand.
/// VI: Handler cho RegisterDeviceCommand.
/// </summary>
public class RegisterDeviceCommandHandler : IRequestHandler<RegisterDeviceCommand, RegisterDeviceResult>
{
    private readonly IMerchantStaffRepository _staffRepository;
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly ILogger<RegisterDeviceCommandHandler> _logger;

    public RegisterDeviceCommandHandler(
        IMerchantStaffRepository staffRepository,
        IHttpContextAccessor httpContextAccessor,
        ILogger<RegisterDeviceCommandHandler> logger)
    {
        _staffRepository = staffRepository;
        _httpContextAccessor = httpContextAccessor;
        _logger = logger;
    }

    public async Task<RegisterDeviceResult> Handle(RegisterDeviceCommand request, CancellationToken cancellationToken)
    {
        var userId = GetUserId();
        var staff = await _staffRepository.GetByUserIdAsync(userId, cancellationToken)
            ?? throw new DomainException("Staff not found");

        staff.RegisterDevice(request.DeviceId, request.DeviceName, request.PushToken, request.Platform);

        _staffRepository.Update(staff);
        await _staffRepository.UnitOfWork.SaveEntitiesAsync(cancellationToken);

        _logger.LogInformation("Device registered: {DeviceId} for staff {StaffId}", request.DeviceId, staff.Id);

        return new RegisterDeviceResult(staff.Id, request.DeviceId, true);
    }

    private Guid GetUserId()
    {
        var userIdClaim = _httpContextAccessor.HttpContext?.User.FindFirst("sub")?.Value
            ?? _httpContextAccessor.HttpContext?.User.FindFirst("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier")?.Value;
        if (string.IsNullOrEmpty(userIdClaim) || !Guid.TryParse(userIdClaim, out var userId))
            throw new DomainException("User not authenticated");
        return userId;
    }
}

#endregion
