using System;
using System.Collections;
using Unity.BossRoom.ConnectionManagement;
using Unity.BossRoom.Gameplay.GameplayObjects;
using Unity.BossRoom.Infrastructure;
using Unity.Multiplayer.Samples;
using Unity.Multiplayer.Samples.BossRoom;
using Unity.Multiplayer.Samples.Utilities;
using Unity.Netcode;
using UnityEngine;
using VContainer;

namespace Unity.BossRoom.Gameplay.GameState
{
    /// <summary>
    /// Server specialization of Character Select game state.
    /// </summary>
    [RequireComponent(typeof(NetcodeHooks), typeof(NetworkCharSelection))]
    public class ServerCharSelectState : GameStateBehaviour
    {
        [SerializeField]
        NetcodeHooks m_NetcodeHooks;

        public override GameState ActiveState => GameState.CharSelect;
        public NetworkCharSelection networkCharSelection { get; private set; }

        Coroutine m_WaitToEndLobbyCoroutine;

        [Inject]
        ConnectionManager m_ConnectionManager;

        protected override void Awake()
        {
            base.Awake();
            networkCharSelection = GetComponent<NetworkCharSelection>();

            m_NetcodeHooks.OnNetworkSpawnHook += OnNetworkSpawn;
            m_NetcodeHooks.OnNetworkDespawnHook += OnNetworkDespawn;
        }

        protected override void OnDestroy()
        {
            base.OnDestroy();

            if (m_NetcodeHooks)
            {
                m_NetcodeHooks.OnNetworkSpawnHook -= OnNetworkSpawn;
                m_NetcodeHooks.OnNetworkDespawnHook -= OnNetworkDespawn;
            }
        }

        void OnClientChangedSeat(ulong clientId, int newSeatIdx, bool lockedIn)
        {
            int idx = FindLobbyPlayerIdx(clientId);
            if (idx == -1)
            {
                throw new Exception($"OnClientChangedSeat: client ID {clientId} is not a lobby player and cannot change seats! Shouldn't be here!");
            }

            if (networkCharSelection.IsLobbyClosed.Value)
            {
                // The user tried to change their class after everything was locked in... too late! Discard this choice
                return;
            }

            // Allow multiple players to select the same character without restrictions
            networkCharSelection.LobbyPlayers[idx] = new NetworkCharSelection.LobbyPlayerState(clientId,
                networkCharSelection.LobbyPlayers[idx].PlayerName,
                networkCharSelection.LobbyPlayers[idx].PlayerNumber,
                lockedIn ? NetworkCharSelection.SeatState.LockedIn : NetworkCharSelection.SeatState.Active,
                newSeatIdx,
                Time.time);

            CloseLobbyIfReady();
        }

        /// <summary>
        /// Returns the index of a client in the master LobbyPlayer list, or -1 if not found
        /// </summary>
        int FindLobbyPlayerIdx(ulong clientId)
        {
            for (int i = 0; i < networkCharSelection.LobbyPlayers.Count; ++i)
            {
                if (networkCharSelection.LobbyPlayers[i].ClientId == clientId)
                    return i;
            }
            return -1;
        }

        void CloseLobbyIfReady()
        {
            if (DedicatedServerUtilities.IsServerBuildTarget && networkCharSelection.LobbyPlayers.Count == 0)
            {
                return;
            }

            foreach (NetworkCharSelection.LobbyPlayerState playerInfo in networkCharSelection.LobbyPlayers)
            {
                if (playerInfo.SeatState != NetworkCharSelection.SeatState.LockedIn)
                    return; // at least one player isn't locked in yet!
            }

            networkCharSelection.IsLobbyClosed.Value = true;
            Debug.Log("Testing");
            SaveLobbyResults();
            m_WaitToEndLobbyCoroutine = StartCoroutine(WaitToEndLobby());
        }

        void CancelCloseLobby()
        {
            if (m_WaitToEndLobbyCoroutine != null)
            {
                StopCoroutine(m_WaitToEndLobbyCoroutine);
            }
            networkCharSelection.IsLobbyClosed.Value = false;
        }

