using System;
using System.IO;
using System.Xml.Linq;

namespace GmRelay.Bot.Tests.Discord;

public sealed class DiscordProjectStructureTests
{
    private static string GetRepoRoot()
    {
        var dir = AppContext.BaseDirectory;
        while (!string.IsNullOrEmpty(dir) && !File.Exists(Path.Combine(dir, "Directory.Build.props")))
        {
            dir = Directory.GetParent(dir)?.FullName;
        }

        return dir ?? throw new InvalidOperationException("Could not find repo root");
    }

    [Fact]
    public void Solution_ShouldIncludeDiscordWorkerProject()
    {
        var repoRoot = GetRepoRoot();
        var solution = File.ReadAllText(Path.Combine(repoRoot, "GM-Relay.slnx"));

        Assert.Contains("src/GmRelay.DiscordBot/GmRelay.DiscordBot.csproj", solution);
    }

    [Fact]
    public void DiscordWorkerProject_ShouldExistWithoutTelegramDependency()
    {
        var repoRoot = GetRepoRoot();
        var projectPath = Path.Combine(repoRoot, "src", "GmRelay.DiscordBot", "GmRelay.DiscordBot.csproj");

        Assert.True(File.Exists(projectPath), "Discord worker project should exist.");

        var project = File.ReadAllText(projectPath);
        Assert.Contains("Microsoft.NET.Sdk.Worker", project);
        Assert.Contains("NetCord.Hosting", project);
        Assert.Contains("GmRelay.ServiceDefaults.csproj", project);
        Assert.Contains("GmRelay.Shared.csproj", project);
        Assert.DoesNotContain("Telegram.Bot", project);
        Assert.DoesNotContain("GmRelay.Bot.csproj", project);
        Assert.Contains("Dapper.AOT", project);
    }

    [Fact]
    public void TelegramWorkerProject_ShouldNotReferenceNetCord()
    {
        var repoRoot = GetRepoRoot();
        var project = File.ReadAllText(Path.Combine(repoRoot, "src", "GmRelay.Bot", "GmRelay.Bot.csproj"));

        Assert.DoesNotContain("NetCord", project, StringComparison.OrdinalIgnoreCase);
    }

    [Fact]
    public void RuntimeWiring_ShouldIncludeDiscordServiceWithoutCouplingTelegram()
    {
        var repoRoot = GetRepoRoot();
        var compose = File.ReadAllText(Path.Combine(repoRoot, "compose.yaml"));
        var appHostProject = File.ReadAllText(Path.Combine(repoRoot, "src", "GmRelay.AppHost", "GmRelay.AppHost.csproj"));
        var appHostProgram = File.ReadAllText(Path.Combine(repoRoot, "src", "GmRelay.AppHost", "Program.cs"));
        var prChecks = File.ReadAllText(Path.Combine(repoRoot, ".gitea", "workflows", "pr-checks.yml"));
        var deploy = File.ReadAllText(Path.Combine(repoRoot, ".gitea", "workflows", "deploy.yml"));
        var version = GetProjectVersion(repoRoot);

        Assert.Contains($"gmrelay-discord-bot:{version}", compose);
        Assert.Contains("Discord__Token=${DISCORD_BOT_TOKEN:?Set DISCORD_BOT_TOKEN in .env}", compose);
        Assert.Contains("src/GmRelay.DiscordBot/Dockerfile", deploy);
        Assert.Contains("DISCORD_BOT_TOKEN", deploy);
        Assert.Contains("dotnet build src/GmRelay.DiscordBot/GmRelay.DiscordBot.csproj --no-restore", prChecks);
        Assert.Contains("GmRelay.DiscordBot.csproj", appHostProject);
        Assert.Contains("Projects.GmRelay_DiscordBot", appHostProgram);
    }

    [Fact]
    public void Version_ShouldBeSynchronizedForDiscordFeatureRelease()
    {
        var repoRoot = GetRepoRoot();
        var version = GetProjectVersion(repoRoot);

        Assert.Contains($"<Version>{version}</Version>", File.ReadAllText(Path.Combine(repoRoot, "Directory.Build.props")));
        Assert.Contains($"VERSION: {version}", File.ReadAllText(Path.Combine(repoRoot, ".gitea", "workflows", "deploy.yml")));
        Assert.Contains($"gmrelay-bot:{version}", File.ReadAllText(Path.Combine(repoRoot, "compose.yaml")));
        Assert.Contains($"gmrelay-web:{version}", File.ReadAllText(Path.Combine(repoRoot, "compose.yaml")));
        Assert.Contains($"gmrelay-discord-bot:{version}", File.ReadAllText(Path.Combine(repoRoot, "compose.yaml")));
        Assert.Contains(
            $"v{version}",
            File.ReadAllText(Path.Combine(repoRoot, "src", "GmRelay.Web", "Components", "Layout", "NavMenu.razor")));
    }

    [Fact]
    public void EnvExample_ShouldContainDiscordBotToken()
    {
        var repoRoot = GetRepoRoot();
        var envExample = File.ReadAllText(Path.Combine(repoRoot, ".env.example"));

        Assert.Contains("DISCORD_BOT_TOKEN", envExample);
    }

    [Fact]
    public void Readme_ShouldNotAskForUnusedDiscordBotClientId()
    {
        var repoRoot = GetRepoRoot();
        var readme = File.ReadAllText(Path.Combine(repoRoot, "README.md"));

        Assert.DoesNotContain("DISCORD_BOT_CLIENT_ID", readme);
        Assert.Contains("DISCORD_CLIENT_ID", readme);
    }

    [Fact]
    public void Compose_ShouldIncludeDiscordHealthcheck()
    {
        var repoRoot = GetRepoRoot();
        var compose = File.ReadAllText(Path.Combine(repoRoot, "compose.yaml"));

        var discordIndex = compose.IndexOf("discord:", StringComparison.Ordinal);
        Assert.True(discordIndex >= 0, "compose.yaml should contain discord service");

        var nextServiceIndex = compose.IndexOf("  web:", StringComparison.Ordinal);
        var discordBlock = compose[discordIndex..nextServiceIndex];

        Assert.Contains("healthcheck:", discordBlock);
        Assert.Contains("test:", discordBlock);
        Assert.Contains("localhost:8082/health", discordBlock);
    }

    private static string GetProjectVersion(string repoRoot)
    {
        var props = XDocument.Load(Path.Combine(repoRoot, "Directory.Build.props"));
        return props.Root?
            .Element("PropertyGroup")?
            .Element("Version")?
            .Value ?? throw new InvalidOperationException("Version not found.");
    }
}
