using System.Diagnostics;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Avalonia.Threading;
using LanMountainDesktop.Launcher.Resources;
using LanMountainDesktop.Launcher.Services;

namespace LanMountainDesktop.Launcher.Views;

public partial class SplashWindow : Window, ISplashStageReporter
{
    private const int DebugModeClickThreshold = 5;
    private static readonly TimeSpan FadeAnimationDuration = TimeSpan.FromMilliseconds(160);

    private int _versionTextClickCount;
    private bool _isDebugModeOpened;
    private bool _isOpened;
    private bool _dismissed;

    public SplashWindow()
    {
        AvaloniaXamlLoader.Load(this);
        Loaded += OnWindowLoaded;
        Opened += OnWindowOpened;
    }

    private void OnWindowLoaded(object? sender, RoutedEventArgs e)
    {
        InitializeBackgroundImage();

        if (this.FindControl<Border>("VersionTextBorder") is { } versionBorder)
        {
            versionBorder.PointerPressed += OnVersionTextClick;
        }
    }

    private void InitializeBackgroundImage()
    {
        try
        {
            var imageInfo = LauncherBackgroundService.LoadBackgroundImage();
            if (imageInfo is { IsValid: true, Bitmap: not null })
            {
                if (this.FindControl<Image>("BackgroundImage") is { } backgroundImage)
                {
                    backgroundImage.Source = imageInfo.Bitmap;
                    backgroundImage.IsVisible = true;
                    backgroundImage.Opacity = 1;
                }
                Logger.Info("[SplashWindow] 背景图片加载成功");
            }
            else if (imageInfo is { Exists: true, IsValid: false })
            {
                Logger.Warn($"[SplashWindow] 背景图片校验失败: {imageInfo.ErrorMessage}");
            }
        }
        catch (Exception ex)
        {
            Logger.Warn($"[SplashWindow] 加载背景图片失败: {ex.Message}");
        }
    }

    private async void OnWindowOpened(object? sender, EventArgs e)
    {
        if (_isOpened)
        {
            return;
        }

        _isOpened = true;

        Opacity = 0d;
        await AnimateOpacityAsync(0d, 1d, FadeAnimationDuration).ConfigureAwait(false);
    }

    public async Task DismissAsync()
    {
        if (_dismissed)
        {
            return;
        }

        _dismissed = true;

        if (!Dispatcher.UIThread.CheckAccess())
        {
            await Dispatcher.UIThread.InvokeAsync(async () => await DismissAsync());
            return;
        }

        await AnimateOpacityAsync(Opacity, 0d, FadeAnimationDuration).ConfigureAwait(false);
        Close();
    }

    public void Report(string stage, string message)
    {
        Dispatcher.UIThread.Post(() =>
        {
            if (this.FindControl<TextBlock>("StatusText") is { } statusText)
            {
                statusText.Text = message;
            }

            if (this.FindControl<ProgressBar>("ProgressIndicator") is { } progressIndicator)
            {
                var progress = ResolveProgress(stage);
                if (progress > 0)
                {
                    progressIndicator.IsIndeterminate = false;
                    progressIndicator.Value = progress;
                }
                else
                {
                    progressIndicator.IsIndeterminate = true;
                }
            }
        });
    }

    public void ReportStage(string stage, int progress)
    {
        Dispatcher.UIThread.Post(() =>
        {
            if (this.FindControl<TextBlock>("StatusText") is { } statusText)
            {
                statusText.Text = stage;
            }

            if (this.FindControl<ProgressBar>("ProgressIndicator") is { } progressIndicator)
            {
                progressIndicator.IsIndeterminate = false;
                progressIndicator.Value = Math.Clamp(progress, 0, 100);
            }
        });
    }

    public void UpdateProgress(int percent, string? message = null)
    {
        Dispatcher.UIThread.Post(() =>
        {
            if (!string.IsNullOrWhiteSpace(message) &&
                this.FindControl<TextBlock>("StatusText") is { } statusText)
            {
                statusText.Text = message;
            }

            if (this.FindControl<ProgressBar>("ProgressIndicator") is { } progressIndicator)
            {
                progressIndicator.IsIndeterminate = false;
                progressIndicator.Value = Math.Clamp(percent, 0, 100);
            }
        });
    }

    public void UpdateStatus(string message)
    {
        Dispatcher.UIThread.Post(() =>
        {
            if (this.FindControl<TextBlock>("StatusText") is { } statusText)
            {
                statusText.Text = message;
            }
        });
    }

    public void SetVersionInfo(string version, string codename)
    {
        Dispatcher.UIThread.Post(() =>
        {
            if (this.FindControl<TextBlock>("VersionText") is { } versionText)
            {
                versionText.Text = $"{version} ({codename})";
            }
        });
    }

    public void SetDebugMode(bool isDebugMode)
    {
        if (!isDebugMode)
        {
            return;
        }

        UpdateStatus(Strings.Splash_DebugPreview);
    }

    private void OnVersionTextClick(object? sender, PointerPressedEventArgs e)
    {
        if (_isDebugModeOpened)
        {
            return;
        }

        _versionTextClickCount++;
        if (_versionTextClickCount >= DebugModeClickThreshold)
        {
            OpenDebugWindow();
        }
    }

    private async void OpenDebugWindow()
    {
        _isDebugModeOpened = true;

        try
        {
            var debugWindow = new ErrorDebugWindow(
                ErrorWindow.CheckDevModeEnabled(),
                ErrorWindow.GetSavedCustomHostPath())
            {
                WindowStartupLocation = WindowStartupLocation.CenterOwner
            };

            debugWindow.Closed += (_, _) =>
            {
                if (debugWindow.WasAccepted)
                {
                    LauncherDebugSettingsStore.Save(new LauncherDebugSettings(
                        debugWindow.IsDevModeEnabled,
                        debugWindow.SelectedHostPath));
                }

                _isDebugModeOpened = false;
                _versionTextClickCount = 0;
            };

            await debugWindow.ShowDialog(this);
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"[SplashWindow] Failed to open debug window: {ex}");
            _isDebugModeOpened = false;
            _versionTextClickCount = 0;
        }
    }

    private async Task AnimateOpacityAsync(double from, double to, TimeSpan duration)
    {
        await AnimateAsync(progress =>
        {
            Opacity = from + ((to - from) * progress);
        }, duration, EaseOutCubic).ConfigureAwait(false);
    }

    private async Task AnimateAsync(Action<double> update, TimeSpan duration, Func<double, double> easing)
    {
        if (duration <= TimeSpan.Zero)
        {
            await Dispatcher.UIThread.InvokeAsync(() => update(1d));
            return;
        }

        var stopwatch = Stopwatch.StartNew();
        while (stopwatch.Elapsed < duration)
        {
            var raw = stopwatch.Elapsed.TotalMilliseconds / duration.TotalMilliseconds;
            var progress = easing(Math.Clamp(raw, 0d, 1d));
            await Dispatcher.UIThread.InvokeAsync(() => update(progress));
            await Task.Delay(16).ConfigureAwait(false);
        }

        await Dispatcher.UIThread.InvokeAsync(() => update(1d));
    }

    private static int ResolveProgress(string stage)
    {
        return stage.ToLowerInvariant() switch
        {
            "initializing" => 10,
            "settings" => 25,
            "update" => 30,
            "plugins" => 50,
            "ui" => 65,
            "shell" => 80,
            "activation" => 90,
            "ready" => 100,
            _ => 0
        };
    }

    private static double EaseOutCubic(double value)
    {
        var inverse = 1d - value;
        return 1d - (inverse * inverse * inverse);
    }
}
