using System.Collections.Specialized;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Platform;
using Avalonia.Platform.Storage;
using Avalonia.VisualTree;
using Avalonia.Threading;
using OF_DL.Helpers;
using OF_DL.Gui.Helpers;
using OF_DL.Gui.ViewModels;
using Serilog;

namespace OF_DL.Gui.Views;

public partial class MainWindow : Window
{
    private bool _hasInitialized;
    private bool _activityLogAutoScroll = true;
    private bool _activityLogUserInteracted;
    private bool _isActivityLogProgrammaticScroll;
    private ScrollViewer? _activityLogScrollViewer;
    private CancellationTokenSource? _copyToastCancellationTokenSource;
    public MainWindowViewModel? ViewModel => DataContext as MainWindowViewModel;

    public MainWindow()
    {
        InitializeComponent();
        Icon = new WindowIcon(AssetLoader.Open(new Uri("avares://OF DL.Gui/Assets/icon.ico")));

        // Start maximized if running in Docker
        if (EnvironmentHelper.IsRunningInDocker())
        {
            WindowState = WindowState.Maximized;
        }

        Opened += OnOpened;
        Closed += OnClosed;
    }

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

        _hasInitialized = true;
        InitializeActivityLogAutoScroll();

        if (DataContext is MainWindowViewModel vm)
        {
            await vm.InitializeAsync();
        }
    }

    private void OnClosed(object? sender, EventArgs e)
    {
        if (ViewModel != null)
        {
            ViewModel.ActivityLog.CollectionChanged -= OnActivityLogCollectionChanged;
        }

        if (_activityLogScrollViewer != null)
        {
            _activityLogScrollViewer.ScrollChanged -= OnActivityLogScrollChanged;
        }

        ActivityLogListBox.PointerWheelChanged -= OnActivityLogPointerInteracted;
        ActivityLogListBox.PointerPressed -= OnActivityLogPointerInteracted;

        _copyToastCancellationTokenSource?.Cancel();
        _copyToastCancellationTokenSource?.Dispose();
        _copyToastCancellationTokenSource = null;
    }

    private async void OnBrowseFfmpegPathClick(object? sender, RoutedEventArgs e)
    {
        if (DataContext is not MainWindowViewModel vm)
        {
            return;
        }

        TopLevel? topLevel = GetTopLevel(this);
        if (topLevel?.StorageProvider == null)
        {
            return;
        }

        IReadOnlyList<IStorageFile> selectedFiles = await topLevel.StorageProvider.OpenFilePickerAsync(
            new FilePickerOpenOptions { Title = "Select FFmpeg executable", AllowMultiple = false });

        IStorageFile? selectedFile = selectedFiles.FirstOrDefault();
        if (selectedFile == null)
        {
            return;
        }

        string? localPath = selectedFile.TryGetLocalPath();
        if (!string.IsNullOrWhiteSpace(localPath))
        {
            vm.SetFfmpegPath(localPath);
            return;
        }

        vm.SetFfmpegPath(selectedFile.Name);
    }

    private async void OnBrowseFfprobePathClick(object? sender, RoutedEventArgs e)
    {
        if (DataContext is not MainWindowViewModel vm)
        {
            return;
        }

        TopLevel? topLevel = GetTopLevel(this);
        if (topLevel?.StorageProvider == null)
        {
            return;
        }

        IReadOnlyList<IStorageFile> selectedFiles = await topLevel.StorageProvider.OpenFilePickerAsync(
            new FilePickerOpenOptions { Title = "Select FFprobe executable", AllowMultiple = false });

        IStorageFile? selectedFile = selectedFiles.FirstOrDefault();
        if (selectedFile == null)
        {
            return;
        }

        string? localPath = selectedFile.TryGetLocalPath();
        if (!string.IsNullOrWhiteSpace(localPath))
        {
            vm.SetFfprobePath(localPath);
            return;
        }

        vm.SetFfprobePath(selectedFile.Name);
    }

    private async void OnBrowseDownloadPathClick(object? sender, RoutedEventArgs e)
    {
        if (DataContext is not MainWindowViewModel vm)
        {
            return;
        }

        TopLevel? topLevel = GetTopLevel(this);
        if (topLevel?.StorageProvider == null)
        {
            return;
        }

        IReadOnlyList<IStorageFolder> selectedFolders = await topLevel.StorageProvider.OpenFolderPickerAsync(
            new FolderPickerOpenOptions { Title = "Select download folder", AllowMultiple = false });

        IStorageFolder? selectedFolder = selectedFolders.FirstOrDefault();
        if (selectedFolder == null)
        {
            return;
        }

        string? localPath = selectedFolder.TryGetLocalPath();
        if (!string.IsNullOrWhiteSpace(localPath))
        {
            vm.SetDownloadPath(localPath);
            return;
        }

        vm.SetDownloadPath(selectedFolder.Name);
    }

    private async void OnJoinDiscordClick(object? sender, RoutedEventArgs e)
    {
        try
        {
            await WebLinkHelper.OpenOrCopyAsync(this, Constants.DiscordInviteUrl,
                dockerFeedbackAsync: ShowCopyToastAsync);
        }
        catch (Exception exception)
        {
            Log.Error(exception, "Failed to handle link click event. {ErrorMessage}", exception.Message);
        }
    }

    private async void OnDocumentationClick(object? sender, RoutedEventArgs e)
    {
        try
        {
            await WebLinkHelper.OpenOrCopyAsync(this, Constants.DocumentationUrl,
                dockerFeedbackAsync: ShowCopyToastAsync);
        }
        catch (Exception exception)
        {
            Log.Error(exception, "Failed to handle link click event. {ErrorMessage}", exception.Message);
        }
    }

    private async void OnAuthLegacyMethodsClick(object? sender, RoutedEventArgs e)
    {
        try
        {
            await WebLinkHelper.OpenOrCopyAsync(this, Constants.LegacyAuthDocumentationUrl,
                dockerFeedbackAsync: ShowCopyToastAsync);
        }
        catch (Exception exception)
        {
            Log.Error(exception, "Failed to handle link click event. {ErrorMessage}", exception.Message);
        }
    }

    private void OnFaqClick(object? sender, RoutedEventArgs e)
    {
        FaqWindow faqWindow = new() { WindowStartupLocation = WindowStartupLocation.CenterOwner };
        faqWindow.Show(this);
    }

    private void OnAboutClick(object? sender, RoutedEventArgs e)
    {
        if (DataContext is not MainWindowViewModel vm)
        {
            return;
        }

        AboutWindow aboutWindow = new(vm.ProgramVersion, vm.FfmpegVersion, vm.FfprobeVersion)
        {
            WindowStartupLocation = WindowStartupLocation.CenterOwner
        };
        aboutWindow.Show(this);
    }

    private async Task ShowCopyToastAsync(string message)
    {
        if (_copyToastCancellationTokenSource != null)
        {
            await _copyToastCancellationTokenSource.CancelAsync();
            _copyToastCancellationTokenSource.Dispose();
        }

        CancellationTokenSource cancellationTokenSource = new();
        _copyToastCancellationTokenSource = cancellationTokenSource;

        CopyToastTextBlock.Text = message;
        CopyToastBorder.Opacity = 0;
        CopyToastBorder.IsVisible = true;
        CopyToastBorder.Opacity = 1;

        try
        {
            await Task.Delay(TimeSpan.FromSeconds(2), cancellationTokenSource.Token);
        }
        catch (OperationCanceledException)
        {
            return;
        }

        CopyToastBorder.Opacity = 0;

        try
        {
            await Task.Delay(TimeSpan.FromMilliseconds(180), cancellationTokenSource.Token);
        }
        catch (OperationCanceledException)
        {
            return;
        }

        if (!cancellationTokenSource.IsCancellationRequested)
        {
            CopyToastBorder.IsVisible = false;
        }
    }

    private void OnModalOverlayClicked(object? sender, PointerPressedEventArgs e)
    {
        // Only handle clicks on the overlay itself (the Grid background)
        if (DataContext is not MainWindowViewModel vm)
        {
            return;
        }

        // Execute cancel command on any open modal
        vm.CreatorConfigEditor.ModalViewModel.CancelCommand.Execute(null);
        vm.CancelSinglePostOrMessageCommand.Execute(null);
        vm.CancelDownloadSelectionWarningCommand.Execute(null);
        vm.CancelMissingCdmWarningCommand.Execute(null);
    }

    private void OnModalContentClicked(object? sender, PointerPressedEventArgs e) =>
        // Stop the event from bubbling up to the overlay
        e.Handled = true;

    private void InitializeActivityLogAutoScroll()
    {
        if (ViewModel != null)
        {
            ViewModel.ActivityLog.CollectionChanged -= OnActivityLogCollectionChanged;
            ViewModel.ActivityLog.CollectionChanged += OnActivityLogCollectionChanged;
        }

        _activityLogScrollViewer = ActivityLogListBox.GetVisualDescendants().OfType<ScrollViewer>().FirstOrDefault();
        if (_activityLogScrollViewer != null)
        {
            _activityLogScrollViewer.ScrollChanged -= OnActivityLogScrollChanged;
            _activityLogScrollViewer.ScrollChanged += OnActivityLogScrollChanged;
        }

        ActivityLogListBox.PointerWheelChanged -= OnActivityLogPointerInteracted;
        ActivityLogListBox.PointerWheelChanged += OnActivityLogPointerInteracted;
        ActivityLogListBox.PointerPressed -= OnActivityLogPointerInteracted;
        ActivityLogListBox.PointerPressed += OnActivityLogPointerInteracted;
    }

    private void OnActivityLogCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e)
    {
        if (!_activityLogAutoScroll)
        {
            return;
        }

        Dispatcher.UIThread.Post(ScrollActivityLogToBottom);
    }

    private void OnActivityLogPointerInteracted(object? sender, PointerEventArgs e) =>
        _activityLogUserInteracted = true;

    private void OnActivityLogScrollChanged(object? sender, ScrollChangedEventArgs e)
    {
        if (_isActivityLogProgrammaticScroll || _activityLogScrollViewer == null || !_activityLogUserInteracted)
        {
            return;
        }

        _activityLogAutoScroll = IsAtBottom(_activityLogScrollViewer);
        _activityLogUserInteracted = false;
    }

    private void ScrollActivityLogToBottom()
    {
        if (ViewModel == null || ViewModel.ActivityLog.Count == 0)
        {
            return;
        }

        _isActivityLogProgrammaticScroll = true;
        try
        {
            ActivityLogListBox.ScrollIntoView(ViewModel.ActivityLog[^1]);
        }
        finally
        {
            _isActivityLogProgrammaticScroll = false;
        }
    }

    private static bool IsAtBottom(ScrollViewer scrollViewer, double tolerance = 2) =>
        scrollViewer.Offset.Y + scrollViewer.Viewport.Height >= scrollViewer.Extent.Height - tolerance;
}
