using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Animation;
using Microsoft.UI.Xaml.Shapes;
using Windows.Foundation;
using Windows.UI.ViewManagement;
using YMhut.Box.WinUI.Services;

namespace YMhut.Box.WinUI.Controls;

public sealed class AnimatedWeatherIconControl : UserControl
{
    private readonly Canvas _canvas = new();
    private readonly AccessibilitySettings _accessibility = new();
    private Storyboard? _storyboard;
    private WeatherVisualKind _kind = WeatherVisualKind.Unknown;
    private WeatherIntensity _intensity = WeatherIntensity.None;
    private bool _animationEnabled = true;

    public AnimatedWeatherIconControl(double size = 20)
    {
        Width = size;
        Height = size;
        MinWidth = size;
        MinHeight = size;
        _canvas.Width = size;
        _canvas.Height = size;
        _canvas.IsHitTestVisible = false;
        Content = _canvas;
        Loaded += (_, _) => Render();
        SizeChanged += (_, _) => Render();
        Unloaded += (_, _) => StopStoryboard();
    }

    public void Update(TitleWeatherSnapshot snapshot, bool animationEnabled)
    {
        _kind = snapshot.VisualKind;
        _intensity = snapshot.Intensity;
        _animationEnabled = animationEnabled;
        Render();
    }

    private void Render()
    {
        StopStoryboard();
        _canvas.Children.Clear();

        var size = Math.Max(16, ActualWidth > 0 ? ActualWidth : Width > 0 ? Width : 20);
        _canvas.Width = size;
        _canvas.Height = size;
        _canvas.Clip = new RectangleGeometry { Rect = new Rect(0, 0, size, size) };
        _storyboard = new Storyboard();

        switch (_kind)
        {
            case WeatherVisualKind.Clear:
                AddSun(size, 0.5, 0.5, 0.23, animate: true);
                break;
            case WeatherVisualKind.PartlyCloudy:
                AddSun(size, 0.34, 0.35, 0.18, animate: true, opacity: 0.9);
                AddCloud(size, 0.20, 0.38, 0.68, 0.38, opacity: 0.95, animate: true);
                break;
            case WeatherVisualKind.Cloudy:
                AddCloud(size, 0.13, 0.34, 0.74, 0.42, opacity: 1, animate: true);
                break;
            case WeatherVisualKind.Fog:
                AddCloud(size, 0.16, 0.25, 0.70, 0.34, opacity: 0.9, animate: false);
                AddFog(size);
                break;
            case WeatherVisualKind.Drizzle:
            case WeatherVisualKind.Rain:
            case WeatherVisualKind.FreezingRain:
            case WeatherVisualKind.Showers:
                AddCloud(size, 0.14, 0.20, 0.72, 0.36, opacity: 1, animate: false);
                AddRain(size, DropsFor(_intensity), _kind == WeatherVisualKind.Drizzle);
                break;
            case WeatherVisualKind.Snow:
            case WeatherVisualKind.SnowGrains:
            case WeatherVisualKind.SnowShowers:
                AddCloud(size, 0.14, 0.20, 0.72, 0.36, opacity: 1, animate: false);
                AddSnow(size, DropsFor(_intensity));
                break;
            case WeatherVisualKind.Thunderstorm:
                AddCloud(size, 0.14, 0.18, 0.72, 0.36, opacity: 1, animate: false);
                AddRain(size, Math.Max(3, DropsFor(_intensity)), drizzle: false);
                AddBolt(size);
                break;
            default:
                AddCloud(size, 0.18, 0.30, 0.64, 0.38, opacity: 0.72, animate: true);
                AddUnknownMark(size);
                break;
        }

        if (CanAnimate() && _storyboard.Children.Count > 0)
        {
            _storyboard.Begin();
        }
    }

    private void AddSun(double size, double cx, double cy, double radius, bool animate, double opacity = 1)
    {
        var centerX = size * cx;
        var centerY = size * cy;
        var r = size * radius;
        var sun = AddEllipse(centerX - r, centerY - r, r * 2, r * 2, ModernUi.Bronze, opacity);

        for (var i = 0; i < 8; i++)
        {
            var angle = i * Math.PI / 4;
            var inner = r * 1.35;
            var outer = r * 1.75;
            AddLine(
                centerX + Math.Cos(angle) * inner,
                centerY + Math.Sin(angle) * inner,
                centerX + Math.Cos(angle) * outer,
                centerY + Math.Sin(angle) * outer,
                ModernUi.Bronze,
                Math.Max(1.1, size * 0.055),
                opacity * 0.72);
        }

        if (animate && CanAnimate())
        {
            AnimatePulse(sun, 0.76, 1, 1800);
        }
    }

