using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Cysharp.Threading.Tasks;
using RebootKit.Engine.Foundation;
using RebootKit.Engine.Services.Input;
using UnityEngine.InputSystem;
using Logger = RebootKit.Engine.Foundation.Logger;

namespace RebootKit.Engine.Services.Console {
    public interface IConsoleCommand {
        string Name { get; }
        string Description { get; }
        void Execute(string[] args);
    }

    public class HelpCommand : IConsoleCommand {
        public string Name { get; } = "help";
        public string Description { get; } = "Prints available commands/cvars and their descriptions.";

        public void Execute(string[] args) {
            RR.Console().PrintHelp();
        }
    }

    public class ConsoleService : IService {
        private static readonly Logger _logger = new(nameof(ConsoleService));

        [Serializable]
        public class Config {
            public ConsoleUI ConsoleUIPrefab;
            public ScriptableInputAction ToggleAction;
        }

        private ConsoleUI _ui;
        private Config _config;

        private List<IConsoleCommand> _commands = new();
        private List<CVar> _cvars = new();

        public bool IsVisible => _ui.IsVisible;

        public ConsoleService(Config config) {
            _config = config;
        }

        public async UniTask OnInit(CancellationToken cancellationToken) {
            _logger.Info("Waking up");

            _ui = UnityEngine.Object.Instantiate(_config.ConsoleUIPrefab);
            UnityEngine.Object.DontDestroyOnLoad(_ui.gameObject);
            await UniTask.Yield(cancellationToken);

            _config.ToggleAction.Action.Enable();
            _config.ToggleAction.Action.performed += OnToggleAction;

            RegisterCommand(new HelpCommand());

            _ui.SetVisibility(false);
            _ui.Clear();
            _ui.Write("Hello shelf\n");
        }

        public void Dispose() {
            if (_ui != null) {
                UnityEngine.Object.Destroy(_ui);
                _ui = null;
            }

            _config.ToggleAction.Action.performed -= OnToggleAction;
        }

        public bool CVarExists(string name) {
            foreach (CVar cvar in _cvars) {
                if (cvar.Name.Equals(name)) {
                    return true;
                }
            }

            return false;
        }

        public CVar GetCVar(string name) {
            foreach (CVar cvar in _cvars) {
                if (cvar.Name.Equals(name)) {
                    return cvar;
                }
            }

            return null;
        }

        public CVar ReplaceOrDefault(CVar cvar) {
            CVar foundCVar = GetCVar(cvar.Name);
            if (foundCVar != null) {
                return foundCVar;
            }

            _cvars.Add(cvar);
            return cvar;
        }

        public void Replace(CVar cvar) {
            CVar foundCVar = GetCVar(cvar.Name);
            if (foundCVar != null) {
                _cvars.Remove(foundCVar);
            }

            _cvars.Add(cvar);
        }

        private string[] ParseCommandInputArguments(string text) {
            if (text.Length == 0) {
                return Array.Empty<string>();
            }

            return new string[] {text};
        }

        public void Execute(string input) {
            if (input.Length == 0) {
                return;
            }

            string commandName = input;
            if (input.IndexOf(' ') != -1) {
                commandName = input.Substring(0, input.IndexOf(' '));
            }

            string[] arguments = ParseCommandInputArguments(input.Substring(commandName.Length));

            foreach (IConsoleCommand command in _commands) {
                if (command.Name.Equals(commandName)) {
                    command.Execute(arguments);
                    return;
                }
            }

            foreach (CVar cvar in _cvars) {
                if (cvar.Name.Equals(commandName)) {
                    if (arguments.Length == 1) {
                        cvar.ParseFromString(arguments[0]);
                    }

                    _ui.Write($"<b>{cvar.Name}</b> - {cvar.ToString()}\n");
                    return;
                }
            }

            _ui.Write($"ERROR: Command/CVar `{commandName}` not found.");
        }

        public void ToggleVisibility() {
            _ui.SetVisibility(!_ui.IsVisible);

            if (_ui.IsVisible) {
                // RR.Input().DisableControls();
            } else {
                RR.Input().EnableControls();
            }
        }

        public void RegisterCommand(IConsoleCommand command) {
            if (_commands.Any(t => t.Name.Equals(command.Name))) {
                _logger.Error($"`{command.Name}` command is already registered");
                return;
            }

            _commands.Add(command);
            _logger.Info($"Registered command: {command.Name}");
        }

        public void PrintHelp() {
            StringBuilder message = new();

            message.AppendLine("Available commands:");
            foreach (IConsoleCommand command in _commands) {
                message.Append("    ");
                message.Append(command.Name);
                message.Append(" - ");
                message.Append(command.Description);
                message.AppendLine();
            }

            message.AppendLine("Available cvars:");
            foreach (CVar cvar in _cvars) {
                message.Append("    ");
                message.Append(cvar.Name);
                message.Append(" - ");
                message.Append(cvar.Description);
                message.AppendLine();
            }

            _ui.Write(message.ToString());
        }

        private void OnToggleAction(InputAction.CallbackContext obj) {
            ToggleVisibility();
        }
    }
}