using System;
using System.Collections;
using System.Collections.Generic;
using Net.Eagle0.Eagle.Common;
using Net.Eagle0.Eagle.Views;
using UnityEngine;
using ProvinceId = System.Int32;

namespace eagle {
    /// <summary>
    /// Manages beast warning effects for provinces.
    /// Spawns circling vulture/crow effects at province centers when BeastsEvent is active.
    /// Uses a dragon-specific effect when the beast is a dragon.
    /// Effect prefabs are loaded from Addressables by address as needed.
    /// </summary>
    public class ProvinceBeastsController : MonoBehaviour {
        private const string EffectAddressPrefix = "Assets/Eagle/Effects/";
        private const string EffectAddressSuffix = ".prefab";

        [Header("References")]
        public RectTransform mapContainer;
        public ProvinceIDLoader provinceIDLoader;

        [Header("Spawn Offset")]
        [Tooltip(
                "Vertical offset as fraction of map height (positive = above center for circling birds)")]
        public float verticalOffsetFraction = 0.02f;

        private Dictionary<string, GameObject> _effectPrefabs = new();
        private IGameModel _pendingModel;

        private Dictionary<ProvinceId, Vector2> _provinceCentroids = new();
        private Dictionary<ProvinceId, GameObject> _activeEffects = new();
        private Dictionary<ProvinceId, (string name, int count)> _pendingEffectLoads = new();

        void Awake() { provinceIDLoader.OnMapLoaded += LoadCentroids; }

        private void LoadCentroids() {
            var data = JsonUtility.FromJson<CentroidsData>(provinceIDLoader.CentroidsJsonText);
            if (data?.provinces == null) {
                Debug.LogWarning("ProvinceBeastsController: Failed to parse centroids.json");
                return;
            }

            foreach (var province in data.provinces) {
                _provinceCentroids[province.id] =
                        new Vector2(province.centroid_x, province.centroid_y);
            }

            if (_pendingModel != null) {
                UpdateBeastsEffects(_pendingModel);
                _pendingModel = null;
            }
        }

        public void UpdateBeastsEffects(IGameModel model) {
            if (_provinceCentroids.Count == 0 && provinceIDLoader.CentroidsJsonText != null) {
                LoadCentroids();
            }
            if (model?.Provinces == null) { return; }

            if (_provinceCentroids.Count == 0) {
                _pendingModel = model;
                return;
            }

            var provincesWithBeasts = new Dictionary<ProvinceId, (string name, int count)>();
            foreach (var province in model.Provinces.Values) {
                var beastInfo = GetBeastInfo(province);
                if (beastInfo != null) { provincesWithBeasts[province.Id] = beastInfo.Value; }
            }

            var toRemove = new List<ProvinceId>();
            foreach (var kvp in _activeEffects) {
                if (!provincesWithBeasts.ContainsKey(kvp.Key)) {
                    Destroy(kvp.Value);
                    toRemove.Add(kvp.Key);
                }
            }
            foreach (var id in toRemove) { _activeEffects.Remove(id); }

            var pendingToRemove = new List<ProvinceId>();
            foreach (var kvp in _pendingEffectLoads) {
                if (!provincesWithBeasts.TryGetValue(kvp.Key, out var beastInfo) ||
                    kvp.Value.name != beastInfo.name) {
                    pendingToRemove.Add(kvp.Key);
                }
            }
            foreach (var id in pendingToRemove) { _pendingEffectLoads.Remove(id); }

            foreach (var kvp in provincesWithBeasts) {
                if (!_activeEffects.ContainsKey(kvp.Key) &&
                    !_pendingEffectLoads.ContainsKey(kvp.Key)) {
                    QueueSpawnBeastsEffect(kvp.Key, kvp.Value.name, kvp.Value.count);
                }
            }
        }

        private (string name, int count)? GetBeastInfo(ProvinceView province) {
            foreach (var evt in province.KnownEvents) {
                if (evt.SealedValueCase == ProvinceEvent.SealedValueOneofCase.BeastsEvent) {
                    return (evt.BeastsEvent.BeastInfo.SingularName, evt.BeastsEvent.Count);
                }
            }
            return null;
        }

