using FoxTube.Pages;
using Microsoft.AppCenter.Analytics;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Mail;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Xml;
using Windows.ApplicationModel.Core;
using Windows.ApplicationModel.DataTransfer;
using Windows.ApplicationModel.Resources;
using Windows.Storage.Streams;
using Windows.System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Documents;
using YoutubeExplode;
using YoutubeExplode.Models.MediaStreams;

namespace FoxTube
{
    public delegate void Event();

    public delegate void ObjectEventHandler(object sender = null, params object[] args);

    public interface INavigationPage
    {
        object Parameter { get; set; }
    }

    public static class Methods
    {
        private static readonly ResourceLoader resources = ResourceLoader.GetForCurrentView("Methods");
        
        public static CommentsPage CommentsPage { get; set; }
        public static MainPage MainPage => (Window.Current.Content as Frame).Content as MainPage;

        public static void CloseApp()
        {
            CoreApplication.Exit();
        }

        public static Uri ToUri(this string url)
        {
            try { return string.IsNullOrWhiteSpace(url) ? null : new Uri(url); }
            catch { return null; }
        }

        public static string GuardFromNull(string str)
        {
            return str ?? string.Empty;
        }

        public static void SendMail(string content)
        {
            MailMessage msg = new MailMessage();
            msg.To.Add("michael.xfox@outlook.com");
            msg.From = new MailAddress(SecretsVault.EmailCredential.UserName);
            msg.Subject = "[Automatic message] FoxTube service message";
            msg.Body = content;

            SmtpClient client = new SmtpClient("smtp.gmail.com", 587);
            client.EnableSsl = true;
            client.Credentials = SecretsVault.EmailCredential;
            client.Send(msg);
        }

        [Obsolete]
        public static string GetChars(this string str, int count)
        {
            try
            {
                string s = "";
                for (int i = 0; i < count; i++)
                    s += str[i];
                return s;
            }
            catch
            {
                return "";
            }
        }

        public static List<object> ToReversedList(this Array array)
        {
            List<object> list = new List<object>();
            foreach (object i in array)
                list.Add(i);
            list.Reverse();
            return list;
        }

        public static void ForEach<T>(this IEnumerable<T> array, Action<T> action)
        {
            array.ToList().ForEach(action);
        }

        public static T Find<T>(this IEnumerable<T> array, Predicate<T> match)
        {
            return array.ToList().Find(match);
        }

        public static List<T> FindAll<T>(this IEnumerable<T> array, Predicate<T> match)
        {
            return array.ToList().FindAll(match);
        }

        [Obsolete]
        public static string ReplaceInvalidChars(this string str, char newValue)
        {
            foreach (char i in Path.GetInvalidFileNameChars())
                str = str.Replace(i, newValue);
            return str;
        }

        [Obsolete]
        public static string Last(this string[] arr)
        {
            return arr[arr.Length - 1];
        }

        public static TimeSpan GetDuration(this string str)
        {
            try
            {
                return XmlConvert.ToTimeSpan(str);
            }
            catch (FormatException)
            {
                TimeSpan time = XmlConvert.ToTimeSpan("PT" + str.Split('T')[1]);
                TimeSpan date = TimeSpan.FromDays(int.Parse(str.Split('W')[0].Remove('P')) * 7);
                date.Add(time);

                return date;
            }
            catch (Exception e)
            {
                Analytics.TrackEvent("Failed to parse duration", new Dictionary<string, string>
                {
                    { "Exception", e.GetType().ToString() },
                    { "Message", e.Message },
                    { "StackTrace", e.StackTrace }
                });
                return TimeSpan.FromMilliseconds(0);
            }
        }

        public static string GetAgo(DateTime dateTime)
        {
            TimeSpan span = DateTime.Now - dateTime;

            if (span.TotalMinutes < 1)
                return resources.GetString("/Methods/now");
            else if (Math.Round(span.TotalMinutes) == 1)
                return resources.GetString("/Methods/oneMinute");
            else if (span.TotalMinutes < 60)
                return Math.Round(span.TotalMinutes) + " " + resources.GetString("/Methods/minutes");
            else if (Math.Round(span.TotalHours) == 1)
                return resources.GetString("/Methods/oneHr");
            else if (span.TotalHours < 24)
                return Math.Round(span.TotalHours) + " " + resources.GetString("/Methods/hrs");
            else if (Math.Round(span.TotalDays) == 1)
                return resources.GetString("/Methods/oneDay");
            else if (span.TotalDays < 7)
                return Math.Round(span.TotalDays) + " " + resources.GetString("/Methods/days");
            else if (Math.Round(span.TotalDays) == 7)
                return resources.GetString("/Methods/oneWeek");
            else if (span.TotalDays < 30)
                return Math.Round(span.TotalDays / 7) + " " + resources.GetString("/Methods/weeks");
            else if (Math.Round(span.TotalDays) == 30)
                return resources.GetString("/Methods/oneMonth");
            else if (Math.Round(span.TotalDays) < 365)
                return Math.Round(span.TotalDays / 30) + " " + resources.GetString("/Methods/months");
            else if (Math.Round(span.TotalDays / 365) == 365)
                return resources.GetString("/Methods/oneYear");
            else
                return Math.Round(span.TotalDays / 365) + " " + resources.GetString("/Methods/years");
        }

