using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace OCES.Audio
{
    /// <summary>
    /// 音乐通道播放器。
    /// 负责：切换目标 Container、等待节拍对齐、执行淡入淡出 Transition。
    /// </summary>
    class MusicChannelPlayer
    {
        readonly MusicContainerConfig m_containerConfig;
        readonly MusicTransitionConfig m_transitionConfig;
        readonly MusicSegmentConfig m_segmentConfig;
        readonly MonoBehaviour m_coroutineHost;
        readonly ChannelFader m_fader;
        readonly BeatClock m_beatClock;
        readonly List<MusicTransition> m_transitionCandidates = new();
                
        Coroutine m_currentFadeInCoroutine;
        Coroutine m_currentFadeOutCoroutine;
        
        // 当前播放的 Container（用于读取 bpm/timeSig 做节拍对齐）
        MusicContainer m_currentContainer;

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

        internal MusicChannelPlayer(
            MusicContainerConfig containerConfig,
            MusicSegmentConfig segmentConfig,
            MusicTransitionConfig transitionConfig,
            LongAudioContainerPlayer player,
            MonoBehaviour coroutineHost,
            Action<uint> onBeat,
            Action<uint> onBar,
            Action<uint> onGrid)
        {
            this.m_containerConfig     = containerConfig;
            this.m_transitionConfig    = transitionConfig;
            this.m_segmentConfig       = segmentConfig;
            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。
        /// </summary>
        internal void SwitchTo(uint newContainerId)
        {
            Debug.Log($"[MusicChannelPlayer] SwitchTo({newContainerId}): CurrentContainerId={this.m_fader.CurrentContainerId}, CurrentHandle={this.m_fader.CurrentHandle}");
            if (newContainerId == this.m_fader.CurrentContainerId && this.m_fader.CurrentHandle != null)
                return; // 已经在播目标，无需切换
            
            MusicTransition transition = ResolveTransition((int)this.m_fader.CurrentContainerId, (int)newContainerId);
            //Debug.Log($"[MusicChannelPlayer] Switch from {this.m_fader.CurrentContainerId} to {newContainerId} with transition {transition.Id}");

            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)
            {
                Debug.Log($"[MusicChannelPlayer] DoTransition L117: CurrentHandle={this.m_fader.CurrentHandle}, CurrentContainerId={this.m_fader.CurrentContainerId}");
                // 旧 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;
                        syncState.SourceStartOffset = GetEffectiveStartOffset(this.m_currentContainer);
                    }
                    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;
                        
                        double newStartOffset = GetEffectiveStartOffset(container);
                        double dspTime;

                        if (syncState is { Mode: SyncPoint.SameAsCurrentSegment })
                        {
                            double elapsedSeconds = AudioSettings.dspTime - syncState.BaseDspTime;
                            int elapsedSamples = (int)(elapsedSeconds * syncState.SampleRate);
                            double currentAudioTime = (double)(syncState.BaseTimeSamples + elapsedSamples) / syncState.SampleRate;
                            // 当前的source播到了哪里

                            double currentLogicalTime = Math.Max(0, currentAudioTime - syncState.SourceStartOffset);
                            // EntryCue之后播了多少秒。要是小于0说明pre-entry还没播完。
                            
                            double newAudioTime = currentLogicalTime + newStartOffset; 
                            // 新Audio应该从多少秒开始播放

                            bool isPlayPreEntry = true; //TODO transition 是否播放pre-entry
                            syncState.TargetAudioTime = isPlayPreEntry ? currentLogicalTime  + newStartOffset : currentLogicalTime;
                            dspTime = AudioSettings.dspTime - newAudioTime;
                        }
                        else
                        {
                            double startPlayTime;
                            if (this.m_currentContainer == null)
                            {
                                startPlayTime = 0;
                            }
                            else
                            {
                                double fadeInTime = transition?.FadeInTime ?? 0f;
                                startPlayTime = newStartOffset - fadeInTime;
                                startPlayTime = Math.Clamp(startPlayTime, 0, double.PositiveInfinity);
                            }
                            syncState.StartPlayTime = startPlayTime;
                            dspTime = AudioSettings.dspTime;
                        }

                        this.m_currentContainer = container;
                        this.m_beatClock.Restart(container, bpm, dspTime, newStartOffset);
                    }));
            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 target = this.m_beatClock.GetNextDspTime(mode);
            yield return new WaitUntil(() =>  AudioSettings.dspTime >= target);
        }
        
        // ─────────────────────────────────────────────
        // 工具
        // ─────────────────────────────────────────────

        /// <summary>
        /// 查询 Transition 配置，支持精确匹配和 -1 通配符。
        /// </summary>
        MusicTransition ResolveTransition(int sourceContainerId, int destinationContainerId)
        {
            this.m_transitionCandidates.Clear();
            foreach (MusicTransition transition in this.m_transitionConfig.MusicTransitionList())
            {
                bool sourceMatch = transition.SourceContainerID == sourceContainerId || transition.SourceContainerID < 0;
                bool destMatch = transition.DestinationContainerID == destinationContainerId || transition.DestinationContainerID < 0;
                if (sourceMatch && destMatch)
                {
                    this.m_transitionCandidates.Add(transition);
                }
            }

            if (this.m_transitionCandidates.Count == 0)
                return this.m_transitionConfig.QueryById(1);

            MusicTransition best = this.m_transitionCandidates[0];
            for (int i = 1; i < this.m_transitionCandidates.Count; i++)
            {
                if (this.m_transitionCandidates[i].Id > best.Id)
                    best = this.m_transitionCandidates[i];
            }
            return best;
        }
        
        double GetEffectiveStartOffset(MusicContainer container)
        {
            if (container.ContainerType == ContainerType.Blend)
            {
                return 0.0;
            }

            if (container.Segments is not { Count: > 0 })
                return 0.0;
            uint firstId = container.Segments[0];
            if (firstId < 1000000u)
            {
                MusicSegment segment = this.m_segmentConfig.QueryById(firstId);
                return segment?.StartOffset ?? 0.0;
            }
            
            MusicContainer child = this.m_containerConfig.QueryById(firstId);
            return child != null ? GetEffectiveStartOffset(child) : 0.0;

        }
    }
}
