using Avalonia.Media.Imaging;
using Avalonia.Threading;
using CommunityToolkit.Mvvm.Input;
using Harmonia.Core.Models;
using Harmonia.Core.Player;
using Harmonia.Core.Playlists;
using Harmonia.Core.Scanner;
using Harmonia.UI.Caching;
using Harmonia.UI.Models;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Input;

namespace Harmonia.UI.ViewModels;

public partial class PlaybackBarViewModel : ViewModelBase, IDisposable
{
    private readonly IAudioPlayer _audioPlayer;
    private readonly IAudioBitmapCache _audioBitmapCache;
    private readonly DispatcherTimer _timer;

    private CancellationTokenSource? _audioImageCancellationTokenSource;

    private Song? _song;
    public Song? Song
    {
        get
        {
            return _song;
        }
        private set
        {
            _song = value;
            OnPropertyChanged();
            OnPropertyChanged(nameof(FormattedSongInfo));

            CurrentPosition = 0; // What if player is being loaded and returning to save state?
            Position = 0;
            MaxPosition = value?.Length.TotalSeconds ?? 0;
        }
    }

    private Bitmap? _songImageSource;
    public Bitmap? SongImageSource
    {
        get
        {
            return _songImageSource;
        }
        private set
        {
            _songImageSource = value;
            OnPropertyChanged();
        }
    }

    public string FormattedSongInfo => Song != null
        ? $"{Song.FileType} - {Song.BitRate} kbps - {Song.SampleRate} Hz"
        : string.Empty;

    private double _currentPosition;
    public double CurrentPosition
    {
        get
        {
            return _currentPosition;
        }
        set
        {
            _currentPosition = value;
            OnPropertyChanged();

            if (_isPositionChangeInProgress)
                _audioPlayer.Position = value;
        }
    }

    private double _position;
    public double Position
    {
        get
        {
            return _position;
        }
        private set
        {
            _position = value;
            OnPropertyChanged();
        }
    }

    private double _maxPosition;
    public double MaxPosition
    {
        get
        {
            return _maxPosition;
        }
        set
        {
            _maxPosition = value;
            OnPropertyChanged();
        }
    }

    private bool _isPositionChangeInProgress;
    public bool IsPositionChangeInProgress
    {
        get { return _isPositionChangeInProgress; }
        set
        {
            _isPositionChangeInProgress = value;
            OnPropertyChanged();
        }
    }

    public double Volume
    {
        get
        {
            return _audioPlayer.Volume;
        }
        set
        {
            if (IsMuted)
                IsMuted = false;

            _audioPlayer.Volume = value;
            OnPropertyChanged();
            OnPropertyChanged(nameof(VolumeState));
        }
    }

    public bool IsMuted
    {
        get
        {
            return _audioPlayer.IsMuted;
        }
        set
        {
            _audioPlayer.IsMuted = value;
            OnPropertyChanged();
            OnPropertyChanged(nameof(VolumeState));
        }
    }

    public VolumeState VolumeState
    {
        get
        {
            if (IsMuted)
                return VolumeState.Muted;

            if (Volume == 0)
                return VolumeState.Off;

            if (Volume < .33)
                return VolumeState.Low;

            if (Volume < .66)
                return VolumeState.Medium;

            return VolumeState.High;
        }
    }

    public bool IsRandom
    {
        get
        {
            return _audioPlayer.IsRandom;
        }
        private set
        {
            _audioPlayer.IsRandom = value;
            OnPropertyChanged();
        }
    }

    public RepeatState RepeatState
    {
        get
        {
            return _audioPlayer.RepeatState;
        }
        private set
        {
            _audioPlayer.RepeatState = value;
            OnPropertyChanged();
        }
    }

    public ICommand PlaySongCommand => new RelayCommand(Play);
    public ICommand PauseSongCommand => new RelayCommand(Pause);
    public ICommand StopSongCommand => new RelayCommand(Stop);
    public ICommand PreviousSongCommand => new RelayCommand(Previous);
    public ICommand NextSongCommand => new RelayCommand(Next);
    public ICommand ToggleMuteCommand => new RelayCommand(ToggleMute);
    public ICommand ToggleRandomizerCommand => new RelayCommand(ToggleRandomizer);
    public ICommand ToggleRepeatCommand => new RelayCommand(ToggleRepeat);

    public PlaybackBarViewModel(IAudioPlayer audioPlayer, IAudioBitmapCache audioBitmapCache, IPlaylistRepository playlistRepository, IAudioFileScanner audioFileScanner)
    {
        _audioPlayer = audioPlayer;
        _audioPlayer.PlayingSongChanged += OnAudioPlayerPlayingSongChanged;

        _audioBitmapCache = audioBitmapCache;

        _timer = new(TimeSpan.FromMilliseconds(100), DispatcherPriority.Default, TickTock);

        Task.Run(() => PlayDemoSong(playlistRepository));
    }

    private async Task PlayDemoSong(IPlaylistRepository playlistRepository)
    {
        if (playlistRepository.Get().Count == 0)
        {
            playlistRepository.AddPlaylist();
        }

        Playlist playlist = playlistRepository.Get().First();

        if (playlist.Songs.Count > 0)
            await _audioPlayer.LoadAsync(playlist.Songs[0], PlaybackMode.LoadOnly);
    }

    private void OnAudioPlayerPlayingSongChanged(object? sender, EventArgs e)
    {
        Song = _audioPlayer.PlayingSong?.Song;
        Task.Run(UpdateImage);
    }

    private async Task UpdateImage()
    {
        // TODO: Show default picture
        if (Song == null)
            return;

        if (_audioImageCancellationTokenSource != null)
            await _audioImageCancellationTokenSource.CancelAsync();

        _audioImageCancellationTokenSource = new();
        CancellationToken cancellationToken = _audioImageCancellationTokenSource.Token;

        Bitmap? bitmap = await _audioBitmapCache.GetAsync(Song, cancellationToken);

        await Dispatcher.UIThread.InvokeAsync(() => SetSongImageSource(bitmap));
    }

    private void SetSongImageSource(Bitmap? bitmap)
    {
        SongImageSource = bitmap;
    }

    private void TickTock(object? sender, object e)
    {
        Position = _audioPlayer.Position;

        if (IsPositionChangeInProgress)
            return;

        CurrentPosition = _audioPlayer.Position;
    }

    public void Play()
    {
        _audioPlayer.Play();
    }

    public void Pause()
    {
        _audioPlayer.Pause();
    }

    public void Stop()
    {
        _audioPlayer.Stop();
        CurrentPosition = 0;
        Position = 0;
    }

    public void Previous()
    {
        _audioPlayer.PreviousAsync();
    }

    public void Next()
    {
        _audioPlayer.NextAsync();
    }

    public void ToggleMute()
    {
        IsMuted = !IsMuted;
    }

    public void ToggleRandomizer()
    {
        IsRandom = !IsRandom;
    }

    public void ToggleRepeat()
    {
        RepeatState = GetNextRepeatState();
    }

    private RepeatState GetNextRepeatState()
    {
        return _audioPlayer.RepeatState switch
        {
            RepeatState.Off => RepeatState.RepeatAll,
            RepeatState.RepeatAll => RepeatState.RepeatOne,
            RepeatState.RepeatOne => RepeatState.Off,
            _ => _audioPlayer.RepeatState,
        };
    }

    public void Dispose()
    {
        SongImageSource?.Dispose();
        GC.SuppressFinalize(this);
    }
}