using System;
using System.Threading;
using Cysharp.Threading.Tasks;
using RebootKit.Engine.Foundation;
using RebootKit.Engine.Services.Console;
using RebootKit.Engine.Services.Game;
using RebootKit.Engine.Services.Input;
using RebootKit.Engine.Services.Simulation;
using UnityEngine;
using UnityEngine.SceneManagement;
using Logger = RebootKit.Engine.Foundation.Logger;

namespace RebootKit.Engine {
    public abstract class RAsset : ScriptableObject {
    }
    
    public class RR : IDisposable {
        public static RR Shared { get; set; }

        private static readonly Logger Logger = new("RR");

        private EngineConfigAsset _engineConfigAsset;

        private ConsoleService _consoleService;
        private InputService _inputService;
        private WorldService _worldService;
        private GameService _gameService;

        private DIContext _diContext;

        public void Dispose() {
        }

        public async UniTask Init(CancellationToken cancellationToken) {
            Logger.Info("Waking up");

            Logger.Info("Loading engine config");
            _engineConfigAsset = Resources.Load<EngineConfigAsset>(RConsts.EngineConfigResourcesPath);
            if (_engineConfigAsset == null) {
                Logger.Error($"Couldn't load engine config from resources: {RConsts.EngineConfigResourcesPath}");
                return;
            }

            Logger.Info("Loading engine found");

            _diContext = new DIContext();
            _diContext.AddInjector(new CVarFieldInjector());

            await CreateCoreServices(cancellationToken);
            await RegisterServicesFromMainSceneAsync(cancellationToken);
        }

        public async UniTask Run(CancellationToken cancellationToken) {
            Logger.Info($"Starting initial game mode");
            await _gameService.Start(_engineConfigAsset.StartingGameMode, cancellationToken);
        }

        private async UniTask<TService> CreateServiceAsync<TService>(ServiceAsset<TService> asset, CancellationToken cancellationToken = default) where TService : IService {
            TService service = asset.Create();
            _diContext.Bind<TService>(service);
            _diContext.Inject(service);

            await service.OnInit(cancellationToken);
            return service;
        }

        private async UniTask CreateCoreServices(CancellationToken cancellationToken) {
            Logger.Debug("Registering core services");
            
            _consoleService = await CreateServiceAsync(_engineConfigAsset.ConsoleService, cancellationToken);
            _inputService = await CreateServiceAsync(_engineConfigAsset.InputService, cancellationToken);
            _worldService = await CreateServiceAsync(_engineConfigAsset.WorldService, cancellationToken);
            _gameService = await CreateServiceAsync(_engineConfigAsset.GameService, cancellationToken);
        }

        private async UniTask RegisterServicesFromMainSceneAsync(CancellationToken cancellationToken = default) {
            GameObject[] gameObjects = SceneManager.GetSceneByBuildIndex(RConsts.MainSceneBuildIndex).GetRootGameObjects();
            if (gameObjects.Length == 0) {
                return;
            }

            foreach (GameObject root in gameObjects) {
                SceneDI di = root.GetComponent<SceneDI>();

                if (di == null) {
                    continue;
                }
                
                di.Install(_diContext);
            }

            await UniTask.Yield();
        }

        //
        // API
        //

        /// Services
        public static ConsoleService Console() => Shared._consoleService;
        public static InputService Input() => Shared._inputService;
        public static WorldService World() => Shared._worldService;
        public static GameService Game() => Shared._gameService;

        /// CVars
        public static CVar CVarIndex(string name, int defaultValue = -1) {
            CVar cvar = Console().GetCVar(name);
            if (cvar != null) {
                return cvar;
            }

            cvar = new CVar(name, defaultValue);
            Console().Replace(cvar);
            return cvar;
        }

        public static CVar CVarNumber(string name, double defaultValue = 0) {
            CVar cvar = Console().GetCVar(name);
            if (cvar != null) {
                return cvar;
            }

            cvar = new CVar(name, defaultValue);
            Console().Replace(cvar);
            return cvar;
        }

        public static CVar CVarString(string name, string defaultValue = "") {
            CVar cvar = Console().GetCVar(name);
            if (cvar != null) {
                return cvar;
            }

            cvar = new CVar(name, defaultValue);
            Console().Replace(cvar);
            return cvar;
        }
    }
}