using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using Newtonsoft.Json;
using Pixiview.Illust;
using Xamarin.Essentials;
using Xamarin.Forms;

namespace Pixiview.Utils
{
    public class Stores
    {
        public static readonly string PersonalFolder = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
        public static readonly string CacheFolder = Environment.GetFolderPath(Environment.SpecialFolder.InternetCache);

        private const string pagesFolder = "pages";
        private const string imageFolder = "img-original";
        private const string previewFolder = "img-master";
        private const string thumbFolder = "img-thumb";
        private const string userFolder = "user-profile";

        private const string illustFile = "illust.json";
        private const string favoriteFile = "favorites.json";

        private static readonly object sync = new object();

        public static bool NetworkAvailable
        {
            get
            {
                try
                {
                    return Connectivity.NetworkAccess == NetworkAccess.Internet
                        || Connectivity.NetworkAccess == NetworkAccess.ConstrainedInternet;
                }
                catch
                {
                    return false;
                }
            }
        }

        private static T LoadObject<T>(string file, string url, string referer = null, bool force = false)
        {
            string content = null;
            if (!force && File.Exists(file))
            {
                try
                {
                    content = File.ReadAllText(file);
                }
                catch (Exception ex)
                {
                    App.DebugError("load", $"failed to read file: {file}, error: {ex.Message}");
                }
            }
            if (content == null)
            {
                var response = Download(url, headers =>
                {
                    headers.Referrer = referer == null ? Configs.Referer : new Uri(referer);
                    headers.Add("user-agent", Configs.UserAgent);
                    headers.Add("accept", Configs.AcceptJson);
                    headers.Add("cookie", Configs.Cookie);
                    headers.Add("x-user-id", Configs.UserId);
                });
                if (response == null)
                {
                    return default;
                }
                using (response)
                {
                    content = response.Content.ReadAsStringAsync().Result;
                    try
                    {
                        var folder = Path.GetDirectoryName(file);
                        if (!Directory.Exists(folder))
                        {
                            Directory.CreateDirectory(folder);
                        }
                        File.WriteAllText(file, content, Encoding.UTF8);
                    }
                    catch (Exception ex)
                    {
                        App.DebugError("save", $"failed to save illust JSON object, error: {ex.Message}");
                    }
                }
            }
            try
            {
                return JsonConvert.DeserializeObject<T>(content);
            }
            catch (Exception ex)
            {
                App.DebugError("load", $"failed to parse illust JSON object, error: {ex.Message}");
                return default;
            }
        }

        private static T ReadObject<T>(string file)
        {
            string content = null;
            if (File.Exists(file))
            {
                try
                {
                    content = File.ReadAllText(file);
                }
                catch (Exception ex)
                {
                    App.DebugError("read", $"failed to read file: {file}, error: {ex.Message}");
                }
            }
            else
            {
                App.DebugError("read", $"file not found: {file}");
                return default;
            }
            try
            {
                return JsonConvert.DeserializeObject<T>(content);
            }
            catch (Exception ex)
            {
                App.DebugError("read", $"failed to parse illust JSON object, error: {ex.Message}");
                return default;
            }
        }

        private static void WriteObject(string file, object obj)
        {
            var dir = Path.GetDirectoryName(file);
            if (!Directory.Exists(dir))
            {
                Directory.CreateDirectory(dir);
            }
            string content;
            try
            {
                content = JsonConvert.SerializeObject(obj, Formatting.None);
            }
            catch (Exception ex)
            {
                App.DebugError("write", $"failed to serialize object, error: {ex.Message}");
                return;
            }

            try
            {
                File.WriteAllText(file, content, Encoding.UTF8);
            }
            catch (Exception ex)
            {
                App.DebugError("write", $"failed to write file: {file}, error: {ex.Message}");
            }
        }

        public static IllustData LoadIllustData(bool force = false)
        {
            var file = Path.Combine(PersonalFolder, illustFile);
            var result = LoadObject<IllustData>(
                file,
                Configs.UrlIllustList,
                force: force);
            return result;
        }

        public static IllustPageData LoadIllustPageData(string id, bool force = false)
        {
            var file = Path.Combine(PersonalFolder, pagesFolder, $"{id}.json");
            var result = LoadObject<IllustPageData>(
                file,
                string.Format(Configs.UrlIllustPage, id),
                string.Format(Configs.UrlIllust, id),
                force: force);
            return result;
        }

        public static IllustFavorite LoadFavoritesIllusts()
        {
            var file = Path.Combine(PersonalFolder, favoriteFile);
            return ReadObject<IllustFavorite>(file);
        }

        public static void SaveFavoritesIllusts(IllustFavorite data)
        {
            var file = Path.Combine(PersonalFolder, favoriteFile);
            lock (sync)
            {
                WriteObject(file, data);
            }
        }

        public static ImageSource LoadIllustImage(string url)
        {
            return LoadImage(url, PersonalFolder, imageFolder);
        }

        public static ImageSource LoadPreviewImage(string url)
        {
            return LoadImage(url, PersonalFolder, previewFolder);
        }

        public static ImageSource LoadThumbnailImage(string url)
        {
            return LoadImage(url, CacheFolder, thumbFolder);
        }

        public static ImageSource LoadUserProfileImage(string url)
        {
            return LoadImage(url, CacheFolder, userFolder);
        }

