namespace GmRelay.Bot.Tests.Web;

public sealed class PortfolioSchemaGateSourceTests
{
    [Fact]
    public async Task Compose_ShouldStartDiscordAndWebOnlyAfterBotMigrationsAreHealthy()
    {
        var compose = await ReadRepositoryFileAsync("compose.yaml");

        AssertServiceDependsOnHealthyBot(compose, "discord");
        AssertServiceDependsOnHealthyBot(compose, "web");
    }

    [Fact]
    public async Task Aspire_ShouldStartDiscordAndWebOnlyAfterBotMigrationsAreHealthy()
    {
        var appHost = NormalizeSource(await ReadRepositoryFileAsync("src/GmRelay.AppHost/Program.cs"));

        Assert.Contains(
            "var bot = builder.AddProject<Projects.GmRelay_Bot>(\"bot\") .WithReference(postgres) .WaitFor(postgres) .WithHttpEndpoint(port: 8081, targetPort: 8081, name: \"health\", isProxied: false) .WithHttpHealthCheck(\"/health\", endpointName: \"health\");",
            appHost,
            StringComparison.Ordinal);
        Assert.Contains(
            "builder.AddProject<Projects.GmRelay_DiscordBot>(\"discord\") .WithReference(postgres) .WaitFor(postgres) .WaitFor(bot);",
            appHost,
            StringComparison.Ordinal);
        Assert.Contains(
            "builder.AddProject<Projects.GmRelay_Web>(\"web\") .WithReference(postgres) .WaitFor(postgres) .WaitFor(bot);",
            appHost,
            StringComparison.Ordinal);
    }

    [Fact]
    public async Task Aspire_ShouldUseApplicationDatabaseConnectionStringName()
    {
        var appHost = NormalizeSource(await ReadRepositoryFileAsync("src/GmRelay.AppHost/Program.cs"));

        Assert.Contains(".AddDatabase(\"gmrelaydb\");", appHost, StringComparison.Ordinal);
    }

    private static void AssertServiceDependsOnHealthyBot(string compose, string serviceName)
    {
        var serviceBlock = GetServiceBlock(compose, serviceName);

        Assert.Contains(
            """
                  bot:
                    condition: service_healthy
            """,
            serviceBlock,
            StringComparison.Ordinal);
    }

    private static string GetServiceBlock(string compose, string serviceName)
    {
        var lines = compose.Split('\n');
        var start = Array.FindIndex(lines, line => line.TrimEnd('\r') == $"  {serviceName}:");
        Assert.True(start >= 0, $"compose.yaml should contain service '{serviceName}'.");

        var end = Array.FindIndex(
            lines,
            start + 1,
            line => line.StartsWith("  ", StringComparison.Ordinal)
                && !line.StartsWith("    ", StringComparison.Ordinal)
                && line.TrimEnd('\r').EndsWith(':'));

        return string.Join('\n', lines[start..(end < 0 ? lines.Length : end)]);
    }

    private static string NormalizeSource(string source)
    {
        return string.Join(' ', source.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries));
    }

    private static async Task<string> ReadRepositoryFileAsync(string relativePath)
    {
        var directory = new DirectoryInfo(AppContext.BaseDirectory);
        while (directory is not null)
        {
            var candidate = Path.Combine(directory.FullName, relativePath);
            if (File.Exists(candidate))
            {
                return await File.ReadAllTextAsync(candidate);
            }

            directory = directory.Parent;
        }

        throw new FileNotFoundException($"Could not locate repository file '{relativePath}'.");
    }
}
