using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;
using NexusShell.Models;

namespace NexusShell.Services;

/// <summary>
/// Native WPF playback: <see cref="MediaElement"/> for video, <see cref="MediaPlayer"/> for audio.
/// Both use Windows Media Foundation via WPF media APIs (not WMP shell / VLC).
/// </summary>
public sealed class NexusMediaPlayerService : IDisposable
{
    private readonly MediaPlayer _audioPlayer = new();
    private readonly NexusMediaLibraryService _library;
    private readonly Dispatcher _dispatcher;
    private MediaElement? _videoElement;
    private string? _currentPath;
    private NexusMediaKind _currentKind;
    private DispatcherTimer? _positionTimer;
    private bool _isPlaying;
    private double _volume = 0.8;
    private TimeSpan _duration;
    private TimeSpan _position;

    public NexusMediaPlayerService(NexusMediaLibraryService library)
    {
        _library = library;
        _dispatcher = Application.Current?.Dispatcher ?? Dispatcher.CurrentDispatcher;
        _audioPlayer.Volume = _volume;
        _audioPlayer.MediaOpened += (_, _) => OnAudioOpened();
        _audioPlayer.MediaEnded += (_, _) => MediaEnded?.Invoke(this, EventArgs.Empty);
        _audioPlayer.MediaFailed += (_, args) =>
            PlaybackError?.Invoke(this, args.ErrorException?.Message ?? "Afspilning mislykkedes.");
    }

    public event EventHandler? StateChanged;
    public event EventHandler? MediaOpened;
    public event EventHandler? MediaEnded;
    public event EventHandler<string>? PlaybackError;
    public event EventHandler? RequestFullscreen;

    public string? CurrentPath => _currentPath;
    public NexusMediaKind CurrentKind => _currentKind;
    public bool IsPlaying => _isPlaying;
    public double Volume => _volume;
    public TimeSpan Position => _position;
    public TimeSpan Duration => _duration;
    public NexusRepeatMode RepeatMode { get; private set; } = NexusRepeatMode.Off;
    public bool ShuffleEnabled { get; private set; }

    public void AttachVideoElement(MediaElement element)
    {
        DetachVideoElement();
        _videoElement = element;
        _videoElement.LoadedBehavior = MediaState.Manual;
        _videoElement.UnloadedBehavior = MediaState.Manual;
        _videoElement.Volume = _volume;
        _videoElement.MediaOpened += OnVideoMediaOpened;
        _videoElement.MediaEnded += (_, _) => MediaEnded?.Invoke(this, EventArgs.Empty);
        _videoElement.MediaFailed += (_, args) =>
            PlaybackError?.Invoke(this, args.ErrorException?.Message ?? "Videoafspilning mislykkedes.");
    }

    public void DetachVideoElement()
    {
        if (_videoElement is null)
        {
            return;
        }

        _videoElement.MediaOpened -= OnVideoMediaOpened;
        _videoElement.Stop();
        _videoElement.Source = null;
        _videoElement = null;
    }

    public bool CanPlay(string path)
    {
        if (!File.Exists(path))
        {
            return false;
        }

        return NexusMediaLibraryService.SupportedExtensions.Contains(Path.GetExtension(path));
    }

    public void Open(string path, NexusMediaKind kind)
    {
        if (!CanPlay(path))
        {
            PlaybackError?.Invoke(this, _library.GetUnsupportedFormatMessage(path));
            return;
        }

        StopInternal();
        _currentPath = path;
        _currentKind = kind;
        _position = TimeSpan.Zero;
        _duration = TimeSpan.Zero;

        if (kind == NexusMediaKind.Video)
        {
            OpenVideo(path);
        }
        else
        {
            OpenAudio(path);
        }

        RaiseStateChanged();
    }

    public void Play()
    {
        if (string.IsNullOrWhiteSpace(_currentPath))
        {
            return;
        }

        if (_currentKind == NexusMediaKind.Video)
        {
            _videoElement?.Play();
        }
        else
        {
            _audioPlayer.Play();
        }

        _isPlaying = true;
        StartPositionTimer();
        RaiseStateChanged();
    }

    public void Pause()
    {
        if (_currentKind == NexusMediaKind.Video)
        {
            _videoElement?.Pause();
        }
        else
        {
            _audioPlayer.Pause();
        }

        _isPlaying = false;
        RaiseStateChanged();
    }

    public void Stop()
    {
        StopInternal();
        RaiseStateChanged();
    }

