using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;

namespace Nickvision.Parabolic.Shared.Helpers;

public static class StringExtensions
{
    private static readonly List<string> SupportedYouTubeLanguages;

    static StringExtensions()
    {
        SupportedYouTubeLanguages = [
            "af", "az", "id", "ms", "bs", "ca", "cs", "da", "de", "et", "en-IN", "en-GB", "en", "es",
            "es-419", "es-US", "eu", "fil", "fr", "fr-CA", "gl", "hr", "zu", "is", "it", "sw", "lv",
            "lt", "hu", "nl", "no", "uz", "pl", "pt-PT", "pt", "ro", "sq", "sk", "sl", "sr-Latn", "fi",
            "sv", "vi", "tr", "be", "bg", "ky", "kk", "mk", "mn", "ru", "sr", "uk", "el", "hy", "iw",
            "ur", "ar", "fa", "ne", "mr", "hi", "as", "bn", "pa", "gu", "or", "ta", "te", "kn", "ml",
            "si", "th", "lo", "my", "ka", "am", "km", "zh-CN", "zh-TW", "zh-HK", "ja", "ko"
        ];
    }

    extension(string s)
    {
        public bool IsSupportedYouTubeLanguage => SupportedYouTubeLanguages.Contains(s);

        public int AriaEtaToSeconds()
        {
            var hours = 0;
            var minutes = 0;
            var seconds = 0;
            var index = -1;
            if ((index = s.IndexOf('h')) != -1)
            {
                hours = int.Parse(s.Substring(0, index)) * 3600;
                s = s.Substring(index + 1);
            }
            if ((index = s.IndexOf('m')) != -1)
            {
                minutes = int.Parse(s.Substring(0, index)) * 60;
                s = s.Substring(index + 1);
            }
            if ((index = s.IndexOf('s')) != -1)
            {
                seconds = int.Parse(s.Substring(0, index));
            }
            return hours + minutes + seconds;
        }

        public double AriaSizeToBytes()
        {
            var index = -1;
            if ((index = s.IndexOf("GiB")) != -1)
            {
                return double.Parse(s.Substring(0, index), NumberStyles.Any, CultureInfo.InvariantCulture) * Math.Pow(1024, 3);
            }
            else if ((index = s.IndexOf("MiB")) != -1)
            {
                return double.Parse(s.Substring(0, index), NumberStyles.Any, CultureInfo.InvariantCulture) * Math.Pow(1024, 2);
            }
            else if ((index = s.IndexOf("KiB")) != -1)
            {
                return double.Parse(s.Substring(0, index), NumberStyles.Any, CultureInfo.InvariantCulture) * 1024;
            }
            else if ((index = s.IndexOf("B")) != -1)
            {
                return double.Parse(s.Substring(0, index), NumberStyles.Any, CultureInfo.InvariantCulture);
            }
            return 0.0;
        }

        public string Clean() => s.Trim().Trim('"').Trim();

        public string SanitizeForFilename(bool includeWindowsCharacters)
        {
            var chars = Path.GetInvalidFileNameChars().ToHashSet();
            chars.Add('/');
            if (includeWindowsCharacters || OperatingSystem.IsWindows())
            {
                chars.Add('<');
                chars.Add('>');
                chars.Add(':');
                chars.Add('"');
                chars.Add('\\');
                chars.Add('|');
                chars.Add('?');
                chars.Add('*');
            }
            var result = s;
            foreach (var c in chars)
            {
                result = result.Replace(c, '_');
            }
            return result;
        }

        public IEnumerable<string> Split(Func<char, bool> controller)
        {
            var nextPiece = 0;
            for (int c = 0; c < s.Length; c++)
            {
                if (controller(s[c]))
                {
                    yield return s.Substring(nextPiece, c - nextPiece);
                    nextPiece = c + 1;
                }
            }
            if (nextPiece < s.Length)
            {
                yield return s.Substring(nextPiece);
            }
        }

        public IEnumerable<string> SplitCommandLine()
        {
            var inQuotes = false;
            return s.Split(c =>
            {
                if (c == '"')
                {
                    inQuotes = !inQuotes;
                }
                return !inQuotes && char.IsWhiteSpace(c);
            }).Select(arg => arg.Trim().TrimMatchingQuotes('"')).Where(arg => !string.IsNullOrEmpty(arg));
        }

        public string TrimMatchingQuotes(char quote)
        {
            if (s.Length >= 2 && s[0] == quote && s[^1] == quote)
            {
                return s.Substring(1, s.Length - 2);
            }
            return s;
        }

        public bool TryParseAriaProgressLine(out double progress, out double speed, out int eta)
        {
            progress = double.NaN;
            speed = 0.0;
            eta = 0;
            var fields = s.Split(' ', StringSplitOptions.RemoveEmptyEntries);
            if (fields.Length != 5)
            {
                return false;
            }
            var sizeSeparatorIndex = fields[1].IndexOf('/');
            if (sizeSeparatorIndex <= 0 || sizeSeparatorIndex == fields[1].Length - 1)
            {
                return false;
            }
            var downloaded = fields[1][..sizeSeparatorIndex];
            var total = fields[1][(sizeSeparatorIndex + 1)..];
            progress = downloaded.AriaSizeToBytes() / total.AriaSizeToBytes();
            speed = fields[3].Length > 3 ? fields[3][3..].AriaSizeToBytes() : 0.0;
            eta = fields[4].Length > 5 ? fields[4][4..^1].AriaEtaToSeconds() : 0;
            return true;
        }
    }
}