        // Human-type beast names from beasts.tsv — grouped by Polytope archetype
        private static readonly HashSet<string> KnightBeastNames = new(StringComparer.Ordinal) {
            "cultist",
            "heretic",
            "cannibal",
            "terrorist",
            "cossack",
            "desperado",
            "inquisitor",
            "zealot"
        };

        private static readonly HashSet<string> SoldierBeastNames = new(StringComparer.Ordinal) {
            "bandit",
            "brigand",
            "robber",
            "highwayman",
            "marauder",
            "dacoit",
            "freebooter",
            "mobster",
            "smuggler",
            "racketeer",
            "raider",
            "looter",
            "privateer"
        };

        private static readonly HashSet<string> ArcherBeastNames = new(StringComparer.Ordinal) {
            "thief",
            "pirate",
            "buccaneer",
            "poacher",
            "rustler",
            "bushwhacker"
        };

        private static readonly HashSet<string> MilitiaBeastNames = new(StringComparer.Ordinal) {
            "hooligan",
            "street tough",
            "ruffian",
            "scalawag",
            "ne'er-do-well",
            "crook",
            "miscreant",
            "asshole",
            "vandal",
            "goon",
            "scoundrel",
            "lout",
            "delinquent",
            "rapscallion"
        };

        private static readonly HashSet<string> PeasantBeastNames = new(StringComparer.Ordinal) {
            "agitator",
            "instigator",
            "rabble-rouser",
            "separatist",
            "particularist",
            "nihilist",
            "proud boy",
            "rebel",
            "traitor",
            "anarchist",
            "dissident",
            "malcontent",
            "provocateur",
            "demagogue",
            "mutineer",
            "firebrand",
            "deserter",
            "squatter"
        };

        private static readonly HashSet<string> ButcherBeastNames =
                new(StringComparer.Ordinal) { "psychopath", "maniac" };

        private static GameObject LookupPrefab(
                IReadOnlyDictionary<string, GameObject> effectPrefabs,
                string prefabName) {
            effectPrefabs.TryGetValue(prefabName, out var prefab);
            if (prefab == null) {
                Debug.LogWarning(
                        $"Beast effect prefab '{prefabName}' not found in loaded prefabs. " +
                        $"Available prefabs: {string.Join(", ", effectPrefabs.Keys)}");
            }
            return prefab;
        }

        public static string GetEffectPrefabAddress(string beastName) {
            return EffectAddressPrefix + GetEffectPrefabName(beastName) + EffectAddressSuffix;
        }