    public void Seek(TimeSpan position)
    {
        _position = position;
        if (_currentKind == NexusMediaKind.Video && _videoElement is not null)
        {
            _videoElement.Position = position;
        }
        else if (_currentKind == NexusMediaKind.Audio)
        {
            _audioPlayer.Position = position;
        }

        RaiseStateChanged();
    }

    public void SeekFraction(double fraction)
    {
        if (_duration <= TimeSpan.Zero)
        {
            return;
        }

        var clamped = Math.Clamp(fraction, 0, 1);
        Seek(TimeSpan.FromTicks((long)(_duration.Ticks * clamped)));
    }

    public void SetVolume(double volume)
    {
        _volume = Math.Clamp(volume, 0, 1);
        if (_videoElement is not null)
        {
            _videoElement.Volume = _volume;
        }

        _audioPlayer.Volume = _volume;
        RaiseStateChanged();
    }

    public void ToggleShuffle() => ShuffleEnabled = !ShuffleEnabled;

    public void CycleRepeatMode()
    {
        RepeatMode = RepeatMode switch
        {
            NexusRepeatMode.Off => NexusRepeatMode.All,
            NexusRepeatMode.All => NexusRepeatMode.One,
            _ => NexusRepeatMode.Off,
        };
        RaiseStateChanged();
    }

    public void RequestFullscreenToggle() => RequestFullscreen?.Invoke(this, EventArgs.Empty);

    public (int Width, int Height) GetVideoDimensions()
    {
        if (_currentKind == NexusMediaKind.Video && _videoElement is not null &&
            _videoElement.NaturalVideoWidth > 0)
        {
            return ((int)_videoElement.NaturalVideoWidth, (int)_videoElement.NaturalVideoHeight);
        }

        return (0, 0);
    }

    public void Dispose()
    {
        StopPositionTimer();
        DetachVideoElement();
        _audioPlayer.Close();
    }

    private void OpenVideo(string path)
    {
        if (_videoElement is null)
        {
            PlaybackError?.Invoke(this, "Videooverflade er ikke tilkoblet.");
            return;
        }

        _videoElement.Source = new Uri(path);
        _videoElement.Play();
        _isPlaying = true;
        StartPositionTimer();
    }

    private void OpenAudio(string path)
    {
        _audioPlayer.Open(new Uri(path));
        _audioPlayer.Play();
        _isPlaying = true;
        StartPositionTimer();
    }

    private void OnVideoMediaOpened(object? sender, RoutedEventArgs e)
    {
        if (_videoElement is null)
        {
            return;
        }

        if (_videoElement.NaturalDuration.HasTimeSpan)
        {
            _duration = _videoElement.NaturalDuration.TimeSpan;
        }

        MediaOpened?.Invoke(this, EventArgs.Empty);
        RaiseStateChanged();
    }

    private void OnAudioOpened()
    {
        if (_audioPlayer.NaturalDuration.HasTimeSpan)
        {
            _duration = _audioPlayer.NaturalDuration.TimeSpan;
        }

        MediaOpened?.Invoke(this, EventArgs.Empty);
        RaiseStateChanged();
    }

    private void StopInternal()
    {
        _isPlaying = false;
        StopPositionTimer();

        if (_videoElement is not null)
        {
            _videoElement.Stop();
            _videoElement.Source = null;
        }

        _audioPlayer.Stop();
        _currentPath = null;
        _position = TimeSpan.Zero;
        _duration = TimeSpan.Zero;
    }

    private void StartPositionTimer()
    {
        StopPositionTimer();
        _positionTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(250) };
        _positionTimer.Tick += (_, _) => UpdatePosition();
        _positionTimer.Start();
    }

    private void StopPositionTimer()
    {
        if (_positionTimer is null)
        {
            return;
        }

        _positionTimer.Stop();
        _positionTimer = null;
    }

    private void UpdatePosition()
    {
        if (_currentKind == NexusMediaKind.Video && _videoElement is not null)
        {
            _position = _videoElement.Position;
            if (_videoElement.NaturalDuration.HasTimeSpan)
            {
                _duration = _videoElement.NaturalDuration.TimeSpan;
            }
        }
        else if (_currentKind == NexusMediaKind.Audio)
        {
            _position = _audioPlayer.Position;
            if (_audioPlayer.NaturalDuration.HasTimeSpan)
            {
                _duration = _audioPlayer.NaturalDuration.TimeSpan;
            }
        }

        RaiseStateChanged();
    }

    private void RaiseStateChanged() => StateChanged?.Invoke(this, EventArgs.Empty);
}
