using System;
using UnityEngine;
using GameFramework;
using Architecture.Core;

namespace IndianOceanAssets.Engine2_5D
{
    /// <summary>
    /// 回声系统 v2 —— 描边波纹版。
    /// 按 E 键从玩家位置释放扩散波纹，波纹扫过的精灵（非光源物体）
    /// 会显示外描边（默认白色，可自定义），维持一段时间后整体消散。
    ///
    /// 与 v1 的区别：
    /// - 不再在黑暗遮罩上画光波（那样会漏出地板）
    /// - 改为通过全局 shader 参数控制精灵描边 shader (SpriteEchoOutline)
    /// - 时间模型：扩散 → 维持(sustainTime) → 消散(fadeTime)
    ///
    /// 挂载位置：玩家物体上（或跟随玩家的空物体）
    /// 依赖：场景中需要有用 SpriteEchoOutline shader 的描边精灵
    ///      （由 EchoOutlineManager 自动生成）
    /// </summary>
    public class EchoSystem : MonoBehaviour
    {
        [Header("SO 事件通道（替代 static OnEchoReleased 事件，参数为释放时玩家世界位置）")]
        [SerializeField] private Vector3Event echoReleasedEvent;

        [Header("按键")]
        [SerializeField] private KeyCode echoKey = KeyCode.E;

        [Header("扩散参数")]
        [Tooltip("波纹扩散速度（世界单位/秒）")]
        [SerializeField] private float expandSpeed = 15f;

        [Tooltip("最大扩散半径（到达后进入维持阶段）")]
        [SerializeField] private float maxRadius = 30f;

        [Tooltip("波纹前沿柔和宽度（精灵被扫到时的过渡宽度，越大越柔）")]
        [SerializeField] private float ringWidth = 1.5f;

        [Header("时间模型")]
        [Tooltip("扩散到最大半径后，描边维持的时间（秒）")]
        [SerializeField] private float sustainTime = 4f;

        [Tooltip("维持结束后，描边消散的时间（秒）")]
        [SerializeField] private float fadeTime = 1.5f;

        [Header("描边外观")]
        [Tooltip("描边颜色（默认白色，可自定义）")]
        [SerializeField] private Color outlineColor = Color.white;

        [Header("红圈视觉")]
        [Tooltip("红圈颜色")]
        [SerializeField] private Color ringColor = new Color(1f, 0.15f, 0.1f, 1f);

        [Tooltip("红圈宽度（世界单位）")]
        [SerializeField] private float ringVisualWidth = 2.5f;

        [Tooltip("红圈离地高度（避免Z-fighting）")]
        [SerializeField] private float ringYOffset = 0.1f;

        [Tooltip("红圈渐隐总时间（秒）。从释放瞬间开始线性渐隐，到 0 后隐藏。默认 4 秒")]
        [SerializeField] private float ringFadeTime = 4f;

        [Tooltip("红圈扩散速度（世界单位/秒），独立于波纹扩散速度")]
        [SerializeField] private float ringExpandSpeed = 15f;

        [Header("Shader 引用")]
        [Tooltip("直接拖入 Assets/Light/shaders/EchoRing.shader。\n"
               + "用序列化字段引用而不是 Shader.Find，确保打包时构建管线能静态识别，"
               + "不会被 shader stripping 剔除（否则运行时会 fallback 成粉色错误着色器）。\n"
               + "留空时仍会尝试 Shader.Find 兜底，但需要把该 shader 加入 "
               + "GraphicsSettings 的 Always Included Shaders 列表。")]
        [SerializeField] private Shader ringShader;

        [Header("冷却")]
        [Tooltip("回声冷却时间（秒）")]
        [SerializeField] private float cooldown = 3f;

        // 红圈视觉
        private GameObject _ringVisual;
        private Material _ringMaterial;

        // 全局 shader 属性 ID（通过 Shader.SetGlobalX 设置，所有描边精灵共享）
        private static readonly int EchoCenterID           = Shader.PropertyToID("_EchoCenter");
        private static readonly int EchoRadiusID           = Shader.PropertyToID("_EchoRadius");
        private static readonly int EchoWidthID            = Shader.PropertyToID("_EchoWidth");
        private static readonly int EchoOutlineIntensityID = Shader.PropertyToID("_EchoOutlineIntensity");
        private static readonly int EchoOutlineColorID    = Shader.PropertyToID("_EchoOutlineColor");
        private static readonly int EchoRingAlphaID       = Shader.PropertyToID("_EchoRingAlpha");
        private static readonly int EchoRingRadiusID      = Shader.PropertyToID("_EchoRingRadius");

        private enum State { Idle, Expanding, Sustaining, Fading }

        private State _state = State.Idle;
        private float _radius;
        private float _intensity;
        private float _stateTimer;
        private Vector2 _center;
        private float _lastEchoTime = -999f;
        private float _ringAlpha = 0f;
        private float _ringRadius = 0f;
        private bool _warnedEcho = false; // 防止 echoReleasedEvent 空引用告警刷屏