        public static void FormatText(this TextBlock block, string text)
        {
            block.Inlines.Clear();
            Regex filter = new Regex(@"\b((?:https?://|www\.)\S+)|(\S+@\S+)\b", RegexOptions.IgnoreCase);
            Regex link = new Regex(@"\b(?:https?://|www\.)\S+\b", RegexOptions.IgnoreCase);
            Regex mail = new Regex(@"\b\S+@\S+\b", RegexOptions.IgnoreCase);

            foreach (string item in filter.Split(text))
            {
                if (link.IsMatch(item))
                {
                    string str = item;
                    if (!str.Contains("http"))
                        str = str.Insert(0, "http://");

                    Hyperlink hl = new Hyperlink();
                    hl.Click += (s, arg) => ProcessLink(str);
                    hl.Inlines.Add(new Run { Text = item });
                    block.Inlines.Add(hl);
                }
                else if (mail.IsMatch(item))
                {
                    Hyperlink hl = new Hyperlink { NavigateUri = $"mailto:{item}".ToUri() };
                    hl.Inlines.Add(new Run { Text = item });
                    block.Inlines.Add(hl);
                }
                else
                    block.Inlines.Add(new Run { Text = item });
            }
        }

        public static string GetVideoQualityLabel(this VideoQuality quality)
        {
            switch (quality)
            {
                case VideoQuality.High1080:
                    return "1080p";
                case VideoQuality.High1440:
                    return "1440p";
                case VideoQuality.High2160:
                    return "2160p";
                case VideoQuality.High2880:
                    return "2880p";
                case VideoQuality.High3072:
                    return "3072p";
                case VideoQuality.High4320:
                    return "4320p";
                case VideoQuality.High720:
                    return "720p";
                case VideoQuality.Low144:
                    return "144p";
                case VideoQuality.Low240:
                    return "240p";
                case VideoQuality.Medium360:
                    return "360p";
                case VideoQuality.Medium480:
                    return "480p";
                default:
                    return "Unknown";
            }
        }
        
        public async static void ProcessLink(string url)
        {
            string type;

            if (YoutubeClient.TryParseChannelId(url, out string output))
            {
                type = "channel";
                goto LinkFound;
            }
            else if (YoutubeClient.TryParsePlaylistId(url, out output))
            {
                type = "playlist";
                goto LinkFound;
            }
            else if (YoutubeClient.TryParseUsername(url, out output))
            {
                type = "user";
                goto LinkFound;
            }
            else if (YoutubeClient.TryParseVideoId(url, out output))
            {
                type = "video";
                goto LinkFound;
            }

            await Launcher.LaunchUriAsync(new Uri(url));
            return;

            LinkFound:
            switch (type)
            {
                case "channel":
                    MainPage.GoToChannel(output);
                    break;
                case "video":
                    MainPage.GoToVideo(output);
                    break;
                case "playlist":
                    MainPage.GoToPlaylist(output);
                    break;
                case "user":
                    MainPage.GoToChannel(await new YoutubeClient().GetChannelIdAsync(output));
                    break;
            }
        }

        public static void Share(DataRequestedEventArgs args, string thumbnail, string title, string url, string type)
        {
            DataRequest request = args.Request;

            request.Data.Properties.Title = title;
            request.Data.Properties.Description = $"{resources.GetString("/Methods/sharing")} {type}";

            request.Data.SetText(title + "\n" + "#YouTube #FoxTube #SharedWithFoxTube");
            request.Data.SetWebLink(url.ToUri());

            request.Data.Properties.Thumbnail = RandomAccessStreamReference.CreateFromUri(thumbnail.ToUri());
            request.Data.SetBitmap(RandomAccessStreamReference.CreateFromUri(thumbnail.ToUri()));
        }

        public static async Task<List<string>> GetHistory()
        {
            SecretsVault.RefreshToken();
            List<string> list = new List<string>();
            
            string output = await SecretsVault.HttpClient.GetStringAsync($"https://www.youtube.com/list_ajax?style=json&action_get_list=1&list=HL&hl={SettingsStorage.RelevanceLanguage}");

            dynamic raw = JsonConvert.DeserializeObject(output);
            foreach (dynamic i in raw.video)
                list.Add(i.encrypted_id.ToString());

            return list;
        }

        public static async Task<List<string>> GetLater()
        {
            SecretsVault.RefreshToken();
            List<string> list = new List<string>();
            
            string output = await SecretsVault.HttpClient.GetStringAsync($"https://www.youtube.com/list_ajax?style=json&action_get_list=1&list=WL&hl={SettingsStorage.RelevanceLanguage}");

            dynamic raw = JsonConvert.DeserializeObject(output);
            foreach (dynamic i in raw.video)
                list.Add(i.encrypted_id.ToString());

            return list;
        }
    }
}