        private static ImageSource LoadImage(string url, string working, string folder)
        {
            var file = Path.Combine(working, folder, Path.GetFileName(url));
            ImageSource image;
            if (File.Exists(file))
            {
                try
                {
                    image = ImageSource.FromFile(file);
                }
                catch (Exception ex)
                {
                    App.DebugError("image.load", $"failed to load image from file: {file}, error: {ex.Message}");
                    image = null;
                }
            }
            else
            {
                image = null;
            }
            if (image == null)
            {
                file = DownloadImage(url, working, folder);
                if (file != null)
                {
                    return ImageSource.FromFile(file);
                }
            }
            return image;
        }

        private static string DownloadImage(string url, string working, string folder)
        {
            try
            {
                var directory = Path.Combine(working, folder);
                if (!Directory.Exists(directory))
                {
                    Directory.CreateDirectory(directory);
                }
                var file = Path.Combine(directory, Path.GetFileName(url));
                App.DebugPrint($"download, url: {url}");
                var response = Download(url, headers =>
                {
                    headers.Referrer = Configs.Referer;
                    headers.Add("user-agent", Configs.UserAgent);
                    headers.Add("accept", Configs.AcceptImage);
                });
                if (response == null)
                {
                    return null;
                }
                using (response)
                using (var fs = File.OpenWrite(file))
                {
                    response.Content.CopyToAsync(fs).Wait();
                    //if (response.Headers.Date != null)
                    //{
                    //    File.SetLastWriteTimeUtc(file, response.Headers.Date.Value.UtcDateTime);
                    //}
                }
                return file;
            }
            catch (Exception ex)
            {
                App.DebugError("image.download", ex.Message);
                return null;
            }
        }

        private static T TryCount<T>(Func<T> func, int tryCount = 3)
        {
            int tries = 0;
            while (tries < tryCount)
            {
                try
                {
                    return func();
                }
                catch (Exception ex)
                {
                    tries++;
                    System.Threading.Thread.Sleep(400);
                    App.DebugError("try.do", $"tries: {tries}, error: {ex.Message}");
                }
            }
            return default;
        }

        private static HttpResponseMessage Download(string url, Action<HttpRequestHeaders> headerAction)
        {
            App.DebugPrint($"GET: {url}");
            var uri = new Uri(url);
            var handler = new HttpClientHandler
            {
                Proxy = Configs.Proxy,
                UseProxy = true,
                AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
            };
            var client = new HttpClient(handler)
            {
                BaseAddress = new Uri($"{uri.Scheme}://{uri.Host}")
            };
            return TryCount(() =>
            {
                using (var request = new HttpRequestMessage(HttpMethod.Get, uri.PathAndQuery)
                {
                    Version = new Version(2, 0)
                })
                {
                    var headers = request.Headers;
                    headerAction(headers);
                    headers.Add("accept-language", Configs.AcceptLanguage);
                    headers.Add("accept-encoding", Configs.AcceptEncoding);
                    return client.SendAsync(request).Result;
                }
            });
        }
    }

    public static class Configs
    {
        public static readonly WebProxy Proxy = new WebProxy("router.tsanie.us", 8088);
        public static readonly Uri Referer = new Uri("https://www.pixiv.net/");

        public const int MaxThreads = 3;
        public const string UrlIllustList = "https://www.pixiv.net/ajax/top/illust?mode=all&lang=zh";
        public const string UrlIllust = "https://www.pixiv.net/artworks/{0}";
        public const string UrlIllustPage = "https://www.pixiv.net/ajax/illust/{0}/pages?lang=zh";
        public const string UserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36";
        public const string AcceptImage = "image/png,image/svg+xml,image/*;q=0.8,video/*;q=0.8,*/*;q=0.5";
        public const string AcceptJson = "application/json";
        public const string AcceptEncoding = "gzip, deflate";
        public const string AcceptLanguage = "zh-cn";

        public const string UserId = "2603358";
        public const string Cookie =
            "PHPSESSID=2603358_VHyGPeRaz7LpeoFkRsHvjXIpApCMb56a; " +
            "a_type=0; b_type=1; c_type=31; d_type=2; " +
            "p_ab_id=2; p_ab_id_2=6; p_ab_d_id=1155161977; " +
            "privacy_policy_agreement=2; " +
            "login_ever=yes; " +
            "__cfduid=d9fa2d4d1ddd30db85ebb519f9855d2561587806747; " +
            "first_visit_datetime_pc=2019-10-29+22%3A05%3A30; " +
            "yuid_b=NgcXQWQ";

        public static string GetThumbnailUrl(string url)
        {
            url = url.Replace("/custom-thumb/", "/img-master/");
            var index = url.LastIndexOf("_square1200.jpg");
            if (index < 0)
            {
                index = url.LastIndexOf("_custom1200.jpg");
            }
            if (index >= 0)
            {
                return url.Substring(0, index) + "_master1200.jpg";
            }
            return url;
        }
    }

    public static class Routes
    {
        public const string Illust = "illust";
        public const string Detail = "detail";
        public const string Follow = "follow";
        public const string Recommends = "recommends";
        public const string ByUser = "byuser";
        public const string Ranking = "ranking";
        public const string Favorites = "favorites";
        public const string Option = "option";
    }
}
