using System;
using System.Collections.Generic;
using Firebase.Database;
using Firebase.Extensions;
using Newtonsoft.Json;
using TMPro;
using UnityEngine;

/// <summary>
/// This is the game state manager on the phone side
/// </summary>
public class GameManager : MonoBehaviour
{
    private GameState currentState;
    private List<Player> players = new();
    private bool isFirst = false;
    public Player currentPlayer = null;

    [Header("Other component")]
    public float explanationTime = 4f;
    private float currentExplanationTime = 0;

    [Header("Home Connection Component")]
    public TMP_InputField roomCodeField;
    public TextMeshProUGUI roomError;
    public TMP_InputField playerNameField;
    public TextMeshProUGUI nameError;
    public UnityEngine.UI.Button submitNewPlayer;

    [Header("WaitingRoom Component")]
    public TextMeshProUGUI listPlayersUI;
    public GameObject submitStartGame;

    [Header("Pages")]
    public GameObject HomeConnection;
    public GameObject WaitingRoom;
    public GameObject BeforeStart;
    public GameObject TakePicture;
    public GameObject VotePicture;
    public GameObject WaitingOtherPlayers;
    public GameObject EndGame;

    private DatabaseReference realtimeDB;
    public Room myRoom;
    private DatabaseReference myOnlineRoom;

    private void Awake()
    {
        FirebaseInitializer.Instance.onFirebaseReady += Initialize;
    }

    private void Start()
    {
        currentExplanationTime = explanationTime;
        HomeConnection.SetActive(true);
        submitNewPlayer.interactable = false;
    }
    private void OnApplicationQuit()
    {
        myOnlineRoom.Child("players").Child(currentPlayer.id).RemoveValueAsync();
        Debug.Log($"delete player {currentPlayer.name}");
        myRoom = null;
    }

    public GameState GetCurrentState()
    {
        return currentState;
    }

    private void Initialize()
    {
        FirebaseInitializer.Instance.onFirebaseReady -= Initialize;
        realtimeDB = FirebaseDatabase.DefaultInstance.RootReference;
        Debug.Log("Realtime DB initialized");

        submitNewPlayer.interactable = true;
    }

    /// <summary>
    /// Send your name and game room to the server
    /// </summary>
    public void PlayerValidateNameAndServerRoom(string _name, string _code)
    {
        nameError.gameObject.SetActive(false);
        roomError.gameObject.SetActive(false);

        if (string.IsNullOrEmpty(_name))
        {
            Debug.LogError("Player name is empty", this);

            nameError.text = "You have to put a valid name";
            nameError.gameObject.SetActive(true);

            //TODO : MARINE : use the error label to explain to the user that they have forget to put a name
            return;
        }

        if (string.IsNullOrEmpty(_code))
        {
            Debug.LogError("Room code is empty", this);
            roomError.text = "You have to put a room code";
            roomError.gameObject.SetActive(true);

            //TODO : MARINE : use the error label to explain to the user that they have forget to put a room code
            return;
        }

        currentPlayer = new Player(_name);

        //check if the room exists, if not display an error message
        CheckIfRoomExists(_code, room =>
        {
            if (room == null)
            {
                Debug.LogError("The room doesn't exists");
                roomError.text = "Error: the room doesn't exists";
                roomError.gameObject.SetActive(true);
            }
            else
            {
                myOnlineRoom = realtimeDB.Child("rooms").Child(_code);
                //if room exists, join it
                JoinRoom(() =>
                {
                    //then subscribe to it
                    myOnlineRoom.ValueChanged += OnRoomUpdate;
                    currentState = GameState.WaitingForOtherPlayersToJoin;
                    players.Add(currentPlayer);

                    WaitingRoom.SetActive(true);
                    HomeConnection.SetActive(false);

                    List<Player> list = new List<Player>();
                    list.Add(currentPlayer);

                    UpdateDisplayedListUser(list) ;
                });
            }
        });
    }

    private void CheckIfRoomExists(string _roomCode, Action<Room> callback_Room)
    {
        realtimeDB.Child("rooms").Child(_roomCode).GetValueAsync().ContinueWithOnMainThread(task =>
        {
            if (task.IsFaulted)
            {
                Debug.LogException(task.Exception);
            }
            else if (task.IsCompleted)
            {
                DataSnapshot snapshot = task.Result;
                if (snapshot == null)
                {
                    callback_Room?.Invoke(null);
                }
                else
                {
                    callback_Room?.Invoke(JsonUtility.FromJson<Room>(snapshot.GetRawJsonValue()));
                }
            }
        });

    }

