using System.Reflection;

namespace DodoSSH.Client.App.Platform;

/// <summary>
/// Which of the two desktop channels this build belongs to.
/// </summary>
/// <remarks>
/// <para>
/// The counterpart of the Android head's <c>DodoChannel</c> metadata, read the same way and for the same
/// reasons. Three things depend on the answer and they are listed here rather than discovered one at a
/// time: which Velopack channel the updater reads, whether that read considers prereleases, and which
/// profile directory this copy keeps its cache and device key in.
/// </para>
/// <para>
/// <b>Android gets this separation from the platform and this head has to build it.</b> Two Android
/// channels cannot replace one another because the installer refuses a package signed by a different key.
/// Nothing refuses anything on Windows: Velopack applies what its feed serves, without verifying a
/// signature. So the two channels are kept apart by construction here — a pack id each, so they install
/// in different directories; a Velopack channel each, so neither ever reads the other's release index; and
/// a profile directory each, so a nightly cannot migrate the schema of a cache the release build is using.
/// See <c>docs/adr/0013-desktop-distribution-and-updates.md</c> rule 9.
/// </para>
/// <para>
/// Resolved once, at first use. The channel of a build does not change while it runs, and a value read
/// per call site is one that eventually gets read differently in two places.
/// </para>
/// </remarks>
internal static class DesktopChannel
{
    /// <summary>The build this channel is not: the one a person cuts from a tag.</summary>
    internal const string Release = "release";

    /// <summary>The build CI publishes from main, which installs beside the release one.</summary>
    internal const string Nightly = "nightly";

    /// <summary>
    /// What this build says it is, defaulting to the release channel.
    /// </summary>
    /// <remarks>
    /// The csproj always writes the metadata, so the fallback covers a build that reached here some other
    /// way — an assembly loaded by a test host, a designer, a trimmed-down copy. <see cref="Release"/> is
    /// the safe answer for all of them: it is what the value was before there were two channels, and every
    /// consequence of being wrong about it is inert. A build that is not installed has no updater at all
    /// (see <c>UpdateChannels.ForThisMachine</c>), and the profile directory it names is the one every
    /// existing install already uses.
    /// </remarks>
    internal static string Name { get; } = Read();

    /// <summary>Whether this build belongs to the nightly channel.</summary>
    internal static bool IsNightly => string.Equals(Name, Nightly, StringComparison.Ordinal);

    private static string Read()
    {
        var declared = typeof(DesktopChannel).Assembly
            .GetCustomAttributes<AssemblyMetadataAttribute>()
            .FirstOrDefault(attribute => string.Equals(attribute.Key, "DodoChannel", StringComparison.Ordinal))
            ?.Value;

        // Only the two the csproj declares are honoured. An unrecognised value is a build made by
        // something nobody here wrote, and answering "release" to it is the same inert default as
        // answering it to no value at all — rather than pointing an updater at a feed named by a string
        // of unknown origin.
        return string.Equals(declared, Nightly, StringComparison.Ordinal) ? Nightly : Release;
    }
}
