using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Reflection;
using System.Text.RegularExpressions;
using static Fast.Shared.Models.Enums;

namespace Fast.Shared.Logic;

public static partial class Util
{
    public static partial class String
    {
        public static string StripBrackets(string str)
        {
            str = str.Replace("[", "");
            str = str.Replace("]", "");
            return str;
        }

        public static string ConvertCommaSeparatedIdsToNames(string Ids, Dictionary<int, string> dic)
        {
            string names;
            List<string> namesList = new();
            if (!string.IsNullOrEmpty(Ids))
            {
                List<int> IdList = Ids.Split(',').Select(x => int.Parse(x)).ToList();
                foreach (int Id in IdList)
                {
                    if (dic.TryGetValue(Id, out var name))
                        namesList.Add(name);
                }
            }

            if (namesList.Count > 0)
                names = string.Join(" \u2022 ", namesList);
            else
                names = "";

            return names;
        }

        public static List<int> ConvertCommaSeparatedIdsToList(string Ids)
        {
            List<int> result = new();

            if (!string.IsNullOrEmpty(Ids))
                result = Ids.Split(',', StringSplitOptions.TrimEntries).Select(x => int.Parse(x)).ToList();

            return result;
        }

        public static string ConvertIdsListToString(List<int>? Ids)
        {
            string result = "";

            if (Ids is { Count: > 0 })
                result = string.Join(',', Ids);

            return result;
        }

        public static string FirstCharToUpper(string input) =>
            input switch
            {
                null => throw new ArgumentNullException(nameof(input)),
                "" => throw new ArgumentException($"{nameof(input)} cannot be empty", nameof(input)),
                _ => input.First().ToString().ToUpper() + input[1..]
            };

        public static string GetExceptionMessage(Exception exception)
        {
            string errorMsg;
            if (exception.InnerException != null)
            {
                if (exception.InnerException.InnerException != null)
                {
                    if (exception.InnerException.InnerException.InnerException != null)
                    {
                        errorMsg = exception.InnerException.InnerException.InnerException.Message;
                    }
                    else
                    {
                        errorMsg = exception.InnerException.InnerException.Message;
                    }
                }
                else
                {
                    errorMsg = exception.InnerException.Message;
                }
            }
            else
            {
                errorMsg = exception.Message;
            }

            return errorMsg;
        }

        public static string GetColDisplayName(PropertyInfo propertyInfo)
        {
            string? colDisplayName = null;
            var columnAttributes = propertyInfo.GetCustomAttributes(typeof(ColumnAttribute), false);
            if (columnAttributes.Length != 0)
                colDisplayName = columnAttributes.Cast<ColumnAttribute>().Single()?.Name;

            colDisplayName ??= propertyInfo.Name;
            return colDisplayName;
        }

        public class DisplayInfo
        {
            public string PropName = "";
            public string DisplayName = "";
            public int Width;
            public bool IsVisible = true;
            public string Filter = "";
            public string Format = "";
            public int Order;
        }

        public static List<DisplayInfo> GetDisplayInfos<T>(bool lowerFirstLetter = false)
        {
            PropertyInfo[] propInfos = typeof(T).GetProperties();
            List<DisplayInfo> displayInfos = new();

            int newOrderNum = 1;
            foreach (var prop in propInfos)
            {
                var displayAttribute = prop.GetCustomAttributes<DisplayAttribute>().FirstOrDefault();
                if (displayAttribute != null && !string.IsNullOrEmpty(displayAttribute.Name) && !string.IsNullOrEmpty(displayAttribute.Description))
                {
                    var displayInfo = new DisplayInfo();
                    displayInfo.PropName = lowerFirstLetter ? prop.Name[..1].ToLower() + prop.Name[1..] : prop.Name;
                    displayInfo.DisplayName = displayAttribute.Name;
                    displayInfo.Order = newOrderNum++;
                    string[] descriptionSplit = displayAttribute.Description.Split(",");
                    displayInfo.Width = int.Parse(descriptionSplit[0]);
                    if (descriptionSplit.Length >= 2)
                        displayInfo.IsVisible = descriptionSplit[1] == "1";
                    if (descriptionSplit.Length >= 3)
                        displayInfo.Filter = descriptionSplit[2];
                    if (descriptionSplit.Length >= 4)
                        displayInfo.Format = descriptionSplit[3];
                    displayInfos.Add(displayInfo);
                }
            }

            return displayInfos;
        }

        public static string GetOrdinal(int num)
        {
            if (num <= 0) return num.ToString();

            return (num % 100) switch
            {
                11 or 12 or 13 => num + "th",
                _ => (num % 10) switch
                {
                    1 => num + "st",
                    2 => num + "nd",
                    3 => num + "rd",
                    _ => num + "th",
                },
            };
        }

        /// <summary>
        /// Invalid characters for file names on all platforms.
        /// </summary>
        static readonly char[] invalidFileNameChars = "<>:\"/\\|?*\0".ToCharArray();

