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

namespace eagle {
    /// <summary>
    /// Manages flood particle effects for provinces.
    /// Spawns rain/water splash effects at province centers when FloodEvent is active.
    /// Uses province masking to clip particles to province boundaries.
    /// </summary>
    public class ProvinceFloodController : MonoBehaviour {
        [Header("References")]
        public RectTransform mapContainer;
        public GameObject floodEffectPrefab;
        public ProvinceIDLoader provinceIDLoader;

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

        private Dictionary<ProvinceId, Vector2> _provinceCentroids = new();
        private Dictionary<ProvinceId, float> _provinceWidths = new();
        private Dictionary<ProvinceId, GameObject> _activeEffects = new();

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

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

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

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

            var provincesWithFlood = new HashSet<ProvinceId>();
            foreach (var province in model.Provinces.Values) {
                if (HasFloodEvent(province)) { provincesWithFlood.Add(province.Id); }
            }

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

            foreach (var provinceId in provincesWithFlood) {
                if (!_activeEffects.ContainsKey(provinceId)) { SpawnFloodEffect(provinceId); }
            }
        }

        private bool HasFloodEvent(ProvinceView province) {
            foreach (var evt in province.KnownEvents) {
                if (evt.SealedValueCase == ProvinceEvent.SealedValueOneofCase.FloodEvent) {
                    return true;
                }
            }
            return false;
        }

        private void SpawnFloodEffect(ProvinceId provinceId) {
            if (floodEffectPrefab == null || mapContainer == null) { return; }
            if (!_provinceCentroids.TryGetValue(provinceId, out Vector2 centroid)) { return; }

            int mapWidth = provinceIDLoader.mapWidth;
            int mapHeight = provinceIDLoader.mapHeight;

            var effect = Instantiate(floodEffectPrefab, mapContainer);
            effect.name = $"Flood_Province{provinceId}";

            Canvas.ForceUpdateCanvases();
            var containerSize = mapContainer.rect.size;
            if (containerSize.x < 1f || containerSize.y < 1f) {
                containerSize = new Vector2(mapWidth, mapHeight);
            }

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

                rectTransform.anchorMin = Vector2.zero;
                rectTransform.anchorMax = Vector2.zero;
                rectTransform.pivot = new Vector2(0.5f, 0.5f);
                var yOffset = verticalOffsetFraction * containerSize.y;
                rectTransform.anchoredPosition = new Vector2(
                        normalizedX * containerSize.x,
                        normalizedY * containerSize.y + yOffset);
            }

            // Scale particle emission area based on province width - shader masks to boundaries
            if (_provinceWidths.TryGetValue(provinceId, out float width)) {
                var scaleFactor = containerSize.x / mapWidth;
                // Use full province width since shader clips to boundaries
                var emissionWidth = width * scaleFactor;
                var particleSystems = effect.GetComponentsInChildren<ParticleSystem>();
                foreach (var ps in particleSystems) {
                    if (ps.gameObject.name == "RainEffect") {
                        var shape = ps.shape;
                        shape.scale = new Vector3(emissionWidth, shape.scale.y, shape.scale.z);
                    } else if (ps.gameObject.name == "SplashEffect") {
                        var shape = ps.shape;
                        shape.radius = emissionWidth * 0.5f;  // Half width for splash radius
                    }
                }
            }

            // Configure province masking
            var floodEffect = effect.GetComponent<FloodEffect>();
            if (floodEffect != null) {
                floodEffect.SetProvinceId(provinceId);
                floodEffect.SetMapContainer(mapContainer);

                if (provinceIDLoader.ProvinceIDTexture != null) {
                    floodEffect.SetProvinceIdMap(provinceIDLoader.ProvinceIDTexture);
                }
            }

            _activeEffects[provinceId] = effect;
        }

        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;
            public float perpendicular_width;
        }
    }
}
