using System;
using System.Collections.Generic;
using System.Linq;
using common.GUIUtils;
using NUnit.Framework;
using Shardok;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using UnityEngine.SceneManagement;

namespace eagle0.Tests {
    [TestFixture]
    public class SceneSplitValidationTests {
        private const string MainScenePath = "Assets/Scenes/Main.unity";
        private const string ConnectionScenePath = "Assets/Scenes/Connection.unity";
        private const string EagleScenePath = "Assets/Scenes/Eagle.unity";
        private const string ShardokScenePath = "Assets/Scenes/Shardok.unity";
        private const string SharedScenePath = "Assets/Scenes/Shared.unity";

        [Test]
        public void BuildSettings_UseSplitScenesOnly() {
            var enabledScenes = EditorBuildSettings.scenes.Where(scene => scene.enabled)
                                        .Select(scene => scene.path)
                                        .ToList();

            CollectionAssert.AreEqual(
                    new[] {
                        MainScenePath,
                        ConnectionScenePath,
                        EagleScenePath,
                        ShardokScenePath,
                        SharedScenePath,
                    },
                    enabledScenes);

            CollectionAssert.DoesNotContain(enabledScenes, "Assets/Gameplay.unity");
        }

        [Test]
        public void SplitSceneAssetsExist() {
            foreach (var scenePath in SplitScenePaths()) {
                Assert.NotNull(
                        AssetDatabase.LoadAssetAtPath<SceneAsset>(scenePath),
                        $"{scenePath} must exist.");
            }

            Assert.IsFalse(
                    AssetDatabase.IsValidFolder("Assets/Gameplay"),
                    "Retired Gameplay scene assets should not remain in Assets/Gameplay.");
        }

        [Test]
        public void MainSceneContainsBootstrapInfrastructure() {
            var scene = OpenScene(MainScenePath);

            AssertHasComponent<BootstrapManager>(scene);
            AssertHasComponent<GameSceneManager>(scene);
        }

        [Test]
        public void ConnectionSceneContainsConnectionController() {
            var scene = OpenScene(ConnectionScenePath);
            var connectionRoot = AssertRoot(scene, "ConnectionCanvas");

            Assert.NotNull(
                    connectionRoot.GetComponent<ConnectionHandler>(),
                    "ConnectionCanvas must contain ConnectionHandler.");
        }

        [Test]
        public void EagleSceneContainsEagleController() {
            var scene = OpenScene(EagleScenePath);
            var eagleRoot = AssertRoot(scene, "Eagle Canvas");

            Assert.NotNull(
                    eagleRoot.GetComponent<eagle.EagleGameController>(),
                    "Eagle Canvas must contain EagleGameController.");
        }

        [Test]
        public void ShardokSceneContainsShardokController() {
            var scene = OpenScene(ShardokScenePath);
            var shardokRoot = AssertRoot(scene, "Shardok Container");

            Assert.NotNull(
                    shardokRoot.GetComponent<ShardokGameController>(),
                    "Shardok Container must contain ShardokGameController.");
        }

        [Test]
        public void SharedSceneContainsSharedUiInfrastructure() {
            var scene = OpenScene(SharedScenePath);

            AssertHasComponent<SettingsPanelController>(scene);
            AssertHasComponent<ErrorHandler>(scene);
        }

        private static IEnumerable<string> SplitScenePaths() {
            yield return MainScenePath;
            yield return ConnectionScenePath;
            yield return EagleScenePath;
            yield return ShardokScenePath;
            yield return SharedScenePath;
        }

        private static Scene OpenScene(string scenePath) {
            return EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Single);
        }

        private static GameObject AssertRoot(Scene scene, string rootName) {
            var root = scene.GetRootGameObjects().FirstOrDefault(obj => obj.name == rootName);
            Assert.NotNull(root, $"{scene.path} must contain root object '{rootName}'.");
            return root;
        }

        private static void AssertHasComponent<T>(Scene scene)
                where T : Component {
            var component = Resources.FindObjectsOfTypeAll<T>().FirstOrDefault(
                    item => item.gameObject.scene == scene);
            Assert.NotNull(component, $"{scene.path} must contain {typeof(T).Name}.");
        }
    }
}
