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

namespace eagle {
    /// <summary>
    /// Manages battle-in-progress effects on the strategic map.
    /// Spawns looping weapon-clash animations between defender and attacker origin provinces
    /// for the duration of each active battle.
    /// Follows the same pattern as ProvinceFestivalController.
    /// </summary>
    public class BattleInProgressController : MonoBehaviour {
        [Header("References")]
        public RectTransform mapContainer;
        public ProvinceIDLoader provinceIDLoader;
        public AudioSource audioSource;

        [Header("Effect Settings")]
        public Sprite swordSprite;
        public Sprite axeSprite;
        public Sprite arrowSprite;

        [Header("Rotation Offsets (degrees)")]
        [Tooltip("Sword blade points down in sprite, so +90 rotates blade toward opponent")]
        public float swordRotationOffset = 90f;

        [Tooltip("Mace head points up in sprite, so -90 rotates head toward opponent")]
        public float maceRotationOffset = -90f;

        [Header("Audio")]
        [Range(0f, 1f)]
        public float volume = 0.5f;

        private Dictionary<ProvinceId, Vector2> _provinceCentroids = new();
        private Dictionary<(int defenderPid, int originPid), GameObject> _activeEffects = new();
        private readonly Dictionary<string, AudioClip> _loadedClips = new();
        private readonly HashSet<string> _loadingClips = new();

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

        private void PlayClash() { PlaySound("melee"); }

        private void PlayArcheryVolley() { PlaySound("archery"); }

        private void PlaySound(string clipName) {
            if (_loadedClips.TryGetValue(clipName, out var clip) && clip != null) {
                audioSource.PlayOneShot(clip, volume);
                return;
            }
            if (_loadingClips.Contains(clipName)) { return; }

            _loadingClips.Add(clipName);
            StartCoroutine(LoadAndPlaySound(clipName));
        }

        private IEnumerator LoadAndPlaySound(string clipName) {
            AudioClip loadedClip = null;
            yield return AddressableLoader.LoadNamedAssetWithRetry<AudioClip>(
                    "sfx-combat",
                    clipName,
                    clip => {
                        loadedClip = clip;
                        if (clip != null) { _loadedClips[clipName] = clip; }
                    });

            _loadingClips.Remove(clipName);
            if (loadedClip != null) { audioSource.PlayOneShot(loadedClip, volume); }
        }

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

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

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

            var activeKeys = new HashSet<(int, int)>();

            foreach (var battle in model.ShardokBattles) {
                int defenderPid = battle.DefenderProvince;

                foreach (var playerInfo in battle.PlayerInfos) {
                    foreach (var originPid in playerInfo.OriginProvinceIds) {
                        // Skip origin == defender: the midpoint would be the
                        // province center, and this happens when province ownership
                        // changes before the battle is removed from the model.
                        if (originPid == defenderPid) { continue; }

                        var key = (defenderPid, originPid);
                        activeKeys.Add(key);

                        if (!_activeEffects.ContainsKey(key)) {
                            SpawnBattleEffect(defenderPid, originPid);
                        }
                    }
                }
            }

            // Remove effects that are no longer active
            var toRemove = new List<(int, int)>();
            foreach (var kvp in _activeEffects) {
                if (!activeKeys.Contains(kvp.Key)) {
                    Destroy(kvp.Value);
                    toRemove.Add(kvp.Key);
                }
            }
            foreach (var key in toRemove) { _activeEffects.Remove(key); }
        }

        private void SpawnBattleEffect(int defenderPid, int originPid) {
            if (swordSprite == null || mapContainer == null) { return; }
            if (!_provinceCentroids.TryGetValue(defenderPid, out Vector2 defenderCentroid)) {
                return;
            }
            if (!_provinceCentroids.TryGetValue(originPid, out Vector2 originCentroid)) { return; }

            var go = new GameObject($"BattleEffect_{defenderPid}_{originPid}");
            go.transform.SetParent(mapContainer, false);

            var rectTransform = go.AddComponent<RectTransform>();

            // Compute midpoint in normalized map coordinates
            Vector2 midpoint = (defenderCentroid + originCentroid) / 2f;
            float normalizedX = midpoint.x / provinceIDLoader.mapWidth;
            float normalizedY = midpoint.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);
            rectTransform.anchoredPosition =
                    new Vector2(normalizedX * containerSize.x, normalizedY * containerSize.y);

            // Compute axis angle from defender to origin
            Vector2 delta = originCentroid - defenderCentroid;
            float axisAngle = Mathf.Atan2(delta.y, delta.x) * Mathf.Rad2Deg;

            // Compute half distance in container space for arrow volleys
            Vector2 defScreen = new Vector2(
                    defenderCentroid.x / provinceIDLoader.mapWidth * containerSize.x,
                    defenderCentroid.y / provinceIDLoader.mapHeight * containerSize.y);
            Vector2 originScreen = new Vector2(
                    originCentroid.x / provinceIDLoader.mapWidth * containerSize.x,
                    originCentroid.y / provinceIDLoader.mapHeight * containerSize.y);
            float halfDistance = Vector2.Distance(defScreen, originScreen) / 2f;

            var effect = go.AddComponent<BattleInProgressEffect>();
            effect.weapon1Sprite = swordSprite;
            effect.weapon2Sprite = axeSprite != null ? axeSprite : swordSprite;
            effect.weapon1RotationOffset = swordRotationOffset;
            effect.weapon2RotationOffset =
                    axeSprite != null ? maceRotationOffset : swordRotationOffset;
            effect.axisAngle = axisAngle;
            effect.halfDistance = halfDistance;
            effect.arrowSprite = arrowSprite;
            effect.onClash = PlayClash;
            effect.onArcheryVolley = PlayArcheryVolley;

            _activeEffects[(defenderPid, originPid)] = go;
        }

        void OnDestroy() {
            foreach (var go in _activeEffects.Values) {
                if (go != null) { Destroy(go); }
            }
            _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;
        }

#if UNITY_EDITOR
        [ContextMenu("Test: Spawn effect between provinces 2 and 5")]
        private void TestSpawnEffect() {
            if (_provinceCentroids.Count == 0 && provinceIDLoader.CentroidsJsonText != null) {
                LoadCentroids();
            }
            SpawnBattleEffect(2, 5);
        }

        [ContextMenu("Test: Clear all effects")]
        private void TestClearEffects() {
            foreach (var go in _activeEffects.Values) {
                if (go != null) { Destroy(go); }
            }
            _activeEffects.Clear();
        }
#endif
    }
}
