using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using OCES.Audio.HandWritten;
using UnityEngine;
using UnityEngine.Audio;

namespace OCES.Audio
{
    /// <summary>
    /// 音效系统
    /// </summary>
    public class SfxSystem : MonoBehaviour
    {
        public UnityEngine.UI.Text audioSystemTextBox;
        
        const int k_maxGlobalConcurrent = 128;
        
        //记录某个 AudioObject 最近一次触发播放的时刻，用于 MinInterval 节流判断，是"上次什么时候播过"。
        readonly Dictionary<uint, double> m_lastPlayTime = new(); 
        readonly Dictionary<uint, int> m_clipConcurrentCount = new();
        readonly List<ActiveSound> m_activeSounds = new();

        // 复用列表，避免 TryPlay 每帧分配临时 List
        readonly List<ActiveSound> m_tempSameObject    = new();
        readonly List<ActiveSound> m_tempSameGroup     = new();
        readonly List<ActiveSound> m_tempLowerPriority = new();

        AudioGroupConfig m_groupConfig;
        AudioMixerGroup m_sfxGroup;
        AudioMixerGroup m_voiceGroup;
        AudioMixerGroup m_accentSfxGroup;
        AudioSourcePool m_pool;
        AudioContainerSelector m_containerSelector;
        PitchStepResolver m_pitchStepResolver;
        VolumeStepResolver m_volumeStepResolver;

#if UNITY_EDITOR
        void Update()
        {
            Editor.DebugInfoCollector.Instance.ActiveSounds = this.m_activeSounds;
            Editor.DebugInfoCollector.Instance.ClipConcurrentCount = this.m_clipConcurrentCount;
        }
#endif


        int GetClipCount(uint id)
        {
            return this.m_clipConcurrentCount.GetValueOrDefault(id, 0);
        }

        void IncrementClipCount(uint id)
        {
            this.m_clipConcurrentCount[id] = GetClipCount(id) + 1;
#if UNITY_EDITOR
            Debug.Log($"{id} count added to {GetClipCount(id)}");
#endif
        }

        void DecrementClipCount(uint id)
        {
            int cur = GetClipCount(id);
            if (cur > 0) this.m_clipConcurrentCount[id] = cur - 1;
            //Debug.Log($"{id} clip count minus to {GetClipCount(id)}");
        }

        // ─────────────────────────────────────────────
        // 初始化
        // ─────────────────────────────────────────────

        public void Initialize(AudioGroupConfig groups, AudioMixer mixer, AudioSourcePool pool)
        {
            this.m_groupConfig = groups;

            this.m_pool = pool;
            this.m_containerSelector = new AudioContainerSelector();
            this.m_pitchStepResolver = new PitchStepResolver();
            this.m_volumeStepResolver = new VolumeStepResolver();

            AudioMixerGroup[] sfx      = Find("Master/Regular/SFX");
            if (sfx.Length > 0) this.m_sfxGroup      = sfx[0];
            AudioMixerGroup[] voice    = Find("Master/Regular/Voice");    
            if (voice.Length > 0) this.m_voiceGroup    = voice[0];
            AudioMixerGroup[] accentSfx = Find("Master/SFX_Accent");
            if (accentSfx.Length > 0) this.m_accentSfxGroup = accentSfx[0];
            
            return;

            AudioMixerGroup[] Find(string path) => mixer.FindMatchingGroups(path);
        }

        // ─────────────────────────────────────────────
        // 节流 & 调度入口
        // ─────────────────────────────────────────────

