using System.Collections;
using System.Collections.Generic;
using DG.Tweening;
using UI_and_Audio;
using UniRx;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.SceneManagement;
using Random = System.Random;

public class AudioManager : MonoBehaviour
{
    public static AudioManager Instance  {get; private set; }

    public AudioMixer audioMixer;
    
    
    [Header("Sources")]
    public AudioSource musicSource;
    public AudioSource sfxSource;
    public AudioSource fireSfxSource; // this needs to be loopable AND stoppable (PlayOneShot would not work here)
    public AudioSource cleanPuddleSfxSource;
    

    [Header("SFX Clips")]
    public AudioClip throwBanana;
    public AudioClip clickSound;
    public List<AudioClip> hoverSounds;
    public List<AudioClip> pageSounds;
    public List<AudioClip> springSnapSounds;
    public AudioClip stoveSound;
    public AudioClip toilet;
    public AudioClip bathtub;
    public List<AudioClip> exclamationSounds;
    public List<AudioClip> whooshes;
    public List<AudioClip> inserts;
    
    private AudioClip lastHoverSound; // avoiding machine gun effect (storing last sound)
    private AudioClip lastPageSound;
    private AudioClip lastSpringSnapSound;
    private AudioClip exclamationSound;
    private AudioClip lastWhoosh;
    public AudioClip lastInsert;
    
    [Header("Cat SFX")]
    public AudioClip catWantsPets;
    public AudioClip catPee;
    public AudioClip catHungry;
    public AudioClip catDeath;
    public List<AudioClip> catSatisfied;

    private AudioClip lastCatSatisfiedSound;
    
    [Header("Lowpass Options For Pause")]
    public float lowpassCutoffLow = 500f; // When tutorial is open
    public float lowpassCutoffHigh = 22000f; // When tutorial is closed
    public float fadeDurationLowpass = 1f;

    [Header("Loose Pitch Value")] public float looseScreenPitchValue = 0.7f;
    public float fadeDurationPitch;
    