    /// <summary>
    /// Add this player to the room
    /// </summary>
    private void JoinRoom(Action callback_OnRoomJoined)
    {
        string JSON = JsonUtility.ToJson(currentPlayer);
        Debug.Log(JSON);
        try
        {
            myOnlineRoom.Child("players").Child(currentPlayer.id).SetRawJsonValueAsync(JSON).ContinueWithOnMainThread(task =>
            {
                if (task.IsFaulted)
                {
                    Debug.LogException(task.Exception);
                }
                else
                {
                    Debug.Log($"{currentPlayer.name} has been added to the room", this);
                    callback_OnRoomJoined?.Invoke();
                }
            });
        }
        catch (Exception ex)
        {
            Debug.LogException(ex);
        }
    }

    /// <summary>
    /// Call this only by the first player
    /// </summary>
    public void StartGame()
    {
        // send Start Game
        myRoom.setPlayersAreReady(1);
        string JSON = JsonUtility.ToJson(myRoom);
        Debug.Log(JSON);
        try
        {
            myOnlineRoom.Child("currentState").SetValueAsync(2).ContinueWithOnMainThread(task =>
            {
                if (task.IsFaulted)
                {
                    Debug.LogException(task.Exception);
                }
                else
                {
                    Debug.Log($"start the game", this);
                    currentState = GameState.Explanation;
                    WaitingRoom.SetActive(false);
                    BeforeStart.SetActive(true);
                }
            });
        }
        catch (Exception ex)
        {
            Debug.LogException(ex);
        }

        
    }

    /// <summary>
    /// Dislay on the screen the current prompt ask to you and another player
    /// Also show all the camera stuff
    /// </summary>
    /// <param name="_prompt">The prompt to display</param>
    public void MakeAProposition(Prompt _prompt)
    {
        currentState = GameState.MakeProposition;
    }

    /// <summary>
    /// Create a proposition from the picture taken and send it to server.
    /// Then go to next proposition (if any) or the page "WaitingForOtherPlayers"
    /// </summary>
    public void SubmitProposition()
    {
        Proposition temp = new Proposition();
    }

    /// <summary>
    /// Display the voting page
    /// </summary>
    public void StartToVote()
    {

    }

    /// <summary>
    /// Choose one result and send it to the server
    /// Then go to the next vote (if any) or to the page "WaitingForOtherPlayers"
    /// </summary>
    public void Vote()
    {

    }

    /// <summary>
    /// Display the UI of the end screen
    /// </summary>
    public void DisplayEndScreen()
    {

    }

    /// <summary>
    /// Automatically called when something change in your room
    /// </summary>
    private void OnRoomUpdate(object sender, ValueChangedEventArgs e)
    {
        try
        {
            myRoom = JsonConvert.DeserializeObject<Room>(e.Snapshot.GetRawJsonValue());
        }
        catch (Exception ex)
        {
            Debug.LogException(ex);
        }

        switch (currentState)
        {
            case GameState.WaitingForOtherPlayersToJoin:
                {
                    CheckIfIAmTheFirst(myRoom.GetPlayerList());
                    UpdateDisplayedListUser(myRoom.GetPlayerList());
                    break;
                }
        }
    }


    private void UpdateDisplayedListUser(List<Player> players)
    {
        listPlayersUI.text = string.Empty;
        for (int i = 0; i < players.Count; i++)
        {
            listPlayersUI.text += "\n" + players[i].name;
        }
    }
    
    private void CheckIfIAmTheFirst(List<Player> players)
    {
        if (players.Count > 1)
        {

        }
        else
        {
            
        }
    }


    public void OnClickSubmitSignIn()
    {
        string playerName = playerNameField.text;
        string roomCode = roomCodeField.text;
        PlayerValidateNameAndServerRoom(playerName, roomCode);
    }
}

public enum GameState
{
    EnteringName = 0,
    WaitingForOtherPlayersToJoin = 1,
    Explanation = 2,
    MakeProposition = 3,
    PropositionsSent = 4,
    MakeVote = 5,
    VoteSent = 6,
    Score = 7,
    Ending = 8
}