        internal void TryPlay(AudioObject audioObject, Action onPlay = null)
        {
            double now = Time.realtimeSinceStartupAsDouble * 1000.0;

            // 第一层：时间节流 单位毫秒
            if (this.m_lastPlayTime.TryGetValue(audioObject.Id, out double lastTime) &&
                now - lastTime < audioObject.MinInterval)
            {
                Debug.LogWarning($"[Throttle] {audioObject.Name[0]} 未达到最小间隔，取消播放。");
                return;
            }

            // 第二层：单 AudioObject/Clip 并发控制 
            if (audioObject.ThrottleCount != 0)
            {   
                this.m_tempSameObject.Clear();
                foreach (ActiveSound activeSound in this.m_activeSounds)
                {
                    if (activeSound.AudioObject.Id == audioObject.Id)
                    {
                        this.m_tempSameObject.Add(activeSound);
                    }
                }
                
                if (this.m_tempSameObject.Count >= audioObject.ThrottleCount &&
                    !TryKill(this.m_tempSameObject, audioObject.KillMode, $"[Object] {audioObject.Name[0]}"))
                    return;
                
            }

            // 第三层：Group 并发控制
            AudioGroup groupConfig = this.m_groupConfig.QueryById(audioObject.Group);
            if (groupConfig == null)
            {
                groupConfig = this.m_groupConfig.QueryById(1);
                Debug.Log($"未找到{audioObject.Id}对应的组文件，已使用默认配置组1。");
            }
            this.m_tempSameGroup.Clear();
            foreach (ActiveSound activeSound in this.m_activeSounds)
            {
                if (activeSound.AudioObject.Group == audioObject.Group)
                {
                    this.m_tempSameGroup.Add(activeSound);
                }
            }

            if (this.m_tempSameGroup.Count >= groupConfig.GroupThrottleCount)
            {
                this.m_tempLowerPriority.Clear();
                foreach (ActiveSound activeSound in this.m_tempSameGroup)
                {
                    if (activeSound.AudioObject.Priority > audioObject.Priority)
                    {
                        this.m_tempLowerPriority.Add(activeSound);
                    }
                }
                
                List<ActiveSound> killCandidates = this.m_tempLowerPriority.Count > 0 ? this.m_tempLowerPriority : this.m_tempSameGroup;
                KillMode killMode = this.m_tempLowerPriority.Count > 0 ? groupConfig.KillMode : audioObject.KillMode;

                if (!TryKill(killCandidates, killMode, $"[Group] {audioObject.Name[0]}"))
                    return;
            }

            // 第四层：全局并发控制
            if (this.m_activeSounds.Count >= k_maxGlobalConcurrent)
            {
                this.m_tempLowerPriority.Clear();
                foreach (ActiveSound activeSound in this.m_activeSounds)
                {
                    if (activeSound.AudioObject.Priority > audioObject.Priority)
                    {
                        this.m_tempLowerPriority.Add(activeSound);
                    }
                }
                
                if (this.m_tempLowerPriority.Count == 0 || !TryKill(this.m_tempLowerPriority, KillMode.Oldest, "[Global]"))
                    return;
            }

            // 执行播放
            float pitch = this.m_pitchStepResolver.ResolvePitch(audioObject, now); //算一下用不用变调
            float volume = this.m_volumeStepResolver.ResolveVolume(audioObject, now);
            PlayNewSound(audioObject, pitch, volume);
            this.m_lastPlayTime[audioObject.Id] = now;
            onPlay?.Invoke();
        }

        // ─────────────────────────────────────────────
        // 播放逻辑
        // ─────────────────────────────────────────────

        void PlayNewSound(AudioObject audioObject, float pitch, float volume)
        {
            ActiveSound active = new()
            {
                AudioObject = audioObject,
                Pitch = pitch,
                Volume =  volume,
                State = ActiveSoundState.Pending,
                StartTime = Time.realtimeSinceStartupAsDouble,
            };
            
            if (audioObject.InitialDelay > 0)
            {
                this.m_activeSounds.Add(active);
                IncrementClipCount(audioObject.Id);
                
                active.Coroutine = StartCoroutine(PlayAfterDelay(active, audioObject));
                return;
            }
            
            ExecutePlay(active);
        }

