using Metin2Launcher.Apply;
using Xunit;

namespace Metin2Launcher.Tests;

public class PathSafetyTests : IDisposable
{
    private readonly string _root;

    public PathSafetyTests()
    {
        _root = Path.Combine(Path.GetTempPath(), "metin-pathsafe-" + Guid.NewGuid());
        Directory.CreateDirectory(_root);
    }

    public void Dispose()
    {
        try { Directory.Delete(_root, recursive: true); } catch { }
    }

    [Fact]
    public void Resolve_allows_simple_relative_path()
    {
        var result = PathSafety.ResolveInside(_root, "pack/item.pck");
        Assert.StartsWith(Path.GetFullPath(_root), result);
        Assert.EndsWith("item.pck", result);
    }

    [Fact]
    public void Resolve_allows_nested_relative_path()
    {
        var result = PathSafety.ResolveInside(_root, "assets/root/serverinfo.py");
        Assert.StartsWith(Path.GetFullPath(_root), result);
    }

    [Fact]
    public void Resolve_rejects_parent_traversal()
    {
        Assert.Throws<UnsafePathException>(
            () => PathSafety.ResolveInside(_root, "../../../etc/passwd"));
    }

    [Fact]
    public void Resolve_rejects_mixed_traversal()
    {
        Assert.Throws<UnsafePathException>(
            () => PathSafety.ResolveInside(_root, "assets/../../outside.txt"));
    }

    [Fact]
    public void Resolve_rejects_absolute_unix_path()
    {
        Assert.Throws<UnsafePathException>(
            () => PathSafety.ResolveInside(_root, "/etc/passwd"));
    }

    [Fact]
    public void Resolve_rejects_empty()
    {
        Assert.Throws<UnsafePathException>(
            () => PathSafety.ResolveInside(_root, ""));
    }

    [Fact]
    public void Resolve_rejects_sibling_root_escape()
    {
        // If client root is /tmp/x and attacker passes ../x2/a.txt, the resolved path
        // becomes /tmp/x2/a.txt which starts with "/tmp/x" lexically but is a different
        // directory. The check must use a trailing separator so "/tmp/x2/..." doesn't
        // match "/tmp/x".
        var sibling = Path.GetFileName(_root) + "2";
        Assert.Throws<UnsafePathException>(
            () => PathSafety.ResolveInside(_root, $"../{sibling}/evil.txt"));
    }
}