    private Tweener lowpassTween;
    
    
    private void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }

        Instance = this;
        
        DontDestroyOnLoad(gameObject);
        
    }

    void OnEnable()  => SceneManager.sceneLoaded += OnSceneLoaded;
    void OnDisable() => SceneManager.sceneLoaded -= OnSceneLoaded;
    
    void OnSceneLoaded(Scene scene, LoadSceneMode mode)
    {
        var sceneMusic = FindFirstObjectByType<SceneMusic>();
        HandleSceneMusicAndPauseEffect();
        if (sceneMusic && sceneMusic.musicClip != null)
        {
            musicSource.clip = sceneMusic.musicClip;
            musicSource.Play();
        }
        
        if (sceneMusic && sceneMusic.musicClip == null)
        {
            Debug.Log("Keep playing the music");
        }
        
        if (sceneMusic == null)
        {
            Debug.Log("sceneMusic clip is null, or sceneMusic is not available so we stop the music"); 
            musicSource.Stop();
        }
        
    }

    public void HandleSceneMusicAndPauseEffect()
    {
        Scene scene = SceneManager.GetActiveScene();
        Debug.Log($"SceneName: {scene.name}");
        if (scene.name == "MainMenu")
        {
            MusicOnNormal();
        }
        
        if (UIManager.Instance)
        {
            UIManager.Instance.isPaused.Subscribe(x =>
            {
                lowpassTween?.Kill();

                float targetCutoff = x ? lowpassCutoffLow : lowpassCutoffHigh;

                // InQuad: starts slow, ends fast (500->22000)
                // OutQuad: starts fast, ends slow (22000->500)
                Ease easeType = x ? Ease.OutCubic : Ease.InCubic;

                lowpassTween = audioMixer.DOSetFloat("MusicLowpassCutoff", targetCutoff, fadeDurationLowpass)
                    .SetEase(easeType)
                    .SetUpdate(true);
            }).AddTo(this);
        }
    }
    
    public void MusicOnLoose()
    {
        audioMixer.DOSetFloat("Pitch", looseScreenPitchValue, fadeDurationPitch)
            .SetEase(Ease.InOutSine)
            .SetUpdate(true);
    }

    public void MusicOnNormal()
    {
        audioMixer.DOSetFloat("Pitch", 1, fadeDurationPitch)
            .SetEase(Ease.InOutSine)
            .SetUpdate(true);
        
        audioMixer.DOSetFloat("MusicLowpassCutoff", 22000, fadeDurationLowpass)
            .SetEase(Ease.OutCubic)
            .SetUpdate(true);
    }

    private void PlayMusic(AudioClip clip, bool loop)
    {
        if (clip == null) return;

        musicSource.clip = clip;
        musicSource.loop = loop;
        musicSource.Play();
    }

    // SFX

    public void PlaySFX(AudioClip clip, float volume = 1f)
    {
        if (clip == null) return;
        sfxSource.PlayOneShot(clip, volume);
    }
    
    public void PlayThrowBanana()
    {
        PlaySFX(throwBanana);
    }

    public void PlayCleanPuddle()
    {
        if(cleanPuddleSfxSource.isPlaying)
            cleanPuddleSfxSource.UnPause();
        else
            cleanPuddleSfxSource.Play();
    }

    public void PauseCleanPuddle()
    {
        cleanPuddleSfxSource.Pause();
    }

    public void PlayStove()
    {
        PlaySFX(stoveSound);
    }
    
    public void PlayClickSound()
    {
        PlaySFX(clickSound);
    }

    public void PlayHoverSound()
    {
        PlaySFX(CycleSFX(hoverSounds, ref lastHoverSound));
    }

    public void PlayTurnPageSound()
    {
        PlaySFX(CycleSFX(pageSounds, ref lastPageSound));
    }
    
    public void PlayCatWantsPets()
    {
        PlaySFX(catWantsPets);
    }

    public void PlayCatPee()
    {
        PlaySFX(catPee);
    }

    public void PlayCatHungry()
    {
        PlaySFX(catHungry);
    }

    public void PlayCatDeath()
    {
        PlaySFX(catDeath);
    }

    public void PlaySpringSnap()
    {
        PlaySFX(CycleSFX(springSnapSounds, ref lastSpringSnapSound));
    }

    public void PlayToilet()
    {
        PlaySFX(toilet);
    }

    public void PlayBathtub()
    {
        PlaySFX(bathtub);
    }

    public void PlayExclamation()
    {
        PlaySFX(CycleSFX(exclamationSounds, ref exclamationSound));
    }

    public void PlayCatSatisfied()
    {
        PlaySFX(CycleSFX(catSatisfied, ref lastCatSatisfiedSound));
    }

    public void PlayWhoosh()
    {
        PlaySFX(CycleSFX(whooshes, ref lastWhoosh));
    }

    public void PlayInsertSound()
    {
        PlaySFX(CycleSFX(inserts, ref lastInsert));
    }
    
    /// <summary>
    ///
    /// Given a list of clips, this function picks a random clip.
    /// It does not pick the lastPlayed clip (avoiding same sound played one after the other),
    /// which needs to be set as a member variable.
    ///
    /// (See PlayTurnPageSound (including references) as example).
    /// 
    /// </summary>
    /// <param name="clips">list from which to pick a random clip</param>
    /// <param name="lastPlayed">reference to last played clip, for avoiding it being played twice in a row</param>
    /// <returns></returns>
    private AudioClip CycleSFX(List<AudioClip> clips, ref AudioClip lastPlayed)
    {
        if (clips == null || clips.Count == 0)
            return null;

        if (clips.Count == 1)
            return clips[0];

        int markedIndex = clips.IndexOf(lastPlayed);
        if (markedIndex < 0) markedIndex = 0; // fallback if not found
    
        int newIndex = UnityEngine.Random.Range(0, clips.Count - 1);
        if (newIndex >= markedIndex)
            newIndex++;

        lastPlayed = clips[newIndex];
        return lastPlayed;
    }
    
}