        /// <summary>
        /// 连续容器播放协程（Random / Sequence 持续模式）
        /// </summary>
        IEnumerator PlayContainerContinuous(AudioSource source, AudioObject audioObject, ActiveSound chainActive, int startIndex)
        {
            bool isRandom = audioObject.ContainerType == ContainerType.Random;

            // 初始化 Random 模式的辅助结构
            List<int> remainingPool  = isRandom ? new List<int>(Enumerable.Range(0, audioObject.Name.Count)) : null;
            Queue<int> recentPlayed  = isRandom ? new Queue<int>() : null;
            int limitRepetition      = isRandom ? Mathf.Clamp(audioObject.LimitRepetition, 0, audioObject.Name.Count - 1) : 0;
            
            int index = startIndex;

            while (true)
            {
                // 选择本轮播放的 index
                if (isRandom)
                    index = this.m_containerSelector.PickNextRandomIndex(
                        audioObject, 
                        remainingPool, 
                        recentPlayed, 
                        limitRepetition);
                
                // 配置并播放
                if (!SetupSource(source, chainActive, index))
                {
                    Debug.LogError($"音频文件未找到：{audioObject.Name[index]}");
                    yield break;
                }

                StartPlayback(source);
                
                yield return new WaitWhile(() => source.isPlaying);
                
                // 判断本轮是否播完
                bool roundFinished = isRandom
                    ? remainingPool.Count == 0 || this.m_containerSelector.GetHistoryCount(audioObject.Id) == audioObject.Name.Count
                    : ++index >= audioObject.Name.Count;

                if (roundFinished)
                {
                    DecrementClipCount(audioObject.Id); 
                    this.m_activeSounds.Remove(chainActive);

                    if (isRandom)
                        this.m_containerSelector.ResetHistory(audioObject.Id);

                    this.m_pool.ReturnToPool(source.gameObject);
                    
                    //Debug.Log($"[Container - Continuous] 协程正常结束: {audioObject.Name[0]}");
                    yield break;
                }
            }
        }
        static void StartPlayback(AudioSource source)
        {
            source.Play();
        }

        IEnumerator PlayAfterDelay(ActiveSound active, AudioObject audioObject)
        {
            Debug.Log($"Delaying for {audioObject.InitialDelay} second(s).");
            yield return new WaitForSeconds(audioObject.InitialDelay);
            
            if (!this.m_activeSounds.Contains(active)) yield break;

            ExecutePlay(active, isRegistered: true);
        }

        // ─────────────────────────────────────────────
        // 工具方法
        // ─────────────────────────────────────────────

        /// <summary>
        /// 尝试打断一个候选声音。返回 false 表示应取消当前播放（Newest 模式）。
        /// </summary>
        bool TryKill(List<ActiveSound> candidates, KillMode killMode, string logPrefix)
        {
            if (killMode == KillMode.Newest)
            {
                Debug.LogWarning($"{logPrefix} 被 KillNewest 取消播放。");
                return false;
            }

            ActiveSound toKill = SelectOldest(candidates);
            if (toKill != null)
            {
                Debug.LogWarning($"{logPrefix} 打断了 {toKill.AudioObject.Name[0]}。");
                StopSound(toKill);
            }
            return true;
        }

        /// <summary>
        /// 从候选列表中选出开始时间最早的声音
        /// </summary>
        ActiveSound SelectOldest(List<ActiveSound> candidates)
        {
            ActiveSound oldest = null;
            foreach (ActiveSound s in candidates)
            {
                if (oldest == null || s.StartTime < oldest.StartTime)
                {
                    oldest = s;
                }
            }
            return oldest;
        }

        /// <summary>
        /// 根据 MixingType 获取对应的 AudioMixerGroup
        /// </summary>
        AudioMixerGroup GetMixerGroup(MixingType type) => type switch
        {
            MixingType.Voice => this.m_voiceGroup,
            MixingType.Accented => this.m_accentSfxGroup,
            _                => GetDefaultGroup(type),
        };

        AudioMixerGroup GetDefaultGroup(MixingType type)
        {
            if (type != MixingType.Sfx)
            {
                Debug.LogWarning($"[SfxSystem] Unrecognized MixingType: {type}, defaulting to SFX.");
            }
            
            return this.m_sfxGroup;
        }

        bool SetupSource(AudioSource source, ActiveSound activeSound, int clipIndex = 0)
        {
            AudioObject audioObject = activeSound.AudioObject;
            AudioClip clip = Resources.Load<AudioClip>($"Audios/{audioObject.Name[clipIndex]}"); // TODO 抽象同一资源加载接口
            if (!clip)
            {
                Debug.LogError($"音频文件未找到：{audioObject.Name[clipIndex]}");
                return false;
            }

            source.clip = clip;
            source.loop = audioObject.LoopCount < 0;
            source.priority = audioObject.Priority;
            source.outputAudioMixerGroup = GetMixerGroup(audioObject.MixingType);
            source.pitch = activeSound.Pitch;
            source.volume = activeSound.Volume;

            return true;
        }

        void RegisterActiveSound(ActiveSound activeSound, AudioSource audioSource, bool isRegistered = false)
        {
            activeSound.Source = audioSource;
            activeSound.State = ActiveSoundState.Playing;
            activeSound.StartTime = Time.realtimeSinceStartupAsDouble;

            if (!isRegistered)
            {
                this.m_activeSounds.Add(activeSound);
                IncrementClipCount(activeSound.AudioObject.Id);
            }

            activeSound.Coroutine = StartCoroutine(RemoveWhenFinished(activeSound));
        }