    private void AddCloud(double size, double left, double top, double width, double height, double opacity, bool animate)
    {
        var x = size * left;
        var y = size * top;
        var w = size * width;
        var h = size * height;
        var fill = CloudBrush();
        var parts = new UIElement[]
        {
            AddEllipse(x + w * 0.05, y + h * 0.35, w * 0.32, h * 0.46, fill, opacity),
            AddEllipse(x + w * 0.24, y + h * 0.10, w * 0.36, h * 0.62, fill, opacity),
            AddEllipse(x + w * 0.48, y + h * 0.24, w * 0.34, h * 0.52, fill, opacity),
            AddRoundedRect(x + w * 0.10, y + h * 0.48, w * 0.74, h * 0.34, fill, opacity)
        };

        if (animate && CanAnimate())
        {
            foreach (var part in parts)
            {
                var transform = new TranslateTransform();
                part.RenderTransform = transform;
                AnimateDrift(transform, size * 0.035, 2300, 0);
            }
        }
    }

    private void AddFog(double size)
    {
        for (var index = 0; index < 3; index++)
        {
            var band = AddRoundedRect(
                size * (0.18 + index * 0.04),
                size * (0.58 + index * 0.12),
                size * (0.62 - index * 0.03),
                Math.Max(1.5, size * 0.065),
                ModernUi.TextSecondary,
                0.62);
            if (CanAnimate())
            {
                var transform = new TranslateTransform();
                band.RenderTransform = transform;
                AnimateDrift(transform, size * 0.06, 1800 + index * 180, index * 90);
            }
        }
    }

    private void AddRain(double size, int count, bool drizzle)
    {
        var duration = _intensity == WeatherIntensity.Heavy ? 520 : _intensity == WeatherIntensity.Moderate ? 720 : 940;
        for (var index = 0; index < count; index++)
        {
            var drop = AddRoundedRect(
                size * (0.28 + index * 0.12),
                size * (0.58 + index % 2 * 0.04),
                Math.Max(1.1, size * 0.055),
                size * (drizzle ? 0.14 : 0.22),
                ModernUi.Accent,
                0.82);
            RotateElement(drop, -15);
            if (CanAnimate())
            {
                var transform = new TranslateTransform();
                drop.RenderTransform = transform;
                AnimateDrop(transform, drop, size * 0.16, duration, index * 120);
            }
        }
    }

    private void AddSnow(double size, int count)
    {
        var duration = _intensity == WeatherIntensity.Heavy ? 780 : _intensity == WeatherIntensity.Moderate ? 980 : 1250;
        for (var index = 0; index < count; index++)
        {
            var flake = AddEllipse(
                size * (0.25 + index * 0.13),
                size * (0.60 + index % 2 * 0.06),
                Math.Max(2.2, size * 0.10),
                Math.Max(2.2, size * 0.10),
                ModernUi.Accent,
                0.78);
            if (CanAnimate())
            {
                var transform = new TranslateTransform();
                flake.RenderTransform = transform;
                AnimateDrop(transform, flake, size * 0.14, duration, index * 150);
            }
        }
    }

    private void AddBolt(double size)
    {
        var bolt = new Polyline
        {
            Stroke = ModernUi.Bronze,
            StrokeThickness = Math.Max(1.7, size * 0.08),
            StrokeLineJoin = PenLineJoin.Round,
            Points =
            {
                new Point(size * 0.52, size * 0.48),
                new Point(size * 0.42, size * 0.70),
                new Point(size * 0.55, size * 0.68),
                new Point(size * 0.48, size * 0.90)
            },
            Opacity = 0.95
        };
        _canvas.Children.Add(bolt);
        if (CanAnimate())
        {
            AnimatePulse(bolt, 0.28, 1, 620);
        }
    }

    private void AddUnknownMark(double size)
    {
        AddLine(size * 0.40, size * 0.44, size * 0.60, size * 0.64, ModernUi.TextSecondary, Math.Max(1.4, size * 0.07), 0.72);
        AddLine(size * 0.60, size * 0.44, size * 0.40, size * 0.64, ModernUi.TextSecondary, Math.Max(1.4, size * 0.07), 0.72);
    }

