// EN: HTTP context-based tenant provider implementation.
// VI: Implementation tenant provider dựa trên HTTP context.

using System.Security.Claims;

namespace OrderService.API.Infrastructure.Tenant;

/// <summary>
/// EN: Extracts tenant context from JWT claims and HTTP headers.
/// VI: Trích xuất tenant context từ JWT claims và HTTP headers.
/// </summary>
public class HttpContextTenantProvider : ITenantProvider
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly ILogger<HttpContextTenantProvider> _logger;

    /// <summary>
    /// EN: Header used by internal service-to-service calls to bypass tenant filtering.
    /// VI: Header được sử dụng bởi cuộc gọi service-to-service nội bộ để bỏ qua tenant filter.
    /// </summary>
    private const string ServiceCallHeader = "X-Service-Call";

    /// <summary>
    /// EN: Header for shop context override (used by POS system).
    /// VI: Header để override shop context (được sử dụng bởi hệ thống POS).
    /// </summary>
    private const string ShopIdHeader = "X-Shop-Id";

    /// <summary>
    /// EN: Expected value for service-to-service call header.
    /// VI: Giá trị mong đợi cho header cuộc gọi service-to-service.
    /// </summary>
    private const string ServiceCallSecret = "internal";

    public HttpContextTenantProvider(
        IHttpContextAccessor httpContextAccessor,
        ILogger<HttpContextTenantProvider> logger)
    {
        _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
    }

    /// <inheritdoc />
    public Guid? GetCurrentUserId()
    {
        var claim = GetClaim(ClaimTypes.NameIdentifier) ?? GetClaim("sub");
        if (claim != null && Guid.TryParse(claim, out var userId))
        {
            return userId;
        }
        return null;
    }

    /// <inheritdoc />
    public Guid? GetCurrentMerchantId()
    {
        var claim = GetClaim("merchant_id");
        if (claim != null && Guid.TryParse(claim, out var merchantId))
        {
            return merchantId;
        }
        return null;
    }

    /// <inheritdoc />
    public Guid? GetCurrentShopId()
    {
        // EN: Priority 1: JWT claim "shop_id"
        // VI: Ưu tiên 1: JWT claim "shop_id"
        var claim = GetClaim("shop_id");
        if (claim != null && Guid.TryParse(claim, out var shopId))
        {
            return shopId;
        }

        // EN: Priority 2: X-Shop-Id header (POS system uses this)
        // VI: Ưu tiên 2: Header X-Shop-Id (hệ thống POS sử dụng)
        var headerValue = _httpContextAccessor.HttpContext?.Request.Headers[ShopIdHeader].FirstOrDefault();
        if (headerValue != null && Guid.TryParse(headerValue, out var headerShopId))
        {
            return headerShopId;
        }

        _logger.LogDebug(
            "EN: No shop_id found in JWT claims or X-Shop-Id header / " +
            "VI: Không tìm thấy shop_id trong JWT claims hoặc header X-Shop-Id");
        return null;
    }

    /// <inheritdoc />
    public bool IsServiceCall()
    {
        var headerValue = _httpContextAccessor.HttpContext?.Request.Headers[ServiceCallHeader].FirstOrDefault();
        return string.Equals(headerValue, ServiceCallSecret, StringComparison.OrdinalIgnoreCase);
    }

    /// <inheritdoc />
    public bool IsAdmin()
    {
        var roles = _httpContextAccessor.HttpContext?.User
            .FindAll(ClaimTypes.Role)
            .Select(c => c.Value)
            .ToList() ?? [];

        // EN: Also check custom "role" claim type used by IAM service
        // VI: Cũng kiểm tra claim type "role" tùy chỉnh được sử dụng bởi IAM service
        var customRoles = _httpContextAccessor.HttpContext?.User
            .FindAll("role")
            .Select(c => c.Value)
            .ToList() ?? [];

        roles.AddRange(customRoles);

        return roles.Any(r => r.Equals("admin", StringComparison.OrdinalIgnoreCase)
            || r.Equals("system", StringComparison.OrdinalIgnoreCase)
            || r.Equals("superadmin", StringComparison.OrdinalIgnoreCase));
    }

    private string? GetClaim(string claimType)
    {
        return _httpContextAccessor.HttpContext?.User.FindFirst(claimType)?.Value;
    }
}