        void ExecutePlay(ActiveSound active, bool isRegistered = false)
        {
            AudioObject audioObject = active.AudioObject;
            float pitch = active.Pitch;
            float volume = active.Volume;

            // =======================
            // Blend（每个clip一个ActiveSound）
            // =======================
            if (audioObject.ContainerType == ContainerType.Blend)
            {
                for (int i = 0; i < audioObject.Name.Count; i++)
                {
                    AudioSource source = this.m_pool.AcquireAudioSource();

                    ActiveSound child = new()
                    {
                        AudioObject = audioObject,
                        Pitch = pitch,
                        Volume = volume,
                        State = ActiveSoundState.Playing,
                    };

                    if (!SetupSource(source, child, i))
                    {
                        this.m_pool.ReturnToPool(source.gameObject);
                        continue;
                    }

                    StartPlayback(source);
                    RegisterActiveSound(child, source);
                }

                // 删除 pending 占位
                if (this.m_activeSounds.Contains(active))
                {
                    DecrementClipCount(audioObject.Id);
                    this.m_activeSounds.Remove(active);
                }

                return;
            }

            AudioSource sourceSingle = this.m_pool.AcquireAudioSource();

            // =======================
            // Continuous
            // =======================
            if (audioObject.Name.Count > 1 && audioObject.ContainerPlayMode)
            {
                active.Source = sourceSingle;
                active.State = ActiveSoundState.Playing;

                this.m_activeSounds.Add(active);
                IncrementClipCount(audioObject.Id);

                int start = audioObject.ContainerType == ContainerType.Random ? -1 : 0;

                active.Coroutine = StartCoroutine(
                    PlayContainerContinuous(sourceSingle, audioObject, active, start)
                );

                return;
            }

            // =======================
            // 单次播放
            // =======================
            int index = audioObject.ContainerType switch
            {
                ContainerType.Random   => m_containerSelector.PickShuffleIndex(audioObject),
                ContainerType.Sequence => m_containerSelector.GetNextSequenceIndex(audioObject),
                _ => 0
            };

            if (!SetupSource(sourceSingle, active, index))
            {
                this.m_pool.ReturnToPool(sourceSingle.gameObject);
                return;
            }

            StartPlayback(sourceSingle);
            RegisterActiveSound(active, sourceSingle, isRegistered);
        }
        IEnumerator RemoveWhenFinished(ActiveSound active)
        {
            if (active.AudioObject.LoopCount < 0)
            {
                //Debug.Log($"RemoveWhenFinished协程已结束，因为播放的是无限循环音效{active.Source.clip.name}");
                yield break;
            }
            
            do
            {
                yield return new WaitWhile(() => active.Source.isPlaying);
                active.CurrentLoopCount++;
            }
            while (active.AudioObject.LoopCount > 0 
                   && active.CurrentLoopCount < active.AudioObject.LoopCount
                   && PlayAgain(active));

            DecrementClipCount(active.AudioObject.Id);
            this.m_activeSounds.Remove(active);
            this.m_pool.ReturnToPool(active.Source.gameObject);
        }

        bool PlayAgain(ActiveSound active)
        {
            StartPlayback(active.Source);
            return true;
        }

        void StopSound(ActiveSound active)
        {
            if (active.Coroutine != null)
            {
                StopCoroutine(active.Coroutine);
                //Debug.Log($"[StopSound] 协程已终止: {active.AudioObject.Name[0]}");
            }
            
            if(active.Source) active.Source.Stop();
            DecrementClipCount(active.AudioObject.Id);
            this.m_activeSounds.Remove(active);
            this.m_pool.ReturnToPool(active.Source.gameObject);
        }


    }

    /// <summary>
    /// 活跃的声音封装
    /// </summary>
    public class ActiveSound
    {
        public AudioSource Source;
        public AudioObject AudioObject;
        // 记录某个具体播放实例的开始时间，用于 SelectOldest 在多个并发实例中挑出最老的那个来打断，是"这个实例是什么时候开始的"。
        public double StartTime;
        public int CurrentLoopCount;
        public Coroutine Coroutine;
        public float Pitch;
        public float Volume;
        public ActiveSoundState State;
    }
}