        /// <summary>
        /// Returns the effect prefab name for a beast type. Beast names are canonical
        /// singular names from beasts.tsv (e.g. "dragon", "wolf", "skeleton").
        /// </summary>
        public static string GetEffectPrefabName(string beastName) {
            switch (beastName) {
                case "dragon": return "DragonEffect";
                case "skeleton": return "SkeletonEffect";
                case "zombie": return "ZombieEffect";
                case "rat": return "RatEffect";
                case "spider": return "SpiderEffect";
                case "demon": return "DemonEffect";
                case "orc": return "OrcEffect";
                case "vampire": return "VampireEffect";
                case "ogre":
                case "troll": return "OgreEffect";
                case "clown": return "ClownEffect";
                case "giant": return "GiantEffect";
                case "tiger": return "TigerEffect";
                case "bear": return "BearEffect";
                case "boar": return "BoarEffect";
                case "crab": return "CrabEffect";
                case "crocodile": return "CrocodileEffect";
                case "frog": return "FrogEffect";
                case "rabbit": return "RabbitEffect";
                case "salamander": return "SalamanderEffect";
                case "scorpion": return "ScorpionEffect";
                case "snake": return "SnakeEffect";
                case "stag": return "StagEffect";
                case "wild goat": return "WildGoatEffect";
                case "wild pig": return "WildPigEffect";
                case "wolf":
                case "wolverine": return "WolfEffect";
                case "honey badger": return "HoneyBadgerEffect";
                case "raccoon": return "RaccoonEffect";
                case "elephant":
                case "mammoth": return "ElephantEffect";
                case "hippopotamus": return "HippopotamusEffect";
                case "lion": return "LionEffect";
                case "velociraptor": return "VelociraptorEffect";
                case "black panther": return "BlackPantherEffect";
                case "rhinoceros": return "RhinocerosEffect";
                case "gorilla": return "GorillaEffect";
                case "hyena": return "HyenaEffect";
                case "leopard": return "LeopardEffect";
                case "warthog": return "WarthogEffect";
                case "emu": return "EmuEffect";
                case "tasmanian devil": return "TasmanianDevilEffect";
                case "kangaroo": return "KangarooEffect";
                case "hippogryph": return "HippogryphEffect";
                default:
                    if (KnightBeastNames.Contains(beastName)) return "KnightEffect";
                    if (SoldierBeastNames.Contains(beastName)) return "SoldierEffect";
                    if (ArcherBeastNames.Contains(beastName)) return "ArcherEffect";
                    if (MilitiaBeastNames.Contains(beastName)) return "MilitiaEffect";
                    if (PeasantBeastNames.Contains(beastName)) return "PeasantEffect";
                    if (ButcherBeastNames.Contains(beastName)) return "ButcherEffect";
                    return "BeastsEffect";
            }
        }

        /// <summary>
        /// Returns the effect prefab for a beast type. Beast names are canonical
        /// singular names from beasts.tsv (e.g. "dragon", "wolf", "skeleton").
        /// </summary>
        public static GameObject GetEffectPrefab(
                string beastName,
                IReadOnlyDictionary<string, GameObject> effectPrefabs) {
            return LookupPrefab(effectPrefabs, GetEffectPrefabName(beastName));
        }

        private void QueueSpawnBeastsEffect(
                ProvinceId provinceId,
                string beastName,
                int beastCount = int.MaxValue) {
            var prefabName = GetEffectPrefabName(beastName);
            if (_effectPrefabs.TryGetValue(prefabName, out var cachedPrefab) &&
                cachedPrefab != null) {
                SpawnBeastsEffect(provinceId, beastName, beastCount, cachedPrefab);
                return;
            }

            _pendingEffectLoads[provinceId] = (beastName, beastCount);
            StartCoroutine(LoadAndSpawnBeastsEffect(provinceId, beastName, beastCount));
        }

        private IEnumerator
        LoadAndSpawnBeastsEffect(ProvinceId provinceId, string beastName, int beastCount) {
            var prefabName = GetEffectPrefabName(beastName);
            GameObject loadedPrefab = null;
            yield return AddressableLoader.LoadAssetWithRetry<GameObject>(
                    GetEffectPrefabAddress(beastName),
                    prefab => {
                        loadedPrefab = prefab;
                        if (prefab != null) { _effectPrefabs[prefabName] = prefab; }
                    },
                    () => { _pendingEffectLoads.Remove(provinceId); });

            if (loadedPrefab == null) { yield break; }
            if (!_pendingEffectLoads.TryGetValue(provinceId, out var pending) ||
                pending.name != beastName) {
                yield break;
            }

            _pendingEffectLoads.Remove(provinceId);
            SpawnBeastsEffect(provinceId, beastName, beastCount, loadedPrefab);
        }

        private void
        SpawnBeastsEffect(ProvinceId provinceId, string beastName, int beastCount = int.MaxValue) {
            SpawnBeastsEffect(
                    provinceId,
                    beastName,
                    beastCount,
                    GetEffectPrefab(beastName, _effectPrefabs));
        }

