// Copyright 2020-2025 The NATS Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using Shouldly;
using ZB.MOM.NatsNet.Server;

namespace ZB.MOM.NatsNet.Server.Tests.Foundation;

/// <summary>
/// Tests for <see cref="ErrorCtx"/> and <see cref="ErrorContextHelper"/>.
/// Mirrors server/errors_test.go: TestErrCtx (ID 297) and TestErrCtxWrapped (ID 298).
/// </summary>
public sealed class ServerErrorsTests
{
    [Fact]
    public void ErrCtx_ShouldPreserveOriginalMessageAndAddContext()
    {
        // Mirror: TestErrCtx
        var ctx = "Extra context information";
        var e = ErrorContextHelper.NewErrorCtx(ServerErrors.ErrWrongGateway, "{0}", ctx);

        // Message should match the underlying error.
        e.Message.ShouldBe(ServerErrors.ErrWrongGateway.Message);

        // Must not be reference-equal to the sentinel.
        e.ShouldNotBeSameAs(ServerErrors.ErrWrongGateway);

        // ErrorIs should find the sentinel in the chain.
        ErrorContextHelper.ErrorIs(e, ServerErrors.ErrWrongGateway).ShouldBeTrue();

        // UnpackIfErrorCtx on a non-ctx error returns Message unchanged.
        ErrorContextHelper.UnpackIfErrorCtx(ServerErrors.ErrWrongGateway)
            .ShouldBe(ServerErrors.ErrWrongGateway.Message);

        // UnpackIfErrorCtx should start with the original error message.
        var trace = ErrorContextHelper.UnpackIfErrorCtx(e);
        trace.ShouldStartWith(ServerErrors.ErrWrongGateway.Message);

        // And end with the context string.
        trace.ShouldEndWith(ctx);
    }

    [Fact]
    public void ErrCtxWrapped_ShouldContainAllContextLayers()
    {
        // Mirror: TestErrCtxWrapped
        var ctxO = "Original Ctx";
        var eO = ErrorContextHelper.NewErrorCtx(ServerErrors.ErrWrongGateway, "{0}", ctxO);

        var ctx = "Extra context information";
        var e = ErrorContextHelper.NewErrorCtx(eO, "{0}", ctx);

        // Message should still match the underlying error.
        e.Message.ShouldBe(ServerErrors.ErrWrongGateway.Message);

        // Must not be reference-equal to the sentinel.
        e.ShouldNotBeSameAs(ServerErrors.ErrWrongGateway);

        // ErrorIs should walk the chain.
        ErrorContextHelper.ErrorIs(e, ServerErrors.ErrWrongGateway).ShouldBeTrue();

        // UnpackIfErrorCtx on a non-ctx error returns Message unchanged.
        ErrorContextHelper.UnpackIfErrorCtx(ServerErrors.ErrWrongGateway)
            .ShouldBe(ServerErrors.ErrWrongGateway.Message);

        var trace = ErrorContextHelper.UnpackIfErrorCtx(e);

        // Must start with the original error.
        trace.ShouldStartWith(ServerErrors.ErrWrongGateway.Message);

        // Must end with the outermost context.
        trace.ShouldEndWith(ctx);

        // Must also contain the inner context.
        trace.ShouldContain(ctxO);
    }
}