        private void Start()
        {
            // 初始化：描边关闭
            Shader.SetGlobalFloat(EchoOutlineIntensityID, 0f);
            Shader.SetGlobalColor(EchoOutlineColorID, outlineColor);
            Shader.SetGlobalFloat(EchoWidthID, ringWidth);
            Shader.SetGlobalFloat(EchoRingAlphaID, 0f);

            CreateRingVisual();
        }

        /// <summary>
        /// 创建红圈视觉：一个平铺在地面上的 Quad，使用 EchoRing shader。
        /// shader 通过全局 _EchoCenter / _EchoRadius 自动计算圆环位置，
        /// 无需每帧更新 Mesh —— 只需在释放时移动 Quad 到回声中心即可。
        /// </summary>
        private void CreateRingVisual()
        {
            _ringVisual = GameObject.CreatePrimitive(PrimitiveType.Quad);
            _ringVisual.name = "EchoRing_Visual";

            // 移除不需要的碰撞体
            var col = _ringVisual.GetComponent<Collider>();
            if (col != null) Destroy(col);

            // 平铺在地面：绕 X 轴旋转 90°
            _ringVisual.transform.rotation = Quaternion.Euler(90f, 0f, 0f);

            // 缩放：覆盖 maxRadius * 2 的范围
            float size = maxRadius * 2f;
            _ringVisual.transform.localScale = new Vector3(size, size, 1f);
            // 不挂载到玩家下——回声中心固定，不跟随玩家移动

            // 创建材质（运行时）
            // 优先使用序列化字段引用的 shader（构建管线可静态识别，不会被剔除）；
            // 字段为空时再尝试 Shader.Find 兜底（必须同时把 shader 加入 GraphicsSettings
            // 的 Always Included Shaders，否则打包后 Find 会返回 null → 粉色错误着色器）。
            var shader = ringShader != null ? ringShader : Shader.Find("IndianOcean/EchoRing");
            if (shader != null)
            {
                _ringMaterial = new Material(shader);
                _ringMaterial.SetColor("_RingColor", ringColor);
                _ringMaterial.SetFloat("_RingWidth", ringVisualWidth);
                // 确保渲染在黑暗遮罩之上（黑暗遮罩 sortingOrder=9999, renderQueue=3100）
                _ringMaterial.renderQueue = 3250;
                var renderer = _ringVisual.GetComponent<MeshRenderer>();
                renderer.sortingOrder = 10001;
                renderer.material = _ringMaterial;
            }
            else
            {
                Debug.LogError(
                    "[EchoSystem] 找不到 IndianOcean/EchoRing shader！红圈将不会渲染。\n"
                    + "修复方法（二选一）：\n"
                    + "  1) 把 Assets/Light/shaders/EchoRing.shader 拖到本组件的 Ring Shader 字段；\n"
                    + "  2) Edit → Project Settings → Graphics → Always Included Shaders "
                    + "里添加该 shader，然后重新打包。");
            }

            _ringVisual.SetActive(false);
        }

        private void Update()
        {
            // 触发：CD 好即可释放，无需等状态机结束（StartEcho 会重置所有状态）
            if (Input.GetKeyDown(echoKey) && Time.time > _lastEchoTime + cooldown)
            {
                StartEcho();
            }

            // 状态机
            switch (_state)
            {
                case State.Expanding:  UpdateExpanding();  break;
                case State.Sustaining: UpdateSustaining(); break;
                case State.Fading:     UpdateFading();     break;
            }

            // 每帧推送全局参数
            Shader.SetGlobalVector(EchoCenterID, new Vector4(_center.x, _center.y, 0f, 0f));
            Shader.SetGlobalFloat(EchoRadiusID, _radius);
            Shader.SetGlobalFloat(EchoWidthID, ringWidth);
            Shader.SetGlobalFloat(EchoOutlineIntensityID, _intensity);
            Shader.SetGlobalColor(EchoOutlineColorID, outlineColor);

            // 红圈独立渐隐与扩散
            UpdateRingAlpha();
            UpdateRingRadius();
            Shader.SetGlobalFloat(EchoRingAlphaID, _ringAlpha);
            Shader.SetGlobalFloat(EchoRingRadiusID, _ringRadius);
        }