        void SaveLobbyResults()
        {
            int numofPlayersInLobby = networkCharSelection.LobbyPlayers.Count;
            PlayerPrefs.SetInt("NumberOfLobbyPlayers", numofPlayersInLobby);
            Debug.Log("Number of Players in lobby are: " + numofPlayersInLobby);
            foreach (NetworkCharSelection.LobbyPlayerState playerInfo in networkCharSelection.LobbyPlayers)
            {
                var playerNetworkObject = NetworkManager.Singleton.SpawnManager.GetPlayerNetworkObject(playerInfo.ClientId);

                if (playerNetworkObject && playerNetworkObject.TryGetComponent(out PersistentPlayer persistentPlayer))
                {
                    persistentPlayer.NetworkAvatarGuidState.AvatarGuid.Value =
                        networkCharSelection.AvatarConfiguration[playerInfo.SeatIdx].Guid.ToNetworkGuid();
                }
            }
        }

        IEnumerator WaitToEndLobby()
        {
            yield return new WaitForSeconds(3);
            SceneLoaderWrapper.Instance.LoadScene("BossRoom", useNetworkSceneManager: true);
        }

        public void OnNetworkDespawn()
        {
            if (NetworkManager.Singleton)
            {
                NetworkManager.Singleton.OnClientDisconnectCallback -= OnClientDisconnectCallback;
                NetworkManager.Singleton.SceneManager.OnSceneEvent -= OnSceneEvent;
            }
            if (networkCharSelection)
            {
                networkCharSelection.OnClientChangedSeat -= OnClientChangedSeat;
            }
        }

        public void OnNetworkSpawn()
        {
            if (!NetworkManager.Singleton.IsServer)
            {
                enabled = false;
            }
            else
            {
                NetworkManager.Singleton.OnClientDisconnectCallback += OnClientDisconnectCallback;
                networkCharSelection.OnClientChangedSeat += OnClientChangedSeat;

                NetworkManager.Singleton.SceneManager.OnSceneEvent += OnSceneEvent;
            }
        }

        void OnSceneEvent(SceneEvent sceneEvent)
        {
            if (sceneEvent.SceneEventType != SceneEventType.LoadComplete) return;
            SeatNewPlayer(sceneEvent.ClientId);
        }

        int GetAvailablePlayerNumber()
        {
            for (int possiblePlayerNumber = 0; possiblePlayerNumber < m_ConnectionManager.MaxConnectedPlayers; ++possiblePlayerNumber)
            {
                if (IsPlayerNumberAvailable(possiblePlayerNumber))
                {
                    return possiblePlayerNumber;
                }
            }
            return -1;
        }

        bool IsPlayerNumberAvailable(int playerNumber)
        {
            foreach (NetworkCharSelection.LobbyPlayerState playerState in networkCharSelection.LobbyPlayers)
            {
                if (playerState.PlayerNumber == playerNumber)
                {
                    return false;
                }
            }
            return true;
        }

        void SeatNewPlayer(ulong clientId)
        {
            if (networkCharSelection.IsLobbyClosed.Value)
            {
                CancelCloseLobby();
            }

            SessionPlayerData? sessionPlayerData = SessionManager<SessionPlayerData>.Instance.GetPlayerData(clientId);
            if (sessionPlayerData.HasValue)
            {
                var playerData = sessionPlayerData.Value;
                if (playerData.PlayerNumber == -1 || !IsPlayerNumberAvailable(playerData.PlayerNumber))
                {
                    playerData.PlayerNumber = GetAvailablePlayerNumber();
                }
                if (playerData.PlayerNumber == -1)
                {
                    throw new Exception($"No available player number for client ID {clientId}");
                }

                networkCharSelection.LobbyPlayers.Add(new NetworkCharSelection.LobbyPlayerState(clientId, playerData.PlayerName, playerData.PlayerNumber, NetworkCharSelection.SeatState.Inactive));
                SessionManager<SessionPlayerData>.Instance.SetPlayerData(clientId, playerData);
            }
        }

        void OnClientDisconnectCallback(ulong clientId)
        {
            for (int i = 0; i < networkCharSelection.LobbyPlayers.Count; ++i)
            {
                if (networkCharSelection.LobbyPlayers[i].ClientId == clientId)
                {
                    networkCharSelection.LobbyPlayers.RemoveAt(i);
                    break;
                }
            }

            if (!networkCharSelection.IsLobbyClosed.Value)
            {
                CloseLobbyIfReady();
            }
        }
    }
}

//using System;
//using System.Collections;
//using Unity.BossRoom.ConnectionManagement;
//using Unity.BossRoom.Gameplay.GameplayObjects;
//using Unity.BossRoom.Infrastructure;
//using Unity.Multiplayer.Samples;
//using Unity.Multiplayer.Samples.BossRoom;
//using Unity.Multiplayer.Samples.Utilities;
//using Unity.Netcode;
//using UnityEngine;
//using VContainer;

