using System;
using System.IO;
using System.Linq;
using fNbt;

namespace ModpackLauncher.Services;

/// <summary>
/// Pre-populates Minecraft's multiplayer server list (`servers.dat`) so the
/// modpack's server is one click away on first launch.
///
/// servers.dat is uncompressed NBT (unlike level.dat which is gzipped). Schema:
///   compound {
///     servers : list[compound] {
///       name             : string
///       ip               : string
///       acceptTextures   : byte (optional, 1 = enabled)
///       hidden           : byte (optional, 0 = visible)
///       icon             : string (optional, base64 PNG)
///     }
///   }
/// </summary>
public sealed class ServerListService
{
    /// <summary>
    /// Add or update an entry. Match-by-IP: if an entry with the same IP exists,
    /// update its name; otherwise prepend a new entry so the friend's first
    /// glance at multiplayer shows our server at the top.
    /// </summary>
    public void EnsureServer(string gameDir, string name, string ip)
    {
        if (string.IsNullOrWhiteSpace(name) || string.IsNullOrWhiteSpace(ip)) return;
        Directory.CreateDirectory(gameDir);
        var path = Path.Combine(gameDir, "servers.dat");

        NbtCompound root;
        NbtList servers;

        if (File.Exists(path))
        {
            try
            {
                var nbt = new NbtFile();
                nbt.LoadFromFile(path, NbtCompression.None, _ => true);
                root = nbt.RootTag;
                if (root.TryGet("servers", out NbtList? existingList) && existingList is not null)
                {
                    servers = existingList;
                }
                else
                {
                    servers = new NbtList("servers", NbtTagType.Compound);
                    root.Add(servers);
                }
            }
            catch
            {
                // Existing file unreadable -- start fresh rather than crashing the install.
                root = new NbtCompound("");
                servers = new NbtList("servers", NbtTagType.Compound);
                root.Add(servers);
            }
        }
        else
        {
            root = new NbtCompound("");
            servers = new NbtList("servers", NbtTagType.Compound);
            root.Add(servers);
        }

        // Match-by-IP. If found, update the display name; otherwise prepend.
        for (int i = 0; i < servers.Count; i++)
        {
            if (servers[i] is not NbtCompound entry) continue;
            if (entry.TryGet("ip", out NbtString? ipTag) && ipTag?.Value == ip)
            {
                entry["name"] = new NbtString("name", name);
                Save(root, path);
                return;
            }
        }

        var newEntry = new NbtCompound
        {
            new NbtString("name", name),
            new NbtString("ip", ip),
            // acceptTextures = 1 lets the server send its resource pack without prompting
            // (the player still gets the prompt; this just allows the option). Default
            // value matches what vanilla MC writes when you click "Done" in the UI.
            new NbtByte("acceptTextures", 1),
        };
        servers.Insert(0, newEntry);
        Save(root, path);
    }

    private static void Save(NbtCompound root, string path)
    {
        var file = new NbtFile(root);
        file.SaveToFile(path, NbtCompression.None);
    }
}
