using DodoSSH.Client.ObjectStore;

namespace DodoSSH.Client.ObjectStore.Tests;

/// <summary>
/// Translating between the paths a file browser uses and the keys a bucket has.
/// </summary>
/// <remarks>
/// Small and worth having, because every one of these cases is a way a bucket listing goes subtly wrong
/// rather than loudly: a missing trailing slash lists a sibling directory's contents, and a leading one asks
/// the service for a key beginning with <c>/</c>, which is legal and is never what anybody stored.
/// </remarks>
public sealed class ObjectKeysTests
{
    [Theory]
    [InlineData("/", "")]
    [InlineData("/reports", "reports")]
    [InlineData("/reports/2026/q3.csv", "reports/2026/q3.csv")]
    public void APathBecomesAKeyWithNoLeadingSlash(string path, string expected)
    {
        ObjectKeys.ToKey(path).ShouldBe(expected);
    }

    [Theory]
    [InlineData("", "/")]
    [InlineData("reports/2026/q3.csv", "/reports/2026/q3.csv")]
    public void AKeyBecomesAnAbsolutePath(string key, string expected)
    {
        ObjectKeys.ToPath(key).ShouldBe(expected);
    }

    /// <remarks>
    /// The one that matters most. Without the trailing slash, listing <c>/reports</c> also returns everything
    /// under <c>/reports-archive</c> — a prefix match knows nothing about path segments, and the result is a
    /// directory listing with another directory's files in it.
    /// </remarks>
    [Theory]
    [InlineData("/reports", "reports/")]
    [InlineData("/reports/", "reports/")]
    [InlineData("/reports/2026", "reports/2026/")]
    public void APrefixAlwaysEndsInASlash(string path, string expected)
    {
        ObjectKeys.ToPrefix(path).ShouldBe(expected);
    }

    /// <remarks>
    /// Empty rather than <c>"/"</c>, because a prefix of <c>/</c> matches nothing: no key stored by anything
    /// begins with a slash. This is the case that makes the root of a bucket list at all.
    /// </remarks>
    [Fact]
    public void TheRootHasAnEmptyPrefix()
    {
        ObjectKeys.ToPrefix("/").ShouldBeEmpty();
    }

    /// <remarks>
    /// A common prefix arrives with its trailing slash on, so trimming has to happen before the last segment
    /// is taken — otherwise every directory in the listing is named "".
    /// </remarks>
    [Theory]
    [InlineData("reports/2026/", "2026")]
    [InlineData("reports/2026/q3.csv", "q3.csv")]
    [InlineData("q3.csv", "q3.csv")]
    [InlineData("reports/", "reports")]
    public void ANameIsTheLastSegment(string key, string expected)
    {
        ObjectKeys.NameOf(key).ShouldBe(expected);
    }

    [Theory]
    [InlineData("/")]
    [InlineData("/reports")]
    [InlineData("/reports/2026/q3.csv")]
    public void APathSurvivesARoundTrip(string path)
    {
        ObjectKeys.ToPath(ObjectKeys.ToKey(path)).ShouldBe(path);
    }
}
