using Microsoft.AspNetCore.Http;
using System.Security.Claims;
using TakeoutSaaS.Shared.Abstractions.Constants;
using TakeoutSaaS.Shared.Abstractions.Exceptions;
using TakeoutSaaS.Shared.Abstractions.Security;
using TakeoutSaaS.Shared.Abstractions.Tenancy;

namespace TakeoutSaaS.Application.App.Personal.Services;

/// <summary>
/// 个人中心请求上下文服务。
/// </summary>
public sealed class PersonalContextService(
    ICurrentUserAccessor currentUserAccessor,
    ITenantProvider tenantProvider,
    IHttpContextAccessor httpContextAccessor)
{
    /// <summary>
    /// 获取当前请求必需上下文。
    /// </summary>
    /// <returns>用户 ID、租户 ID、角色编码集合、追踪标识。</returns>
    public (long UserId, long TenantId, IReadOnlyList<string> RoleCodes, string TraceId) GetRequiredContext()
    {
        // 1. 读取用户上下文
        var userId = currentUserAccessor.UserId;
        if (userId <= 0)
        {
            throw new BusinessException(ErrorCodes.Unauthorized, "未登录或登录已过期");
        }

        // 2. 读取租户上下文
        var tenantId = tenantProvider.GetCurrentTenantId();
        if (tenantId <= 0)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "缺少租户标识");
        }

        // 3. 读取角色与追踪标识
        var roleCodes = ResolveRoleCodes();
        var traceId = ResolveTraceId();
        return (userId, tenantId, roleCodes, traceId);
    }

    /// <summary>
    /// 获取当前请求 TraceId。
    /// </summary>
    /// <returns>TraceId，若不存在则返回空字符串。</returns>
    public string ResolveTraceId()
    {
        // 1. 从 Activity 与 HttpContext 中解析 TraceId
        var traceId = System.Diagnostics.Activity.Current?.TraceId.ToString()
                      ?? httpContextAccessor.HttpContext?.TraceIdentifier
                      ?? string.Empty;

        // 2. 返回追踪标识
        return traceId;
    }

    /// <summary>
    /// 获取当前用户角色编码。
    /// </summary>
    /// <returns>角色编码集合。</returns>
    public IReadOnlyList<string> ResolveRoleCodes()
    {
        // 1. 读取 Claim 角色值
        var claims = httpContextAccessor.HttpContext?.User?.Claims ?? [];
        var roles = claims
            .Where(static c => c.Type == ClaimTypes.Role || c.Type == "role")
            .Select(static c => c.Value)
            .Where(static x => !string.IsNullOrWhiteSpace(x))
            .Distinct(StringComparer.OrdinalIgnoreCase)
            .ToArray();

        // 2. 返回去重后的角色编码
        return roles;
    }
}