        private void StartEcho()
        {
            _state = State.Expanding;
            _radius = 0f;
            _ringRadius = 0f;
            _intensity = 1f;
            _stateTimer = 0f;
            _lastEchoTime = Time.time;

            // 播放摇铃音效
            if (AudioManager.Instance != null)
                AudioManager.Instance.PlaySFXFromResources("SFX/Bell", 0.9f);

            // 记录释放时刻的玩家位置（回声中心固定，不跟随移动）
            Vector3 p = transform.position;
            _center = new Vector2(p.x, p.z);

            // 显示红圈：移动到回声中心位置
            if (_ringVisual != null)
            {
                _ringVisual.transform.position = new Vector3(p.x, ringYOffset, p.z);
                _ringVisual.SetActive(true);
            }

            // 通知订阅者（敌人聆听等）
            if (echoReleasedEvent != null)
                echoReleasedEvent.Raise(p);
            else if (!_warnedEcho)
            {
                Debug.LogWarning("[EchoSystem] echoReleasedEvent 未接线！敌人不会响应摇铃。请在 EchoSystem 的 Echo Released Event 字段拖入 EchoReleased 资产。", this);
                _warnedEcho = true;
            }
        }

        private void UpdateExpanding()
        {
            _radius += expandSpeed * Time.deltaTime;
            if (_radius >= maxRadius)
            {
                _radius = maxRadius;
                _state = State.Sustaining;
                _stateTimer = 0f;
            }
        }

        private void UpdateSustaining()
        {
            _stateTimer += Time.deltaTime;
            _intensity = 1f; // 维持阶段满强度
            if (_stateTimer >= sustainTime)
            {
                _state = State.Fading;
                _stateTimer = 0f;
            }
        }

        private void UpdateFading()
        {
            _stateTimer += Time.deltaTime;
            // 消散阶段强度从 1 → 0
            _intensity = Mathf.Clamp01(1f - _stateTimer / Mathf.Max(0.0001f, fadeTime));
            if (_stateTimer >= fadeTime)
            {
                _state = State.Idle;
                _intensity = 0f;
            }
        }

        /// <summary>
        /// 红圈独立渐隐逻辑：
        /// 从释放瞬间开始线性渐隐（1 → 0），ringFadeTime 秒后完全消失。
        /// </summary>
        private void UpdateRingAlpha()
        {
            if (_state == State.Idle)
            {
                _ringAlpha = 0f;
                return;
            }

            float elapsed = Time.time - _lastEchoTime;
            _ringAlpha = Mathf.Clamp01(1f - elapsed / Mathf.Max(0.001f, ringFadeTime));

            // 渐隐完成后隐藏红圈物体（节省渲染开销）
            if (_ringAlpha <= 0f && _ringVisual != null)
                _ringVisual.SetActive(false);
        }

        /// <summary>
        /// 红圈独立扩散：按 ringExpandSpeed 扩散，封顶 maxRadius。
        /// </summary>
        private void UpdateRingRadius()
        {
            if (_state == State.Idle)
            {
                _ringRadius = 0f;
                return;
            }

            _ringRadius += ringExpandSpeed * Time.deltaTime;
            if (_ringRadius > maxRadius)
                _ringRadius = maxRadius;
        }

        /// <summary>
        /// 外部调用：强制停止回声
        /// </summary>
        public void StopEcho()
        {
            _state = State.Idle;
            _intensity = 0f;
            _ringAlpha = 0f;
            _ringRadius = 0f;
            Shader.SetGlobalFloat(EchoOutlineIntensityID, 0f);
            Shader.SetGlobalFloat(EchoRingAlphaID, 0f);
            if (_ringVisual != null)
                _ringVisual.SetActive(false);
        }

        public bool IsActive => _state != State.Idle;

        /// <summary>摇铃冷却剩余/总时长（供 HUD 显示，替代反射读取私有字段）。</summary>
        public (float remaining, float total) BellCooldown
            => (Mathf.Max(0f, (_lastEchoTime + cooldown) - Time.time), cooldown);

        private void OnValidate()
        {
            if (echoReleasedEvent == null)
                Debug.LogWarning($"[EchoSystem] Echo Released Event 未接线（{gameObject.name}）。按 E 摇铃时敌人不会响应。", this);
        }

        // ===== 编辑器可视化 =====
        private void OnDrawGizmosSelected()
        {
            // 绘制回声最大范围（青色线框）
            Gizmos.color = new Color(0f, 1f, 1f, 0.3f);
            Gizmos.DrawWireSphere(transform.position, maxRadius);
            
            // 绘制当前波纹半径（如果在扩散中）
            if (_state != State.Idle && _radius > 0)
            {
                Gizmos.color = new Color(1f, 1f, 0f, 0.5f);
                Gizmos.DrawWireSphere(new Vector3(_center.x, transform.position.y, _center.y), _radius);
                
                // 绘制波纹中心
                Gizmos.color = Color.yellow;
                Gizmos.DrawWireSphere(new Vector3(_center.x, transform.position.y, _center.y), 0.3f);
            }
            
            // 绘制冷却时间指示
            if (Time.time < _lastEchoTime + cooldown)
            {
                float remaining = (_lastEchoTime + cooldown) - Time.time;
                float cooldownRatio = remaining / cooldown;
                
                Gizmos.color = new Color(1f, 0f, 0f, 0.3f);
                Gizmos.DrawWireSphere(transform.position, maxRadius * cooldownRatio);
            }
        }
    }
}
