// EN: Real-time SignalR hub for POS and KDS updates.
// VI: SignalR hub thời gian thực cho POS và KDS.

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.SignalR;

namespace OrderService.API.Hubs;

/// <summary>
/// EN: Real-time SignalR hub for POS and KDS updates.
/// VI: SignalR hub thời gian thực cho POS và KDS.
/// </summary>
/// <remarks>
/// EN: Group naming convention:
///   - shop:{shopId} — all POS updates for a shop
///   - kds:{shopId}  — kitchen display system updates only
///   - pos:{shopId}  — POS terminal updates only
/// VI: Quy ước đặt tên group:
///   - shop:{shopId} — tất cả cập nhật POS cho shop
///   - kds:{shopId}  — chỉ cập nhật hệ thống hiển thị bếp
///   - pos:{shopId}  — chỉ cập nhật thiết bị POS
/// </remarks>
[Authorize]
public class PosHub : Hub<IPosHubClient>
{
    private readonly PosConnectionManager _connectionManager;
    private readonly ILogger<PosHub> _logger;

    public PosHub(
        PosConnectionManager connectionManager,
        ILogger<PosHub> logger)
    {
        _connectionManager = connectionManager;
        _logger = logger;
    }

    #region Connection Lifecycle

    /// <summary>
    /// EN: Called when a new connection is established.
    /// VI: Được gọi khi kết nối mới được thiết lập.
    /// </summary>
    public override async Task OnConnectedAsync()
    {
        var userId = Context.UserIdentifier;
        if (string.IsNullOrEmpty(userId))
        {
            _logger.LogWarning(
                "EN: POS hub connection attempted without user identifier / " +
                "VI: POS hub kết nối mà không có user identifier");
            await base.OnConnectedAsync();
            return;
        }

        _logger.LogInformation(
            "EN: User {UserId} connected to POS hub with connection {ConnectionId} / " +
            "VI: User {UserId} kết nối POS hub với connection {ConnectionId}",
            userId, Context.ConnectionId);

        await base.OnConnectedAsync();
    }

    /// <summary>
    /// EN: Called when a connection is terminated.
    /// VI: Được gọi khi kết nối bị ngắt.
    /// </summary>
    public override async Task OnDisconnectedAsync(Exception? exception)
    {
        var shopId = _connectionManager.RemoveConnection(Context.ConnectionId);

        if (shopId.HasValue)
        {
            _logger.LogInformation(
                "EN: Connection {ConnectionId} disconnected from shop {ShopId} / " +
                "VI: Connection {ConnectionId} ngắt kết nối khỏi shop {ShopId}",
                Context.ConnectionId, shopId.Value);
        }

        if (exception != null)
        {
            _logger.LogError(exception,
                "EN: POS hub connection {ConnectionId} disconnected with error / " +
                "VI: POS hub connection {ConnectionId} ngắt kết nối với lỗi",
                Context.ConnectionId);
        }

        await base.OnDisconnectedAsync(exception);
    }

    #endregion

    #region Group Management

    /// <summary>
    /// EN: Join a shop group to receive all POS updates for that shop.
    /// VI: Tham gia group shop để nhận tất cả cập nhật POS cho shop đó.
    /// </summary>
    public async Task JoinShop(Guid shopId)
    {
        var userId = Context.UserIdentifier
            ?? throw new HubException("User not authenticated / User chưa xác thực");

        // EN: Validate that the authenticated user has access to the requested shop.
        // VI: Xác thực rằng user đã xác thực có quyền truy cập shop được yêu cầu.
        ValidateShopAccess(shopId);

        _logger.LogInformation(
            "EN: User {UserId} joining shop {ShopId} / " +
            "VI: User {UserId} tham gia shop {ShopId}",
            userId, shopId);

        _connectionManager.AddConnection(Context.ConnectionId, shopId);

        await Groups.AddToGroupAsync(Context.ConnectionId, $"shop:{shopId}");
    }

