using System.Diagnostics;
using Microsoft.AspNetCore.Http;
using Nexus.Api.Models;

namespace Nexus.Api.Services;

/// <summary>
/// Builds the transport-safe result metadata shared by browser, bridge and
/// OpenClaw mutation responses. URLs deliberately remain a frontend concern.
/// </summary>
public static class OperationResultFactory
{
    private const int MaxOperationIdLength = 128;

    public static OperationResultDto FromHttpContext(
        HttpContext context,
        string status,
        EntityRefDto? primaryRef,
        int revision = 0,
        IEnumerable<EntityRefDto>? affectedRefs = null)
    {
        var requestedCorrelation = context.Request.Headers["X-Correlation-ID"]
            .FirstOrDefault();
        var operationId = IsSafeIdentifier(requestedCorrelation)
            ? requestedCorrelation!.Trim()
            : IsSafeIdentifier(context.TraceIdentifier)
                ? context.TraceIdentifier.Trim()
                : Guid.NewGuid().ToString("N");
        var traceId = Activity.Current?.Id;
        if (string.IsNullOrWhiteSpace(traceId))
        {
            var traceParent = context.Request.Headers["traceparent"].FirstOrDefault();
            traceId = IsSafeIdentifier(traceParent) ? traceParent!.Trim() : null;
        }

        context.Response.Headers["X-Correlation-ID"] = operationId;
        return Create(
            operationId,
            status,
            revision,
            primaryRef,
            affectedRefs,
            traceId);
    }

    public static OperationResultDto FromInvocation(
        OpenClawInvocationContext context,
        string status,
        EntityRefDto? primaryRef,
        int revision = 0,
        IEnumerable<EntityRefDto>? affectedRefs = null)
        => Create(
            context.CorrelationId,
            status,
            revision,
            primaryRef,
            affectedRefs,
            Activity.Current?.Id ?? context.TraceParent);

    public static OperationResultDto Create(
        string operationId,
        string status,
        int revision,
        EntityRefDto? primaryRef,
        IEnumerable<EntityRefDto>? affectedRefs = null,
        string? traceId = null)
    {
        var uniqueAffected = (affectedRefs ?? [])
            .Where(reference =>
                primaryRef is null ||
                !string.Equals(reference.Type, primaryRef.Type, StringComparison.Ordinal) ||
                !string.Equals(reference.Id, primaryRef.Id, StringComparison.Ordinal))
            .DistinctBy(reference => (reference.Type, reference.Id))
            .ToArray();

        return new OperationResultDto(
            operationId,
            status,
            Math.Max(0, revision),
            primaryRef,
            uniqueAffected,
            traceId);
    }

    private static bool IsSafeIdentifier(string? value)
        => !string.IsNullOrWhiteSpace(value)
           && value.Length <= MaxOperationIdLength
           && !value.Any(char.IsControl);
}
