using Google.Apis.YouTube.v3.Data;
using Microsoft.AppCenter;
using Microsoft.AppCenter.Analytics;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Xml;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.ApplicationModel.Background;
using Windows.Globalization;
using Windows.Storage;
using Windows.System.Power;
using Windows.UI.Notifications;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

namespace FoxTube
{
    sealed partial class App : Application
    {
        public static string[] AvailableLanguages => new[] { "en-US", "ru-RU" };

        Stopwatch sw = new Stopwatch();
        public App()
        {
            SettingsStorage.LoadData();
            
            switch (SettingsStorage.Theme)
            {
                case 0:
                    RequestedTheme = ApplicationTheme.Light;
                    break;
                case 1:
                    RequestedTheme = ApplicationTheme.Dark;
                    break;
            }
            ApplicationLanguages.PrimaryLanguageOverride = SettingsStorage.Language;

            CheckVersion();

            InitializeComponent();
            Suspending += OnSuspending;
            UnhandledException += UnhandledError;

            AppCenter.Start("45774462-9ea7-438a-96fc-03982666f39e", typeof(Analytics));
            AppCenter.SetCountryCode(SettingsStorage.Region);

            sw.Start();
        }

        /// <summary>
        /// Comparing current version with last recorded version. If doesn't match, poping up changelog notification
        /// </summary>
        public async void CheckVersion()
        {
            PackageVersion ver = Package.Current.Id.Version;
            if (SettingsStorage.Version != $"{ver.Major}.{ver.Minor}.{ver.Build}")
            {
                try
                {
                    XmlDocument changelog = new XmlDocument();
                    StorageFile file = await (await Package.Current.InstalledLocation.GetFolderAsync(@"Assets\Data")).GetFileAsync("Patchnotes.xml");
                    changelog.Load(await file.OpenStreamForReadAsync());
                    XmlElement e = changelog["items"].ChildNodes[0] as XmlElement;

                    ToastNotificationManager.CreateToastNotifier().Show(Background.Notification.GetChangelogToast(e.GetAttribute("version")));

                    SettingsStorage.Version = $"{ver.Major}.{ver.Minor}.{ver.Build}";
                }
                catch (Exception e)
                {
                    Analytics.TrackEvent("Unable to retrieve changelog", new Dictionary<string, string>
                    {
                        { "Exception", e.GetType().ToString() },
                        { "Message", e.Message },
                        { "App version", $"{ver.Major}.{ver.Minor}.{ver.Revision}.{ver.Build}" },
                        { "StackTrace", e.StackTrace }
                    });
                }
            }
        }

        protected override void OnLaunched(LaunchActivatedEventArgs e)
        {
            if (!(Window.Current.Content is Frame rootFrame))
            {
                rootFrame = new Frame();
                rootFrame.NavigationFailed += OnNavigationFailed;
                Window.Current.Content = rootFrame;
            }

            if (e.PrelaunchActivated == false)
            {
                if (rootFrame.Content == null)
                    rootFrame.Navigate(typeof(MainPage), e.Arguments);

                Window.Current.Activate();
            }
            
            ActivateToastBackgoundTask();
            ActivateBackgoundTask();
        }

        /// <summary>
        /// Initializes background task for processing toast notifications' clicks
        /// </summary>
        public async void ActivateToastBackgoundTask()
        {
            const string taskName = "FoxtubeToastBackground";
            if (BackgroundTaskRegistration.AllTasks.Any(i => i.Value.Name.Equals(taskName)))
                return;

            var backgroundRequest = await BackgroundExecutionManager.RequestAccessAsync();
            if (backgroundRequest == BackgroundAccessStatus.DeniedBySystemPolicy || backgroundRequest == BackgroundAccessStatus.DeniedByUser)
                return;

            BackgroundTaskBuilder builder = new BackgroundTaskBuilder() { Name = taskName };
            builder.SetTrigger(new ToastNotificationActionTrigger());
            
            BackgroundTaskRegistration registration = builder.Register();
        }

        /// <summary>
        /// Initializes background task for checking user's subscriptions and poping toast notifications when new video is uploaded
        /// </summary>
        public async void ActivateBackgoundTask()
        {
            const string taskName = "FoxtubeBackgound";
            if (BackgroundTaskRegistration.AllTasks.Any(i => i.Value.Name.Equals(taskName)))
                return;

            var backgroundRequest = await BackgroundExecutionManager.RequestAccessAsync();
            var saverRequest = PowerManager.EnergySaverStatus;
            if (backgroundRequest == BackgroundAccessStatus.DeniedBySystemPolicy || backgroundRequest == BackgroundAccessStatus.DeniedByUser || saverRequest == EnergySaverStatus.On)
                return;

            BackgroundTaskBuilder builder = new BackgroundTaskBuilder()
            {
                Name = taskName,
                IsNetworkRequested = true,
                TaskEntryPoint = "FoxTube.Background.BackgroundProcessor"
            };
            builder.SetTrigger(new TimeTrigger(15, false));

            BackgroundTaskRegistration registration = builder.Register();
        }

        protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args)
        {
            var deferral = args.TaskInstance.GetDeferral();
            base.OnBackgroundActivated(args);

            if (args.TaskInstance.Task.Name == "FoxtubeToastBackground" && args.TaskInstance.TriggerDetails is ToastNotificationActionTriggerDetail details)
            {
                string[] arguments = details.Argument.Split('|');

                switch (arguments[0])
                {
                    case "later":
                        try
                        {
                            SecretsVault.AuthorizationStateChanged += async (s, e) =>
                            {
                                if ((bool)e[0])
                                {
                                    PlaylistItem item = new PlaylistItem()
                                    {
                                        Snippet = new PlaylistItemSnippet()
                                        {
                                            ResourceId = new ResourceId()
                                            {
                                                Kind = "youtube#video",
                                                VideoId = arguments[1]
                                            },
                                            PlaylistId = "WL"
                                        }
                                    };

                                    await SecretsVault.Service.PlaylistItems.Insert(item, "snippet").ExecuteAsync();
                                }
                            };
                            SecretsVault.CheckAuthorization(false);
                        }
                        catch (Exception e)
                        {
                            Analytics.TrackEvent("Failed to add video to WL from toast", new Dictionary<string, string>
                            {
                                { "Exception", e.GetType().ToString() },
                                { "Message", e.Message },
                                { "Video ID", arguments[1] },
                                { "StackTrace", e.StackTrace }
                            });
                        }
                        break;
                }
            }

            deferral.Complete();
        }

        protected override void OnActivated(IActivatedEventArgs e)
        {
            base.OnActivated(e);

            if (!(Window.Current.Content is Frame rootFrame))
            {
                rootFrame = new Frame();
                rootFrame.NavigationFailed += OnNavigationFailed;

                Window.Current.Content = rootFrame;
            }

            if (rootFrame.Content == null)
                rootFrame.Navigate(typeof(MainPage));

            Window.Current.Activate();

            switch (e.Kind)
            {
                case ActivationKind.ToastNotification:
                    if (SecretsVault.IsAuthorized)
                        ProcessToast((e as ToastNotificationActivatedEventArgs).Argument);
                    else
                        SecretsVault.AuthorizationStateChanged += (s, arg) => ProcessToast((e as ToastNotificationActivatedEventArgs).Argument);
                    break;
            }
        }

        private void ProcessToast(string arg)
        {
            string[] args = arg.Split('|');
            switch (args[0])
            {
                case "changelog":
                case "inbox":
                    Methods.MainPage.GoToDeveloper(args[1]);
                    break;

                case "video":
                    Methods.MainPage.GoToVideo(args[1]);
                    break;

                case "channel":
                    Methods.MainPage.GoToChannel(args[1]);
                    break;

                case "download":
                    Methods.MainPage.GoToDownloads();
                    break;
                case "dcancel":
                    DownloadAgent.Cancel(args[1]);
                    break;
                case "clipboard":
                    switch (args[1])
                    {
                        case "video":
                            Methods.MainPage.GoToVideo(args[2]);
                            break;
                        case "channel":
                            Methods.MainPage.GoToChannel(args[2]);
                            break;
                        case "playlist":
                            Methods.MainPage.GoToPlaylist(args[2]);
                            break;
                    }
                    break;
            }
        }
        
        void OnNavigationFailed(object sender, NavigationFailedEventArgs e)
        {
            throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
        }
        
        private void OnSuspending(object sender, SuspendingEventArgs e)
        {
            var deferral = e.SuspendingOperation.GetDeferral();

            sw.Stop();
            SettingsStorage.Uptime += sw.Elapsed;

            HistorySet.Save();
            SettingsStorage.SaveData();
            DownloadAgent.QuitPrompt();
            Controls.Player.ManifestGenerator.ClearRoaming();
            deferral.Complete();
            Analytics.TrackEvent("Session terminated");
        }

        private void UnhandledError(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e)
        {
            Analytics.TrackEvent("The app crashed", new Dictionary<string, string>()
            {
                { "Exception", e.Exception.GetType().ToString() },
                { "Details", e.Message },
                { "StackTrace", e.Exception.StackTrace }
            });
        }
    }
}