    /// <summary>
    /// EN: Join a KDS group to receive kitchen updates only.
    /// VI: Tham gia group KDS để nhận cập nhật bếp.
    /// </summary>
    public async Task JoinKds(Guid shopId)
    {
        var userId = Context.UserIdentifier
            ?? throw new HubException("User not authenticated / User chưa xác thực");

        // EN: Validate that the authenticated user has access to the requested shop.
        // VI: Xác thực rằng user đã xác thực có quyền truy cập shop được yêu cầu.
        ValidateShopAccess(shopId);

        _logger.LogInformation(
            "EN: User {UserId} joining KDS for shop {ShopId} / " +
            "VI: User {UserId} tham gia KDS cho shop {ShopId}",
            userId, shopId);

        _connectionManager.AddConnection(Context.ConnectionId, shopId);

        await Groups.AddToGroupAsync(Context.ConnectionId, $"kds:{shopId}");
    }

    /// <summary>
    /// EN: Join a POS terminal group.
    /// VI: Tham gia group thiết bị POS.
    /// </summary>
    public async Task JoinPos(Guid shopId)
    {
        var userId = Context.UserIdentifier
            ?? throw new HubException("User not authenticated / User chưa xác thực");

        // EN: Validate that the authenticated user has access to the requested shop.
        // VI: Xác thực rằng user đã xác thực có quyền truy cập shop được yêu cầu.
        ValidateShopAccess(shopId);

        _logger.LogInformation(
            "EN: User {UserId} joining POS terminal for shop {ShopId} / " +
            "VI: User {UserId} tham gia thiết bị POS cho shop {ShopId}",
            userId, shopId);

        _connectionManager.AddConnection(Context.ConnectionId, shopId);

        await Groups.AddToGroupAsync(Context.ConnectionId, $"pos:{shopId}");
    }

    /// <summary>
    /// EN: Leave a shop group.
    /// VI: Rời group shop.
    /// </summary>
    public async Task LeaveShop(Guid shopId)
    {
        var userId = Context.UserIdentifier
            ?? throw new HubException("User not authenticated / User chưa xác thực");

        _logger.LogInformation(
            "EN: User {UserId} leaving shop {ShopId} / " +
            "VI: User {UserId} rời shop {ShopId}",
            userId, shopId);

        await Groups.RemoveFromGroupAsync(Context.ConnectionId, $"shop:{shopId}");
    }

    /// <summary>
    /// EN: Leave a KDS group.
    /// VI: Rời group KDS.
    /// </summary>
    public async Task LeaveKds(Guid shopId)
    {
        var userId = Context.UserIdentifier
            ?? throw new HubException("User not authenticated / User chưa xác thực");

        _logger.LogInformation(
            "EN: User {UserId} leaving KDS for shop {ShopId} / " +
            "VI: User {UserId} rời KDS cho shop {ShopId}",
            userId, shopId);

        await Groups.RemoveFromGroupAsync(Context.ConnectionId, $"kds:{shopId}");
    }

    /// <summary>
    /// EN: Leave a POS terminal group.
    /// VI: Rời group thiết bị POS.
    /// </summary>
    public async Task LeavePos(Guid shopId)
    {
        var userId = Context.UserIdentifier
            ?? throw new HubException("User not authenticated / User chưa xác thực");

        _logger.LogInformation(
            "EN: User {UserId} leaving POS terminal for shop {ShopId} / " +
            "VI: User {UserId} rời thiết bị POS cho shop {ShopId}",
            userId, shopId);

        await Groups.RemoveFromGroupAsync(Context.ConnectionId, $"pos:{shopId}");
    }

    #endregion

    #region Authorization Helpers

    /// <summary>
    /// EN: Validate that the authenticated user has access to the specified shop.
    ///     Prevents cross-tenant access where a user from shop A joins shop B's group.
    /// VI: Xác thực rằng user đã xác thực có quyền truy cập shop được chỉ định.
    ///     Ngăn chặn truy cập cross-tenant khi user từ shop A tham gia group của shop B.
    /// </summary>
    private void ValidateShopAccess(Guid shopId)
    {
        var userShopClaim = Context.User?.FindFirst("shop_id")?.Value;
        if (string.IsNullOrEmpty(userShopClaim) || !Guid.TryParse(userShopClaim, out var userShopId) || userShopId != shopId)
        {
            _logger.LogWarning(
                "EN: Unauthorized shop access attempt - User tried to join shop {RequestedShopId} but belongs to shop {UserShopId} / " +
                "VI: Truy cập shop trái phép - User cố tham gia shop {RequestedShopId} nhưng thuộc shop {UserShopId}",
                shopId, userShopClaim ?? "none");
            throw new HubException("Unauthorized: You don't have access to this shop / Không có quyền: Bạn không có quyền truy cập shop này");
        }
    }

    #endregion
}
