using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Threading;
using NexusShell.Services;
using NexusShell.Services.Diagnostics;

namespace NexusShell.Controls;

public partial class NexusDesktopParticleField : UserControl
{
    private sealed record Particle(Ellipse Shape, double Vx, double Vy);

    private readonly List<Particle> _particles = [];
    private readonly Random _random = new();
    private DispatcherTimer? _timer;
    private NexusTimerRegistration? _timerRegistration;
    private double _width;
    private double _height;

    private static readonly Color ColorPurple = Color.FromRgb(0x8A, 0x4F, 0xFF);
    private static readonly Color ColorBlue = Color.FromRgb(0x00, 0xD9, 0xFF);

    public NexusDesktopParticleField()
    {
        InitializeComponent();
        Loaded += OnLoaded;
        Unloaded += OnUnloaded;
        SizeChanged += OnSizeChanged;
    }

    private void OnLoaded(object sender, RoutedEventArgs e)
    {
        EnsureParticles();
        StartAnimation();
    }

    private void OnUnloaded(object sender, RoutedEventArgs e)
    {
        _timer?.Stop();
        _timerRegistration?.Dispose();
        _timerRegistration = null;
        _timer = null;
    }

    private void OnSizeChanged(object sender, SizeChangedEventArgs e)
    {
        _width = e.NewSize.Width;
        _height = e.NewSize.Height;

        if (_particles.Count == 0)
        {
            EnsureParticles();
        }
    }

    private void EnsureParticles()
    {
        if (_width <= 0 || _height <= 0)
        {
            return;
        }

        if (_particles.Count > 0)
        {
            return;
        }

        var count = ShouldReduceMotion() ? _random.Next(12, 20) : _random.Next(55, 78);
        ParticleCanvas.Children.Clear();
        _particles.Clear();

        for (var i = 0; i < count; i++)
        {
            var size = _random.Next(1, 3);
            var opacity = _random.NextDouble() * 0.23 + 0.12;
            var color = _random.NextDouble() > 0.5 ? ColorPurple : ColorBlue;

            var ellipse = new Ellipse
            {
                Width = size,
                Height = size,
                Fill = new SolidColorBrush(color) { Opacity = opacity },
            };

            Canvas.SetLeft(ellipse, _random.NextDouble() * _width);
            Canvas.SetTop(ellipse, _random.NextDouble() * _height);
            ParticleCanvas.Children.Add(ellipse);

            var speed = _random.NextDouble() * 1.5 + 0.5;
            var angle = _random.NextDouble() * Math.PI * 2;
            _particles.Add(new Particle(ellipse, Math.Cos(angle) * speed, Math.Sin(angle) * speed));
        }
    }

    private void StartAnimation()
    {
        _timer?.Stop();
        _timerRegistration?.Dispose();
        _timerRegistration = null;
        if (ShouldReduceMotion())
        {
            return;
        }

        var intervalMs = 50;
        _timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(intervalMs) };
        _timer.Tick += (_, _) => TickParticles();
        _timer.Start();
        _timerRegistration = NexusTimerRegistry.Register(
            "desktop-particles",
            nameof(NexusDesktopParticleField),
            _timer.Interval,
            uiThread: true,
            "Animated desktop particle background");
    }

    private static bool ShouldReduceMotion() => NexusMotionPreferences.ShouldReduceMotion();

    private void TickParticles()
    {
        if (_width <= 0 || _height <= 0)
        {
            return;
        }

        var dt = 0.033;

        foreach (var particle in _particles)
        {
            var left = Canvas.GetLeft(particle.Shape);
            var top = Canvas.GetTop(particle.Shape);
            left += particle.Vx * dt;
            top += particle.Vy * dt;

            if (left < -4)
            {
                left = _width + 2;
            }
            else if (left > _width + 4)
            {
                left = -2;
            }

            if (top < -4)
            {
                top = _height + 2;
            }
            else if (top > _height + 4)
            {
                top = -2;
            }

            Canvas.SetLeft(particle.Shape, left);
            Canvas.SetTop(particle.Shape, top);
        }
    }
}