        private void SpawnBeastsEffect(
                ProvinceId provinceId,
                string beastName,
                int beastCount,
                GameObject prefab) {
            if (mapContainer == null) {
                Debug.LogWarning("SpawnBeastsEffect: mapContainer is null");
                return;
            }
            if (!_provinceCentroids.TryGetValue(provinceId, out Vector2 centroid)) {
                Debug.LogWarning($"SpawnBeastsEffect: No centroid found for province {provinceId}");
                return;
            }

            if (prefab == null) {
                Debug.LogWarning(
                        $"SpawnBeastsEffect: GetEffectPrefab returned null for '{beastName}'");
                return;
            }
            var effect = Instantiate(prefab, mapContainer);
            effect.name = $"Beasts_Province{provinceId}";

            // Cap the number of drawn creatures at the actual beast count from the event.
            // Effect classes defer initialization to Start(), so setting fields here takes
            // effect before any creatures are spawned.
            var beastsEffect = effect.GetComponent<BeastsEffect>();
            if (beastsEffect != null) {
                beastsEffect.birdCount = Mathf.Min(beastsEffect.birdCount, beastCount);
            }
            var monsterEffect = effect.GetComponent<MonsterEffect>();
            if (monsterEffect != null) {
                monsterEffect.monsterCount = Math.Min(monsterEffect.monsterCount, beastCount);
            }
            var animalEffect = effect.GetComponent<AnimalEffect>();
            if (animalEffect != null) {
                animalEffect.animalCount = Math.Min(animalEffect.animalCount, beastCount);
            }

            var rectTransform = effect.GetComponent<RectTransform>();
            if (rectTransform != null) {
                var normalizedX = centroid.x / provinceIDLoader.mapWidth;
                var normalizedY = centroid.y / provinceIDLoader.mapHeight;

                Canvas.ForceUpdateCanvases();
                var containerSize = mapContainer.rect.size;

                if (containerSize.x < 1f || containerSize.y < 1f) {
                    containerSize =
                            new Vector2(provinceIDLoader.mapWidth, provinceIDLoader.mapHeight);
                }

                rectTransform.anchorMin = Vector2.zero;
                rectTransform.anchorMax = Vector2.zero;
                rectTransform.pivot = new Vector2(0.5f, 0.5f);
                // Spawn slightly above center for circling vultures/crows
                var yOffset = verticalOffsetFraction * containerSize.y;
                rectTransform.anchoredPosition = new Vector2(
                        normalizedX * containerSize.x,
                        normalizedY * containerSize.y + yOffset);
            }

            _activeEffects[provinceId] = effect;
        }

        [ContextMenu("Test Dragon in Motcia (ID 2)")]
        public void TestDragon() { SpawnBeastsEffect(2, "dragon"); }

        [ContextMenu("Test Skeleton in Berkorszag (ID 4)")]
        public void TestSkeleton() { SpawnBeastsEffect(4, "skeleton"); }

        [ContextMenu("Test Zombie in Yuetia (ID 37)")]
        public void TestZombie() { SpawnBeastsEffect(37, "zombie"); }

        [ContextMenu("Test Rat in Parnia (ID 41)")]
        public void TestRat() { SpawnBeastsEffect(41, "rat"); }

        [ContextMenu("Test Spider in Soria (ID 6)")]
        public void TestSpider() { SpawnBeastsEffect(6, "spider"); }

        [ContextMenu("Test Demon in Al Raala (ID 13)")]
        public void TestDemon() { SpawnBeastsEffect(13, "demon"); }

        [ContextMenu("Test Orc in Mesh (ID 29)")]
        public void TestOrc() { SpawnBeastsEffect(29, "orc"); }

        [ContextMenu("Test Vampire in Chapellia (ID 27)")]
        public void TestVampire() { SpawnBeastsEffect(27, "vampire"); }

        [ContextMenu("Test Knight (Cultist) in Laufarvia (ID 3)")]
        public void TestKnight() { SpawnBeastsEffect(3, "cultist"); }

        [ContextMenu("Test Soldier (Bandit) in Vovimaria (ID 10)")]
        public void TestSoldier() { SpawnBeastsEffect(10, "bandit"); }