    private Ellipse AddEllipse(double left, double top, double width, double height, Brush fill, double opacity)
    {
        var shape = new Ellipse
        {
            Width = width,
            Height = height,
            Fill = fill,
            Opacity = opacity,
            RenderTransformOrigin = new Point(0.5, 0.5)
        };
        Canvas.SetLeft(shape, left);
        Canvas.SetTop(shape, top);
        _canvas.Children.Add(shape);
        return shape;
    }

    private Border AddRoundedRect(double left, double top, double width, double height, Brush fill, double opacity)
    {
        var border = new Border
        {
            Width = width,
            Height = height,
            CornerRadius = new CornerRadius(Math.Max(2, height / 2)),
            Background = fill,
            Opacity = opacity,
            RenderTransformOrigin = new Point(0.5, 0.5)
        };
        Canvas.SetLeft(border, left);
        Canvas.SetTop(border, top);
        _canvas.Children.Add(border);
        return border;
    }

    private Line AddLine(double x1, double y1, double x2, double y2, Brush stroke, double thickness, double opacity)
    {
        var line = new Line
        {
            X1 = x1,
            Y1 = y1,
            X2 = x2,
            Y2 = y2,
            Stroke = stroke,
            StrokeThickness = thickness,
            StrokeStartLineCap = PenLineCap.Round,
            StrokeEndLineCap = PenLineCap.Round,
            Opacity = opacity
        };
        _canvas.Children.Add(line);
        return line;
    }

    private void RotateElement(UIElement element, double angle)
    {
        element.RenderTransformOrigin = new Point(0.5, 0.5);
        element.RenderTransform = new RotateTransform { Angle = angle };
    }

    private bool CanAnimate()
        => _animationEnabled && !_accessibility.HighContrast;

    private void AnimatePulse(UIElement target, double from, double to, double durationMs)
    {
        var animation = new DoubleAnimation
        {
            From = from,
            To = to,
            AutoReverse = true,
            Duration = new Duration(TimeSpan.FromMilliseconds(durationMs)),
            RepeatBehavior = RepeatBehavior.Forever
        };
        Storyboard.SetTarget(animation, target);
        Storyboard.SetTargetProperty(animation, "Opacity");
        _storyboard?.Children.Add(animation);
    }

    private void AnimateDrop(TranslateTransform transform, UIElement target, double distance, double durationMs, double delayMs)
    {
        var move = new DoubleAnimation
        {
            From = -distance * 0.35,
            To = distance,
            BeginTime = TimeSpan.FromMilliseconds(delayMs),
            Duration = new Duration(TimeSpan.FromMilliseconds(durationMs)),
            RepeatBehavior = RepeatBehavior.Forever
        };
        Storyboard.SetTarget(move, transform);
        Storyboard.SetTargetProperty(move, "Y");
        _storyboard?.Children.Add(move);

        var fade = new DoubleAnimation
        {
            From = 0.2,
            To = 0.9,
            AutoReverse = true,
            BeginTime = TimeSpan.FromMilliseconds(delayMs),
            Duration = new Duration(TimeSpan.FromMilliseconds(durationMs * 0.55)),
            RepeatBehavior = RepeatBehavior.Forever
        };
        Storyboard.SetTarget(fade, target);
        Storyboard.SetTargetProperty(fade, "Opacity");
        _storyboard?.Children.Add(fade);
    }

    private void AnimateDrift(TranslateTransform transform, double distance, double durationMs, double delayMs)
    {
        var move = new DoubleAnimation
        {
            From = -distance,
            To = distance,
            AutoReverse = true,
            BeginTime = TimeSpan.FromMilliseconds(delayMs),
            Duration = new Duration(TimeSpan.FromMilliseconds(durationMs)),
            RepeatBehavior = RepeatBehavior.Forever
        };
        Storyboard.SetTarget(move, transform);
        Storyboard.SetTargetProperty(move, "X");
        _storyboard?.Children.Add(move);
    }

    private void StopStoryboard()
    {
        _storyboard?.Stop();
        _storyboard = null;
    }

    private static int DropsFor(WeatherIntensity intensity) => intensity switch
    {
        WeatherIntensity.Heavy => 5,
        WeatherIntensity.Moderate => 4,
        WeatherIntensity.Light => 3,
        _ => 2
    };

    private static Brush CloudBrush()
        => ModernUi.TextSecondary;
}
