// Assets/_Project/Scripts/Net/NetworkBootstrap.cs
using System;
using Unity.Netcode;
using Unity.Netcode.Transports.UTP;
using UnityEngine;

namespace TD.Net
{
    /// <summary>
    /// Static facade for starting a host, joining a host, and disconnecting.
    /// Wraps NGO's <see cref="NetworkManager"/> and <see cref="UnityTransport"/>
    /// so the rest of the codebase (lobby UI, main menu UI) doesn't talk to
    /// NGO directly — and so the transport implementation can be swapped
    /// without touching lobby code.
    /// </summary>
    /// <remarks>
    /// <para><b>Why a static facade.</b> Today this is a thin wrapper over
    /// <see cref="UnityTransport"/>. When Steam integration lands
    /// (see <c>Project_Roadmap.md</c> §1.7-Future Steam Lobby Migration), this
    /// class becomes the seam: it grows into an <c>IConnectionProvider</c>
    /// abstraction with two implementations (<c>DirectIpConnectionProvider</c>
    /// for LAN / DRM-free distribution, <c>SteamConnectionProvider</c> for
    /// Steam friend-invite + lobby-browser flows). The lobby UI never changes;
    /// only this class does.</para>
    ///
    /// <para><b>Default port.</b> NGO's NetworkConfig holds the canonical
    /// transport settings (port, listen address). <see cref="DefaultPort"/>
    /// is just the value we use when the user doesn't override it in the UI.</para>
    ///
    /// <para><b>Scene management.</b> NGO's <c>SceneManager.LoadScene</c> is
    /// called by the host AFTER <see cref="StartHost"/> succeeds — see
    /// <see cref="LoadSceneAsHost"/>. The lobby and match scene names are
    /// canonicalized in <see cref="SceneNames"/>.</para>
    /// </remarks>
    public static class NetworkBootstrap
    {
        // ----- Configuration ---------------------------------------------

        /// <summary>Default port used by Host / Join when the UI doesn't override.</summary>
        public const ushort DefaultPort = 7777;

        /// <summary>Default listen address for the host. 0.0.0.0 listens on all interfaces.</summary>
        public const string DefaultListenAddress = "0.0.0.0";

        /// <summary>Default address clients use when no IP is entered (loopback for solo testing).</summary>
        public const string DefaultConnectAddress = "127.0.0.1";

        // ----- Public API ------------------------------------------------

        /// <summary>True if a host or client connection is currently active.</summary>
        public static bool IsConnected =>
            NetworkManager.Singleton != null
            && (NetworkManager.Singleton.IsHost
                || NetworkManager.Singleton.IsServer
                || NetworkManager.Singleton.IsClient);

        /// <summary>True if the local peer is the host (server + client in one process).</summary>
        public static bool IsHost =>
            NetworkManager.Singleton != null && NetworkManager.Singleton.IsHost;

        /// <summary>
        /// Starts a host on <paramref name="listenAddress"/>:<paramref name="port"/>.
        /// Returns true on success. Logs and returns false if NetworkManager
        /// is missing or already running.
        /// </summary>
        public static bool StartHost(ushort port = DefaultPort, string listenAddress = DefaultListenAddress)
        {
            var nm = NetworkManager.Singleton;
            if (nm == null)
            {
                Debug.LogError("[NetworkBootstrap] NetworkManager.Singleton is null. " +
                               "Make sure a NetworkManager exists in the active scene.");
                return false;
            }
            if (IsConnected)
            {
                Debug.LogWarning("[NetworkBootstrap] StartHost called while already connected. Ignored.");
                return false;
            }

            ConfigureTransport(listenAddress, port);

            bool ok = nm.StartHost();
            if (!ok) Debug.LogError("[NetworkBootstrap] NetworkManager.StartHost() returned false.");
            return ok;
        }

        /// <summary>
        /// Starts a client and connects to <paramref name="address"/>:<paramref name="port"/>.
        /// Returns true on success. Logs and returns false if NetworkManager
        /// is missing or already running.
        /// </summary>
        public static bool StartClient(string address, ushort port = DefaultPort)
        {
            var nm = NetworkManager.Singleton;
            if (nm == null)
            {
                Debug.LogError("[NetworkBootstrap] NetworkManager.Singleton is null. " +
                               "Make sure a NetworkManager exists in the active scene.");
                return false;
            }
            if (IsConnected)
            {
                Debug.LogWarning("[NetworkBootstrap] StartClient called while already connected. Ignored.");
                return false;
            }
            if (string.IsNullOrWhiteSpace(address))
                address = DefaultConnectAddress;

            ConfigureTransport(address, port);

            bool ok = nm.StartClient();
            if (!ok) Debug.LogError("[NetworkBootstrap] NetworkManager.StartClient() returned false.");
            return ok;
        }

        /// <summary>
        /// Shuts down the current host or client connection. Safe to call when
        /// already disconnected.
        /// </summary>
        public static void Disconnect()
        {
            var nm = NetworkManager.Singleton;
            if (nm == null) return;
            if (!IsConnected) return;

            nm.Shutdown();
        }

        /// <summary>
        /// Server-only helper that transitions every peer to <paramref name="sceneName"/>
        /// via NGO's networked scene manager. Logs and no-ops if not the server,
        /// if NGO scene management is disabled, or if the scene name is invalid.
        /// </summary>
        public static void LoadSceneAsHost(string sceneName)
        {
            var nm = NetworkManager.Singleton;
            if (nm == null) { Debug.LogError("[NetworkBootstrap] NetworkManager null."); return; }
            if (!nm.IsServer)
            {
                Debug.LogWarning($"[NetworkBootstrap] LoadSceneAsHost('{sceneName}') called on a client. Ignored.");
                return;
            }
            if (nm.SceneManager == null)
            {
                Debug.LogError("[NetworkBootstrap] NetworkManager.SceneManager is null — " +
                               "Enable Scene Management on the NetworkManager.");
                return;
            }
            nm.SceneManager.LoadScene(sceneName, UnityEngine.SceneManagement.LoadSceneMode.Single);
        }

        // ----- Internals --------------------------------------------------

        // Writes connection data into the UnityTransport component attached to
        // the NetworkManager. Doesn't allocate; reuses the transport instance
        // that's already in the scene.
        //
        // FUTURE STEAM MIGRATION: when SteamConnectionProvider lands, this
        // method's body is replaced (or split) to configure the appropriate
        // transport. The public API above stays as-is so call sites in the
        // lobby UI don't change.
        private static void ConfigureTransport(string address, ushort port)
        {
            var nm = NetworkManager.Singleton;
            var transport = nm.NetworkConfig?.NetworkTransport as UnityTransport;
            if (transport == null)
            {
                Debug.LogError("[NetworkBootstrap] NetworkManager's transport is not UnityTransport. " +
                               "If you switched transports (e.g. Steam), update NetworkBootstrap " +
                               "to configure the new one.");
                return;
            }
            transport.SetConnectionData(address, port, DefaultListenAddress);
        }
    }

    /// <summary>
    /// Canonical scene names used by scene transitions.
    /// Centralized so renames or additions touch one place.
    /// </summary>
    public static class SceneNames
    {
        public const string MainMenu = "MainMenu";
        public const string Lobby    = "Lobby";

        /// <summary>The gameplay scene (currently "Main" — the original prototype scene).</summary>
        public const string Match    = "Main";
    }
}