        [ContextMenu("Test Archer (Thief) in Soria (ID 6)")]
        public void TestArcher() { SpawnBeastsEffect(6, "thief"); }

        [ContextMenu("Test Militia (Hooligan) in Bolve (ID 9)")]
        public void TestMilitia() { SpawnBeastsEffect(9, "hooligan"); }

        [ContextMenu("Test Peasant (Agitator) in Oryslia (ID 7)")]
        public void TestPeasant() { SpawnBeastsEffect(7, "agitator"); }

        [ContextMenu("Test Bear in Kezonoria (ID 28)")]
        public void TestBear() { SpawnBeastsEffect(28, "bear"); }

        [ContextMenu("Test Boar in Pemeria (ID 36)")]
        public void TestBoar() { SpawnBeastsEffect(36, "boar"); }

        [ContextMenu("Test Crocodile in Kojaria (ID 39)")]
        public void TestCrocodile() { SpawnBeastsEffect(39, "crocodile"); }

        [ContextMenu("Test Snake in Chia (ID 43)")]
        public void TestSnake() { SpawnBeastsEffect(43, "snake"); }

        [ContextMenu("Test Wolf in Alah (ID 18)")]
        public void TestWolf() { SpawnBeastsEffect(18, "wolf"); }

        [ContextMenu("Test Crab in Musland (ID 5)")]
        public void TestCrab() { SpawnBeastsEffect(5, "crab"); }

        [ContextMenu("Test Frog in Oryslia (ID 7)")]
        public void TestFrog() { SpawnBeastsEffect(7, "frog"); }

        [ContextMenu("Test Rabbit in Atfordia (ID 8)")]
        public void TestRabbit() { SpawnBeastsEffect(8, "rabbit"); }

        [ContextMenu("Test Salamander in Bolve (ID 9)")]
        public void TestSalamander() { SpawnBeastsEffect(9, "salamander"); }

        [ContextMenu("Test Scorpion in Vovimaria (ID 10)")]
        public void TestScorpion() { SpawnBeastsEffect(10, "scorpion"); }

        [ContextMenu("Test Stag in Garholtia (ID 11)")]
        public void TestStag() { SpawnBeastsEffect(11, "stag"); }

        [ContextMenu("Test Wild Goat in Pozia (ID 12)")]
        public void TestWildGoat() { SpawnBeastsEffect(12, "wild goat"); }

        [ContextMenu("Test Wild Pig in Onmaa (ID 14)")]
        public void TestWildPig() { SpawnBeastsEffect(14, "wild pig"); }

        [ContextMenu("Test Ogre in Pieksa (ID 15)")]
        public void TestOgre() { SpawnBeastsEffect(15, "ogre"); }

        [ContextMenu("Test Troll in Oscasland (ID 16)")]
        public void TestTroll() { SpawnBeastsEffect(16, "troll"); }

        [ContextMenu("Test Clown in Hofolen (ID 17)")]
        public void TestClown() { SpawnBeastsEffect(17, "clown"); }

        [ContextMenu("Test Honey Badger in Shumal (ID 1)")]
        public void TestHoneyBadger() { SpawnBeastsEffect(1, "honey badger"); }

        [ContextMenu("Test Raccoon in Hella (ID 19)")]
        public void TestRaccoon() { SpawnBeastsEffect(19, "raccoon"); }

        [ContextMenu("Test Giant in Elekes (ID 20)")]
        public void TestGiant() { SpawnBeastsEffect(20, "giant"); }

        [ContextMenu("Test Tiger in Sigkarl (ID 21)")]
        public void TestTiger() { SpawnBeastsEffect(21, "tiger"); }

        [ContextMenu("Test Elephant in Hella (ID 19)")]
        public void TestElephant() { SpawnBeastsEffect(19, "elephant"); }

        [ContextMenu("Test Mammoth in Elekes (ID 20)")]
        public void TestMammoth() { SpawnBeastsEffect(20, "mammoth"); }