//namespace Unity.BossRoom.Gameplay.GameState
//{
//    /// <summary>
//    /// Server specialization of Character Select game state.
//    /// </summary>
//    [RequireComponent(typeof(NetcodeHooks), typeof(NetworkCharSelection))]
//    public class ServerCharSelectState : GameStateBehaviour
//    {
//        [SerializeField]
//        NetcodeHooks m_NetcodeHooks;

//        public override GameState ActiveState => GameState.CharSelect;
//        public NetworkCharSelection networkCharSelection { get; private set; }

//        Coroutine m_WaitToEndLobbyCoroutine;

//        [Inject]
//        ConnectionManager m_ConnectionManager;

//        protected override void Awake()
//        {
//            base.Awake();
//            networkCharSelection = GetComponent<NetworkCharSelection>();

//            m_NetcodeHooks.OnNetworkSpawnHook += OnNetworkSpawn;
//            m_NetcodeHooks.OnNetworkDespawnHook += OnNetworkDespawn;
//        }

//        protected override void OnDestroy()
//        {
//            base.OnDestroy();

//            if (m_NetcodeHooks)
//            {
//                m_NetcodeHooks.OnNetworkSpawnHook -= OnNetworkSpawn;
//                m_NetcodeHooks.OnNetworkDespawnHook -= OnNetworkDespawn;
//            }
//        }

//        // void OnClientChangedSeat(ulong clientId, int newSeatIdx, bool lockedIn)
//        // {
//        //     int idx = FindLobbyPlayerIdx(clientId);
//        //     if (idx == -1)
//        //     {
//        //         throw new Exception($"OnClientChangedSeat: client ID {clientId} is not a lobby player and cannot change seats! Shouldn't be here!");
//        //     }
//        //
//        //     if (networkCharSelection.IsLobbyClosed.Value)
//        //     {
//        //         // The user tried to change their class after everything was locked in... too late! Discard this choice
//        //         return;
//        //     }
//        //
//        //     // Allow multiple players to select the same character without restrictions
//        //     networkCharSelection.LobbyPlayers[idx] = new NetworkCharSelection.LobbyPlayerState(clientId,
//        //         networkCharSelection.LobbyPlayers[idx].PlayerName,
//        //         networkCharSelection.LobbyPlayers[idx].PlayerNumber,
//        //         lockedIn ? NetworkCharSelection.SeatState.LockedIn : NetworkCharSelection.SeatState.Active,
//        //         newSeatIdx,
//        //         Time.time);
//        //     Debug.Log("CloseLobbyIfReady");
//        //     CloseLobbyIfReady();
//        // }

//        void OnClientChangedSeat(ulong clientId, int newSeatIdx, bool lockedIn)
//        {
//            int idx = FindLobbyPlayerIdx(clientId);
//            if (idx == -1)
//            {
//                throw new Exception($"OnClientChangedSeat: client ID {clientId} is not a lobby player and cannot change seats! Shouldn't be here!");
//            }

//            if (networkCharSelection.IsLobbyClosed.Value)
//            {
//                // The user tried to change their class after everything was locked in... too late! Discard this choice
//                return;
//            }

//            if (newSeatIdx == -1)
//            {
//                // we can't lock in with no seat
//                lockedIn = false;
//            }
//            else
//            {
//                // see if someone has already locked-in that seat! If so, too late... discard this choice
//                foreach (NetworkCharSelection.LobbyPlayerState playerInfo in networkCharSelection.LobbyPlayers)
//                {
//                    if (playerInfo.ClientId != clientId && playerInfo.SeatIdx == newSeatIdx && playerInfo.SeatState == NetworkCharSelection.SeatState.LockedIn)
//                    {
//                        // somebody already locked this choice in. Stop!
//                        // Instead of granting lock request, change this player to Inactive state.
//                        networkCharSelection.LobbyPlayers[idx] = new NetworkCharSelection.LobbyPlayerState(clientId,
//                            networkCharSelection.LobbyPlayers[idx].PlayerName,
//                            networkCharSelection.LobbyPlayers[idx].PlayerNumber,
//                            NetworkCharSelection.SeatState.Inactive);

//                        // then early out
//                        return;
//                    }
//                }
//            }

