using System;
using System.Collections;
using UnityEngine;

namespace OCES.Audio
{
    /// <summary>
    /// 音乐通道播放器。
    /// 负责：切换目标 Container、等待节拍对齐、执行淡入淡出 Transition。
    /// </summary>
    class MusicChannelPlayer
    {
        readonly MusicContainerConfig m_containerConfig;
        readonly MusicTransitionConfig m_transitionConfig;
        readonly MonoBehaviour m_coroutineHost;
        readonly ChannelFader m_fader;
        readonly BeatClock m_beatClock;
                
        Coroutine m_currentFadeInCoroutine;
        Coroutine m_currentFadeOutCoroutine;

        // 当前正在播放的句柄
        ContainerPlayHandle m_currentHandle;
        uint m_currentContainerId;

        // 当前播放的 Container（用于读取 bpm/timeSig 做节拍对齐）
        MusicContainer m_currentContainer;

        // 当前播放开始的时间（用于计算当前播到哪个拍子）
        double m_playStartTime;

        // 正在进行的 transition 协程（防止重叠）
        Coroutine m_transitionCoroutine;

        internal MusicChannelPlayer(
            MusicContainerConfig containerConfig,
            MusicTransitionConfig transitionConfig,
            MusicContainerPlayer player,
            MonoBehaviour coroutineHost,
            Action<uint> onBeat,
            Action<uint> onBar,
            Action<uint> onGrid)
        {
            this.m_containerConfig     = containerConfig;
            this.m_transitionConfig    = transitionConfig;
            this.m_coroutineHost       = coroutineHost;
            this.m_fader               = new ChannelFader(player, coroutineHost);
            this.m_beatClock           = new BeatClock(coroutineHost, onBeat, onBar, onGrid);
            
            player.OnBlendError += this.m_beatClock.OnBlendError;
        }

        // ─────────────────────────────────────────────
        // 公开接口
        // ─────────────────────────────────────────────

        /// <summary>
        /// 切换到新的 Container。
        /// fromPathId / toPathId 用于查询 Transition 配置。
        /// </summary>
        internal void SwitchTo(uint newContainerId, uint fromPathId, uint toPathId)
        {
            if (newContainerId == this.m_currentContainerId && this.m_currentHandle != null)
                return; // 已经在播目标，无需切换

            MusicTransition transition = ResolveTransition(fromPathId, toPathId);

            if (this.m_transitionCoroutine != null)
                this.m_coroutineHost.StopCoroutine(this.m_transitionCoroutine);

            this.m_transitionCoroutine = this.m_coroutineHost.StartCoroutine(
                DoTransition(newContainerId, transition));
        }

        /// <summary>
        /// 立即停止当前播放（无淡出）
        /// </summary>
        internal void Stop()
        {
            this.m_beatClock.StopAll();
            
            if (this.m_transitionCoroutine != null)
                this.m_coroutineHost.StopCoroutine(this.m_transitionCoroutine);

            this.m_fader.StopCurrent();
        }

        // ─────────────────────────────────────────────
        // Transition 流程
        // ─────────────────────────────────────────────