        /// <summary>
        /// Replaces invalid file name characters with the replacement string.
        /// </summary>
        public static string GetLegalFileName(string fileName, string replacement = "")
        {
            return string.Join(replacement, fileName.Split(invalidFileNameChars));
        }

        public static string FixLength(string str, int Length, PadDirection PadDirection = PadDirection.Right)
        {
            if (str.Length > Length)
                str = str[..Length];

            if (PadDirection == PadDirection.Right)
                str = str.PadRight(Length);
            else
                str = str.PadLeft(Length);

            return str;
        }

        public static bool IsValidEmail(string email)
        {
            var trimmedEmail = email.Trim();

            if (trimmedEmail.EndsWith('.'))
                return false;
            try
            {
                var addr = new System.Net.Mail.MailAddress(email);
                return addr.Address == trimmedEmail;
            }
            catch
            {
                return false;
            }
        }

        public static string GetFaxNumberNormalized(string faxNumber)
        {
            faxNumber = faxNumber.Trim();
            //use regex to split the input into country code, area code, and number
            Match regexResult = FaxNumberRegex().Match(faxNumber);
            string countryCode = regexResult.Groups["CountryCode"].Value.Trim();
            countryCode = !string.IsNullOrWhiteSpace(countryCode) ? countryCode : "1";
            string areaCode = regexResult.Groups["AreaCode"].Value.Trim();
            string number = regexResult.Groups["Number"].Value.Trim();

            string result = countryCode + areaCode + number;
            result = result.Replace("-", "").RemoveWhitespace();
            return result;
        }

        public static string GetNewGuid()
        {
            return Guid.NewGuid().ToString("N");
        }

        public static List<string> ParseCsvLine(string line)
        {
            var result = new List<string>();
            var currentString = "";
            bool inQuotes = false;

            for (int i = 0; i < line.Length; i++)
            {
                if (inQuotes)
                {
                    if (line[i] == '"' && (i + 1 < line.Length && line[i + 1] == '"')) // Handle escaped quotes
                    {
                        currentString += line[i];
                        i++; // Skip next quote
                    }
                    else if (line[i] == '"')
                    {
                        inQuotes = false;
                    }
                    else
                    {
                        currentString += line[i];
                    }
                }
                else
                {
                    if (line[i] == '"')
                    {
                        inQuotes = true;
                    }
                    else if (line[i] == ',')
                    {
                        result.Add(currentString);
                        currentString = "";
                    }
                    else
                    {
                        currentString += line[i];
                    }
                }
            }

            result.Add(currentString); // Add last field
            return result;
        }

        public static string SanitizeForJsFieldName(string input)
        {
            if (string.IsNullOrEmpty(input))
                return "_"; // Return a default valid identifier if input is null or empty

            // Replace invalid starting characters with an underscore
            var sanitized = JsFieldStartRegex().Replace(input[0].ToString(), "_");

            // If the string is longer than 1 character, process the rest of the string
            if (input.Length > 1)
                // Append the rest of the string, replacing invalid characters with underscores
                sanitized += JsFieldPartRegex().Replace(input[1..], "_");

            return sanitized;
        }

        [GeneratedRegex(@"^(?:(?:[\+]?(?<CountryCode>[\d]{1,3}(?:[ ]+|[\-.])))?[(]?(?<AreaCode>[\d]{3})[\-/)]?(?:[ ]+)?)?(?<Number>[a-zA-Z2-9][a-zA-Z0-9 \-.]{6,})(?:(?:[ ]+|[xX]|(i:ext[\.]?)){1,2}(?<Ext>[\d]{1,5}))?$")]
        private static partial Regex FaxNumberRegex();

        [GeneratedRegex("[^a-zA-Z$_]")]
        private static partial Regex JsFieldStartRegex();

        [GeneratedRegex("[^a-zA-Z0-9$_]")]
        private static partial Regex JsFieldPartRegex();
    }

    public static string? ToCommaSeparatedString(bool putSpaceAfterComma, List<string?>? items)
    {
        var separator = putSpaceAfterComma ? ", " : ",";
        if (items == null) return null;

        var filtered = items
            .Where(s => !string.IsNullOrWhiteSpace(s))
            .Select(s => s!.Trim()) // safe because null already filtered

        .ToList();

        return filtered.Count > 0 ? string.Join(",", filtered) : null;
    }

    public static string? MergeCommaSeparatedStrings(bool putSpaceAfterComma, params string?[] inputs)
    {
        var newSeparator = putSpaceAfterComma ? ", " : ",";
        var nonEmptyInputs = inputs
            .Where(s => !string.IsNullOrWhiteSpace(s)) // filter null or empty inputs
            .Select(s =>
                string.Join(newSeparator,
                    s!.Split(',')
                     .Select(token => token.Trim()) // trim each value
                     .Where(token => !string.IsNullOrWhiteSpace(token))
                )
            )
            .Where(joined => !string.IsNullOrEmpty(joined)) // filter empty results
            .ToList();

        if (nonEmptyInputs.Count == 0)
            return null;

        return string.Join(newSeparator, nonEmptyInputs);
    }
}