//            networkCharSelection.LobbyPlayers[idx] = new NetworkCharSelection.LobbyPlayerState(clientId,
//                networkCharSelection.LobbyPlayers[idx].PlayerName,
//                networkCharSelection.LobbyPlayers[idx].PlayerNumber,
//                lockedIn ? NetworkCharSelection.SeatState.LockedIn : NetworkCharSelection.SeatState.Active,
//                newSeatIdx,
//                Time.time);

//            if (lockedIn)
//            {
//                // to help the clients visually keep track of who's in what seat, we'll "kick out" any other players
//                // who were also in that seat. (Those players didn't click "Ready!" fast enough, somebody else took their seat!)
//                for (int i = 0; i < networkCharSelection.LobbyPlayers.Count; ++i)
//                {
//                    if (networkCharSelection.LobbyPlayers[i].SeatIdx == newSeatIdx && i != idx)
//                    {
//                        // change this player to Inactive state.
//                        networkCharSelection.LobbyPlayers[i] = new NetworkCharSelection.LobbyPlayerState(
//                            networkCharSelection.LobbyPlayers[i].ClientId,
//                            networkCharSelection.LobbyPlayers[i].PlayerName,
//                            networkCharSelection.LobbyPlayers[i].PlayerNumber,
//                            NetworkCharSelection.SeatState.Inactive);
//                    }
//                }
//            }

//            CloseLobbyIfReady();
//        }

//        /// <summary>
//        /// Returns the index of a client in the master LobbyPlayer list, or -1 if not found
//        /// </summary>
//        int FindLobbyPlayerIdx(ulong clientId)
//        {
//            for (int i = 0; i < networkCharSelection.LobbyPlayers.Count; ++i)
//            {
//                if (networkCharSelection.LobbyPlayers[i].ClientId == clientId)
//                    return i;
//            }
//            return -1;
//        }
//        public void StartGame()
//        {
//            networkCharSelection.IsLobbyClosed.Value = true;
//            SaveLobbyResults();
//            m_WaitToEndLobbyCoroutine = StartCoroutine(WaitToEndLobby());
//        }
//        void CloseLobbyIfReady()
//        {
//            // If dedicated server, leave char select open if there's no one left
//            if (DedicatedServerUtilities.IsServerBuildTarget && networkCharSelection.LobbyPlayers.Count == 0)
//            {
//                return;
//            }

//            foreach (NetworkCharSelection.LobbyPlayerState playerInfo in networkCharSelection.LobbyPlayers)
//            {
//                if (playerInfo.SeatState != NetworkCharSelection.SeatState.LockedIn)
//                    return; // nope, at least one player isn't locked in yet!
//            }

//            // everybody's ready at the same time! Lock it down!
//            networkCharSelection.IsLobbyClosed.Value = true;

//            // remember our choices so the next scene can use the info
//            SaveLobbyResults();

//            // Delay a few seconds to give the UI time to react, then switch scenes
//            IEnumerator WaitToEndLobby()
//            {
//                yield return new WaitForSeconds(3);
//                SceneLoaderWrapper.Instance.LoadScene("BossRoom", useNetworkSceneManager: true);
//            }
//            m_WaitToEndLobbyCoroutine = StartCoroutine(WaitToEndLobby());
//        }


//        // void CloseLobbyIfReady()
//        // {
//        //     // If dedicated server, leave char select open if there's no one left
//        //     if (DedicatedServerUtilities.IsServerBuildTarget && networkCharSelection.LobbyPlayers.Count == 0)
//        //     {
//        //         return;
//        //     }
//        //     
//        //     startButtonScipt.InteractableTruer(false);
//        //     foreach (NetworkCharSelection.LobbyPlayerState playerInfo in networkCharSelection.LobbyPlayers)
//        //     {
//        //         if (playerInfo.SeatState != NetworkCharSelection.SeatState.LockedIn)
//        //             return; // at least one player isn't locked in yet!
//        //     }
//        //     startButtonScipt.InteractableTruer(true);
//        // }
//        public StartButtonScipt startButtonScipt;
//        void CancelCloseLobby()
//        {
//            if (m_WaitToEndLobbyCoroutine != null)
//            {
//                StopCoroutine(m_WaitToEndLobbyCoroutine);
//            }
//            networkCharSelection.IsLobbyClosed.Value = false;
//        }

//        void SaveLobbyResults()
//        {
//            int numofPlayersInLobby = networkCharSelection.LobbyPlayers.Count;
//            PlayerPrefs.SetInt("NumberOfLobbyPlayers", numofPlayersInLobby);
//            Debug.Log("Number of Players in lobby are: " + numofPlayersInLobby);
//            foreach (NetworkCharSelection.LobbyPlayerState playerInfo in networkCharSelection.LobbyPlayers)
//            {
//                var playerNetworkObject = NetworkManager.Singleton.SpawnManager.GetPlayerNetworkObject(playerInfo.ClientId);