        IEnumerator DoTransition(uint newContainerId, MusicTransition transition)
        {
            if (this.m_currentFadeInCoroutine != null)
            {
                this.m_coroutineHost.StopCoroutine(this.m_currentFadeInCoroutine);
                this.m_currentFadeInCoroutine = null;
            }

            if (this.m_currentFadeOutCoroutine != null)
            {
                this.m_coroutineHost.StopCoroutine(this.m_currentFadeOutCoroutine);
                this.m_currentFadeOutCoroutine = null;
            }

            // ── 构建 SyncPoint 状态 ──
            SyncPointState syncState = new()
            {
                Mode = transition?.SyncPoint ?? SyncPoint.Start,
                Ready = false,
            };

            //Debug.Log($"[MusicChannelPlayer] DoTransition: transition?.SyncPoint={transition?.SyncPoint}, Mode={syncState.Mode}");

            if (syncState.Mode == SyncPoint.SameAsCurrentSegment)
            {
                // 旧 Container 不存在（如游戏首次启动），静默降级为 Start
                if (this.m_fader.CurrentHandle == null)
                {
                    syncState.Mode = SyncPoint.Start;
                }
                // Blend 类型不支持 SameAsCurrentSegment，降级为 Start
                else if (this.m_currentContainer is { ContainerType: ContainerType.Blend })
                {
                    Debug.LogWarning($"[MusicChannelPlayer] SyncPoint.SameAsCurrentSegment 不支持 Blend 类型 Container({this.m_currentContainer.Id})，降级为 Start");
                    syncState.Mode = SyncPoint.Start;
                }
                else
                {
                    AudioSource oldSource = this.m_fader.CurrentHandle.GetFirstLeafSource();
                    if (oldSource && oldSource.clip)
                    {
                        syncState.BaseTimeSamples = oldSource.timeSamples;
                        syncState.BaseDspTime = AudioSettings.dspTime;
                        syncState.SampleRate = oldSource.clip.frequency;
                    }
                    else
                    {
                        Debug.LogWarning("[MusicChannelPlayer] SameAsCurrentSegment: 旧 Source 不存在，降级为 Start");
                        syncState.Mode = SyncPoint.Start;
                    }
                }
            }

            // ── 1. 等待节拍对齐（由 Transition 的 AlignMode 决定）──
            if (transition != null && this.m_currentContainer != null)
            {
                yield return this.m_coroutineHost.StartCoroutine(
                    WaitForAlignment(transition.AlignMode, this.m_currentContainer));
            }

            // ── 2 & 3. 淡出与淡入并行：两条分支从同一时刻起算各自的 Offset，互不等待 ──
            if (newContainerId == this.m_fader.CurrentContainerId && this.m_fader.CurrentHandle != null) yield break;
            // 如果等待期间被切回来了，就不淡变了

            ContainerPlayHandle outgoing = this.m_fader.CurrentHandle;
            float outVol = this.m_fader.CurrentVolume;

            this.m_currentFadeOutCoroutine = this.m_coroutineHost.StartCoroutine(
                this.m_fader.FadeOutBranch(outgoing, outVol, transition, syncState));

            this.m_currentFadeInCoroutine = this.m_coroutineHost.StartCoroutine(
                this.m_fader.FadeInBranch(newContainerId, transition, syncState,
                    onContainerStarted: () =>
                    {
                        MusicContainer container = this.m_containerConfig.QueryById(newContainerId);
                        float bpm = container.Bpm > 0f ? container.Bpm : 120f;

                        // SyncPoint: BeatClock 用调整后的 dspTime，对齐到音频实际播放位置
                        double dspTime;
                        if (syncState is { Mode: SyncPoint.SameAsCurrentSegment })
                        {
                            double elapsedSeconds = AudioSettings.dspTime - syncState.BaseDspTime;
                            int elapsedSamples = (int)(elapsedSeconds * syncState.SampleRate);
                            double audioTime = (double)(syncState.BaseTimeSamples + elapsedSamples) / syncState.SampleRate;
                            dspTime = AudioSettings.dspTime - audioTime;
                        }
                        else
                        {
                            dspTime = AudioSettings.dspTime;
                        }

                        this.m_currentContainer = container;
                        this.m_playStartTime    = dspTime;
                        this.m_beatClock.Restart(container, bpm, dspTime);
                    }));
            yield return this.m_currentFadeInCoroutine;
            this.m_currentFadeInCoroutine = null;
        }

        // ─────────────────────────────────────────────
        // 节拍对齐等待
        // ─────────────────────────────────────────────

        IEnumerator WaitForAlignment(AlignMode mode, MusicContainer container)
        {
            if (mode == AlignMode.Immediate || container.Bpm <= 0f)
                yield break;

            double now = AudioSettings.dspTime;
            double elapsed = now - this.m_playStartTime;

            double secondsPerBeat = 60.0 / container.Bpm;

            switch (mode)
            {
                case AlignMode.Beat:
                {
                    double beatsElapsed  = elapsed / secondsPerBeat;
                    double nextBeat      = Math.Ceiling(beatsElapsed);
                    double waitSeconds   = (nextBeat - beatsElapsed) * secondsPerBeat;
                    if (waitSeconds > 0.001)
                        yield return new WaitForSeconds((float)waitSeconds);
                    break;
                }
                case AlignMode.Bar:
                {
                    int beatsPerBar = MusicContainerConfig.GetBeatsPerBar(container.TimeSig);
                    double secondsPerBar   = secondsPerBeat * beatsPerBar;
                    double barsElapsed     = elapsed / secondsPerBar;
                    double nextBar         = Math.Ceiling(barsElapsed);
                    double waitSeconds     = (nextBar - barsElapsed) * secondsPerBar;
                    if (waitSeconds > 0.001)
                        yield return new WaitForSeconds((float)waitSeconds);
                    break;
                }
            }
        }
        
        // ─────────────────────────────────────────────
        // 工具
        // ─────────────────────────────────────────────

        /// <summary>
        /// 查询 Transition 配置，支持精确匹配和 999 通配符。
        /// ID 规则：FromPathId × 1000 + ToPathId，999 表示任意。
        /// </summary>
        MusicTransition ResolveTransition(uint fromPathId, uint toPathId)
        {
            // 优先精确匹配
            uint exactId = fromPathId * 1000 + toPathId;
            MusicTransition exact = this.m_transitionConfig.QueryById(exactId);
            if (exact != null) return exact;

            // From 为任意
            uint fromWildcard = 999u * 1000 + toPathId;
            MusicTransition fromWild = this.m_transitionConfig.QueryById(fromWildcard);
            if (fromWild != null) return fromWild;

            // To 为任意
            uint toWildcard = fromPathId * 1000 + 999u;
            MusicTransition toWild = this.m_transitionConfig.QueryById(toWildcard);
            if (toWild != null) return toWild;

            // 全通配
            const uint allWild = 999u * 1000 + 999u;
            return this.m_transitionConfig.QueryById(allWild);
        }
    }
}
