namespace ZB.MOM.WW.MxGateway.IntegrationTests;

public sealed class IntegrationTestEnvironmentTests
{
    /// <summary>Verifies that live MXAccess tests use correct environment variable name.</summary>
    [Fact]
    public void LiveMxAccessTests_AreOptInByEnvironmentVariable()
    {
        Assert.Equal(
            "MXGATEWAY_RUN_LIVE_MXACCESS_TESTS",
            IntegrationTestEnvironment.LiveMxAccessVariableName);
    }

    /// <summary>Verifies that worker executable uses correct environment variable name.</summary>
    [Fact]
    public void LiveMxAccessWorkerExecutable_UsesDocumentedEnvironmentVariable()
    {
        Assert.Equal(
            "MXGATEWAY_LIVE_MXACCESS_WORKER_EXE",
            IntegrationTestEnvironment.LiveMxAccessWorkerExecutableVariableName);
    }

    /// <summary>Verifies that repository root resolution accepts git worktree files.</summary>
    [Fact]
    public void ResolveRepositoryRoot_AcceptsGitWorktreeFile()
    {
        string temporaryRoot = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
        string nestedDirectory = Path.Combine(temporaryRoot, "tests", "bin");

        try
        {
            Directory.CreateDirectory(nestedDirectory);
            Directory.CreateDirectory(Path.Combine(temporaryRoot, "src"));
            File.WriteAllText(Path.Combine(temporaryRoot, ".git"), "gitdir: ../.git/worktrees/test");

            // Pass temporaryRoot as the stop-boundary so the walker can never leak
            // into ambient ancestors of Path.GetTempPath().
            string repositoryRoot = IntegrationTestEnvironment.ResolveRepositoryRoot(
                nestedDirectory,
                stopBoundary: temporaryRoot);

            Assert.Equal(temporaryRoot, repositoryRoot);
        }
        finally
        {
            if (Directory.Exists(temporaryRoot))
            {
                Directory.Delete(temporaryRoot, recursive: true);
            }
        }
    }

    /// <summary>
    /// Verifies that <see cref="IntegrationTestEnvironment.ResolveRepositoryRoot"/>
    /// throws <see cref="InvalidOperationException"/> with a diagnostic message when
    /// the walk exhausts without finding a repository root. The previous silent
    /// fallback to <c>Directory.GetCurrentDirectory()</c> masked misconfiguration;
    /// operators get a clear, actionable failure instead.
    /// The <c>stopBoundary</c> isolates the walker from ambient ancestors of
    /// <see cref="Path.GetTempPath"/> (a redirected <c>TMP</c>, a co-located checkout
    /// at <c>C:\src</c>, etc.) that could otherwise satisfy
    /// <c>IsRepositoryRoot</c> and make this assertion flake on contributor or CI
    /// boxes.
    /// </summary>
    [Fact]
    public void ResolveRepositoryRoot_NoMarkers_ThrowsInvalidOperationExceptionNamingStartAndMarkers()
    {
        string isolatedRoot = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
        string isolatedStart = Path.Combine(isolatedRoot, "nested");

        try
        {
            Directory.CreateDirectory(isolatedStart);

            InvalidOperationException ex = Assert.Throws<InvalidOperationException>(
                () => IntegrationTestEnvironment.ResolveRepositoryRoot(
                    isolatedStart,
                    stopBoundary: isolatedRoot));

            Assert.Contains(isolatedStart, ex.Message, StringComparison.Ordinal);
            Assert.Contains(".git", ex.Message, StringComparison.Ordinal);
            Assert.Contains(".sln", ex.Message, StringComparison.Ordinal);
            Assert.Contains(
                IntegrationTestEnvironment.LiveMxAccessWorkerExecutableVariableName,
                ex.Message,
                StringComparison.Ordinal);
        }
        finally
        {
            if (Directory.Exists(isolatedRoot))
            {
                Directory.Delete(isolatedRoot, recursive: true);
            }
        }
    }

    /// <summary>
    /// Verifies the <c>stopBoundary</c> parameter on
    /// <see cref="IntegrationTestEnvironment.ResolveRepositoryRoot"/> isolates the
    /// walker from ambient ancestors that happen to satisfy <c>IsRepositoryRoot</c>.
    /// The test deliberately constructs an outer directory that *does* carry repository-root
    /// markers (<c>src/</c> + <c>.git</c>) and an inner isolated chain that does
    /// not. Without the boundary the walker would happily stop at the outer
    /// directory; with the boundary it must throw because the chain it can see
    /// carries no markers. A future refactor that dropped or mis-honored the
    /// boundary would surface here as a failed assertion instead of a silent flake
    /// in CI.
    /// </summary>
    [Fact]
    public void ResolveRepositoryRoot_StopBoundary_IsolatesWalkerFromAmbientAncestorMarkers()
    {
        string outerRoot = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
        string innerBoundary = Path.Combine(outerRoot, "inner");
        string isolatedStart = Path.Combine(innerBoundary, "nested");

        try
        {
            // Outer directory satisfies IsRepositoryRoot — it is the "ambient
            // ancestor" the production walker would otherwise stop at.
            Directory.CreateDirectory(Path.Combine(outerRoot, "src"));
            File.WriteAllText(Path.Combine(outerRoot, ".git"), "gitdir: ../.git/worktrees/test");

            // Inner chain carries no markers. The boundary sits between the inner
            // chain and the outer marker-bearing ancestor.
            Directory.CreateDirectory(isolatedStart);

            // Sanity: without the boundary the production walker reaches outerRoot
            // and silently returns it — the exact ambient-ancestor leak.
            string leakedRoot = IntegrationTestEnvironment.ResolveRepositoryRoot(isolatedStart);
            Assert.Equal(outerRoot, leakedRoot);

            // With the boundary the walker is sealed inside the inner chain and
            // must throw — the marker on outerRoot is invisible to it.
            InvalidOperationException ex = Assert.Throws<InvalidOperationException>(
                () => IntegrationTestEnvironment.ResolveRepositoryRoot(
                    isolatedStart,
                    stopBoundary: innerBoundary));

            Assert.Contains(isolatedStart, ex.Message, StringComparison.Ordinal);
        }
        finally
        {
            if (Directory.Exists(outerRoot))
            {
                Directory.Delete(outerRoot, recursive: true);
            }
        }
    }
}