//                if (playerNetworkObject && playerNetworkObject.TryGetComponent(out PersistentPlayer persistentPlayer))
//                {
//                    persistentPlayer.NetworkAvatarGuidState.AvatarGuid.Value =
//                        networkCharSelection.AvatarConfiguration[playerInfo.SeatIdx].Guid.ToNetworkGuid();
//                }
//            }
//        }

//        IEnumerator WaitToEndLobby()
//        {
//            yield return new WaitForSeconds(3);
//            SceneLoaderWrapper.Instance.LoadScene("BossRoom", useNetworkSceneManager: true);
//        }

//        public void OnNetworkDespawn()
//        {
//            if (NetworkManager.Singleton)
//            {
//                NetworkManager.Singleton.OnClientDisconnectCallback -= OnClientDisconnectCallback;
//                NetworkManager.Singleton.SceneManager.OnSceneEvent -= OnSceneEvent;
//            }
//            if (networkCharSelection)
//            {
//                networkCharSelection.OnClientChangedSeat -= OnClientChangedSeat;
//            }
//        }

//        public void OnNetworkSpawn()
//        {
//            if (!NetworkManager.Singleton.IsServer)
//            {
//                enabled = false;
//            }
//            else
//            {
//                NetworkManager.Singleton.OnClientDisconnectCallback += OnClientDisconnectCallback;
//                networkCharSelection.OnClientChangedSeat += OnClientChangedSeat;

//                NetworkManager.Singleton.SceneManager.OnSceneEvent += OnSceneEvent;
//            }
//        }

//        void OnSceneEvent(SceneEvent sceneEvent)
//        {
//            if (sceneEvent.SceneEventType != SceneEventType.LoadComplete) return;
//            SeatNewPlayer(sceneEvent.ClientId);
//        }

//        int GetAvailablePlayerNumber()
//        {
//            for (int possiblePlayerNumber = 0; possiblePlayerNumber < m_ConnectionManager.MaxConnectedPlayers; ++possiblePlayerNumber)
//            {
//                if (IsPlayerNumberAvailable(possiblePlayerNumber))
//                {
//                    return possiblePlayerNumber;
//                }
//            }
//            return -1;
//        }

//        bool IsPlayerNumberAvailable(int playerNumber)
//        {
//            foreach (NetworkCharSelection.LobbyPlayerState playerState in networkCharSelection.LobbyPlayers)
//            {
//                if (playerState.PlayerNumber == playerNumber)
//                {
//                    return false;
//                }
//            }
//            return true;
//        }

//        void SeatNewPlayer(ulong clientId)
//        {
//            if (networkCharSelection.IsLobbyClosed.Value)
//            {
//                CancelCloseLobby();
//            }

//            SessionPlayerData? sessionPlayerData = SessionManager<SessionPlayerData>.Instance.GetPlayerData(clientId);
//            if (sessionPlayerData.HasValue)
//            {
//                var playerData = sessionPlayerData.Value;
//                if (playerData.PlayerNumber == -1 || !IsPlayerNumberAvailable(playerData.PlayerNumber))
//                {
//                    playerData.PlayerNumber = GetAvailablePlayerNumber();
//                }
//                if (playerData.PlayerNumber == -1)
//                {
//                    throw new Exception($"No available player number for client ID {clientId}");
//                }

//                networkCharSelection.LobbyPlayers.Add(new NetworkCharSelection.LobbyPlayerState(clientId, playerData.PlayerName, playerData.PlayerNumber, NetworkCharSelection.SeatState.Inactive));
//                SessionManager<SessionPlayerData>.Instance.SetPlayerData(clientId, playerData);
//            }
//        }

//        void OnClientDisconnectCallback(ulong clientId)
//        {
//            for (int i = 0; i < networkCharSelection.LobbyPlayers.Count; ++i)
//            {
//                if (networkCharSelection.LobbyPlayers[i].ClientId == clientId)
//                {
//                    networkCharSelection.LobbyPlayers.RemoveAt(i);
//                    break;
//                }
//            }

//            if (!networkCharSelection.IsLobbyClosed.Value)
//            {
//                CloseLobbyIfReady();
//            }
//        }
//    }
//}