using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TwitchLib.Client.Models;
using TwitchLib.Unity;
using TwitchLib.PubSub;
using System.IO;
using TMPro;

//using this version of dll https://github.com/TwitchLib/TwitchLib.Unity/releases/tag/1.0.0
//read this tutorial kinda https://docs.google.com/document/d/1GfYC3BGW2gnS7GmNE1TwMEdk0QYY2zHccxXp53-WiKM/edit

public static class Commands {
    public const string Join = "join";
    public const string Leave = "leave";
    public const string Build = "build";
    public const string Upgrade = "upgrade";
    public const string Gamble = "gamble";
    public const string Gift = "gift";
}

public class TwitchClient : MonoBehaviour {
    public Game Game;

    public Client client;
    public static readonly int MAX_ERRORS = 10;
    public Queue<string> ErrorMessages;
    public GameObject errorTextPrefab;
    public Transform errorTextParent;

    // Start is called before the first frame update
    void Start() {
        try {
            if (File.Exists(Login.SECRET_FILENAME)) {
                using StreamReader reader = new StreamReader(Login.SECRET_FILENAME); // looks for local folder
                Login.Username = reader.ReadLine().Trim(); // line 1 is channel name
                Login.Token = reader.ReadLine().Trim(); // line 2 is access token
            }
            ErrorMessages = new Queue<string>(MAX_ERRORS);
            ConnectionCredentials cc = new ConnectionCredentials(Login.Username, Login.Token);
            client = new Client();
            client.Initialize(cc, Login.Username);
            Game.OnError += Game_OnError;
            client.OnChatCommandReceived += Client_OnChatCommandReceived;
            client.OnUserLeft += Client_OnUserLeft;
            client.OnConnected += Client_OnConnected;
            client.OnIncorrectLogin += Client_OnIncorrectLogin;
            client.Connect();
        } catch {
            AddErrorMessage("Invalid Login");
        }
    }

    private void Client_OnIncorrectLogin(object sender, TwitchLib.Client.Events.OnIncorrectLoginArgs e) {
        AddErrorMessage("Invalid Login.");
    }

    private void Game_OnError(Game game, string username, Game.Error error, string msg) {
        AddErrorMessage($"{username}: {error}{(string.IsNullOrEmpty(msg) ? "" : $", {msg}")}");
        LogErrorMessages();
    }

    private void AddErrorMessage(string message) {
        if (ErrorMessages.Count == MAX_ERRORS) {
            ErrorMessages.Dequeue();
            Destroy(errorTextParent.GetChild(1).gameObject);
        }
        var go = Instantiate(errorTextPrefab, Vector3.zero, Quaternion.identity, errorTextParent);
        var txt = go.GetComponent<TextMeshProUGUI>();
        txt.fontSize = 16;
        txt.text = $"<color=#FF2222>{message}</color>";
        ErrorMessages.Enqueue(message);
    }

    private void Client_OnConnected(object sender, TwitchLib.Client.Events.OnConnectedArgs e) {
        Debug.Log("Client Connected.");
        client.SendMessage(client.GetJoinedChannel(Login.Username), "Starting Game...");
    }

    private void Client_OnUserLeft(object sender, TwitchLib.Client.Events.OnUserLeftArgs e) {
        string username = e.Username;
        Debug.Log($"{username} left.");
        Game.SetPlayerActive(username, false);
        //Debug.Log($"Active Players:");
        //foreach (Player player in Game.ActivePlayers) {
        //    Debug.Log(player.Username);
        //}
    }

    private void Client_OnChatCommandReceived(object sender, TwitchLib.Client.Events.OnChatCommandReceivedArgs e) {
        string cmd = e.Command.CommandText.ToLower();
        var args = e.Command.ArgumentsAsList;
        string username = e.Command.ChatMessage.Username;
        Debug.Log($"Cmd Received: {cmd}");
        switch (cmd.ToLower()) {
            case Commands.Join:
                Debug.Log($"{username} joined.");
                if (!Game.AddPlayer(username)) {
                    return;
                }
                Debug.Log($"Active Players:");
                foreach (Player player in Game.ActivePlayers) {
                    Debug.Log(player.Username);
                }
                break;
            case Commands.Leave:
                if (!Game.SetPlayerActive(username, false)) {
                    return;
                }
                Debug.Log($"{username} left.");
                break;
            case Commands.Build:
                if (!Game.Build(username, args)) {
                    return;
                }
                Debug.Log($"{username} built at {args[0]}.");
                break;
            case Commands.Upgrade:
                if (!Game.Upgrade(username, args)) {
                    return;
                }
                Debug.Log($"{username} upgraded at {args[0]}.");
                break;
            case Commands.Gamble:
                if (!Game.Gamble(username, args)) {
                    return;
                }
                Debug.Log($"{username} gambled.");
                break;
            case Commands.Gift:
                if (!Game.Gift(username, args)) {
                    return;
                }
                Debug.Log($"{username} gifted.");
                break;
            default:
                Game.OnError?.Invoke(Game, username, Game.Error.InvalidCommand);
                break;
        }
    }

    // todo switch this to show on screen
    private void LogErrorMessages() {
        foreach (string error in ErrorMessages) {
            Debug.LogError(error);
        }
    }

    // Update is called once per frame
    void Update() {

    }
}