        [ContextMenu("Test Hippopotamus in Kezonoria (ID 28)")]
        public void TestHippopotamus() { SpawnBeastsEffect(28, "hippopotamus"); }

        [ContextMenu("Test Lion in Sigkarl (ID 21)")]
        public void TestLion() { SpawnBeastsEffect(21, "lion"); }

        [ContextMenu("Test Velociraptor in Elekes (ID 20)")]
        public void TestVelociraptor() { SpawnBeastsEffect(20, "velociraptor"); }

        [ContextMenu("Test Black Panther in Laufarvia (ID 3)")]
        public void TestBlackPanther() { SpawnBeastsEffect(3, "black panther"); }

        [ContextMenu("Test Rhinoceros in Vovimaria (ID 10)")]
        public void TestRhinoceros() { SpawnBeastsEffect(10, "rhinoceros"); }

        [ContextMenu("Test Gorilla in Soria (ID 6)")]
        public void TestGorilla() { SpawnBeastsEffect(6, "gorilla"); }

        [ContextMenu("Test Hyena in Bolve (ID 9)")]
        public void TestHyena() { SpawnBeastsEffect(9, "hyena"); }

        [ContextMenu("Test Leopard in Oryslia (ID 7)")]
        public void TestLeopard() { SpawnBeastsEffect(7, "leopard"); }

        [ContextMenu("Test Warthog in Atfordia (ID 8)")]
        public void TestWarthog() { SpawnBeastsEffect(8, "warthog"); }

        [ContextMenu("Test Emu in Pozia (ID 12)")]
        public void TestEmu() { SpawnBeastsEffect(12, "emu"); }

        [ContextMenu("Test Tasmanian Devil in Onmaa (ID 14)")]
        public void TestTasmanianDevil() { SpawnBeastsEffect(14, "tasmanian devil"); }

        [ContextMenu("Test Kangaroo in Pieksa (ID 15)")]
        public void TestKangaroo() { SpawnBeastsEffect(15, "kangaroo"); }

        [ContextMenu("Test Hippogryph in Shumal (ID 1)")]
        public void TestHippogryph() { SpawnBeastsEffect(1, "hippogryph"); }

        [ContextMenu("Test All Beast Types")]
        public void TestAllBeastTypes() {
            TestDragon();
            TestSkeleton();
            TestZombie();
            TestRat();
            TestSpider();
            TestDemon();
            TestOrc();
            TestVampire();
            TestKnight();
            TestSoldier();
            TestArcher();
            TestMilitia();
            TestPeasant();
            TestBear();
            TestBoar();
            TestCrocodile();
            TestSnake();
            TestWolf();
            TestCrab();
            TestFrog();
            TestRabbit();
            TestSalamander();
            TestScorpion();
            TestStag();
            TestWildGoat();
            TestWildPig();
            TestOgre();
            TestTroll();
            TestClown();
            TestHoneyBadger();
            TestRaccoon();
            TestGiant();
            TestTiger();
            TestElephant();
            TestMammoth();
            TestHippopotamus();
            TestLion();
            TestVelociraptor();
            TestBlackPanther();
            TestRhinoceros();
            TestGorilla();
            TestHyena();
            TestLeopard();
            TestWarthog();
            TestEmu();
            TestTasmanianDevil();
            TestKangaroo();
            TestHippogryph();
        }

        [ContextMenu("Clear All Effects")]
        public void ClearAllEffects() {
            foreach (var effect in _activeEffects.Values) {
                if (effect != null) { Destroy(effect); }
            }
            _activeEffects.Clear();
        }

        void OnDestroy() {
            foreach (var effect in _activeEffects.Values) {
                if (effect != null) { Destroy(effect); }
            }
            _activeEffects.Clear();
        }

        [Serializable]
        private class CentroidsData {
            public ProvinceCentroid[] provinces;
        }

        [Serializable]
        private class ProvinceCentroid {
            public int id;
            public string name;
            public float centroid_x;
            public float centroid_y;
        }
    }
}
