using FluentValidation;
using FluentValidation.Validators;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text;
using System.Text.RegularExpressions;

namespace KarleyLibrary.Erweiterungen
{
    public static class FluentValidationErweiterung
    {

        public static IRuleBuilderOptions<T, string?> ISO88591<T>(this IRuleBuilder<T, string?> ruleBuilder)
        {
            return ruleBuilder.SetValidator(new EncodingValidator<T>("ISO-8859-1"));
        }
        public static IRuleBuilderOptions<T, string?> Phone<T>(this IRuleBuilder<T, string?> ruleBuilder)
        {
            return ruleBuilder.SetValidator(new PhoneValidator<T>());
        }
        public static IRuleBuilderOptions<T, string?> Url<T>(this IRuleBuilder<T, string?> ruleBuilder)
        {
            return ruleBuilder.SetValidator(new UrlValidator<T>());
        }
        public static IRuleBuilderOptions<T, string?> UstId<T>(this IRuleBuilder<T, string?> ruleBuilder)
        {
            return ruleBuilder.SetValidator(new UstidValidator<T>());
        }
        public static IRuleBuilderOptions<T, string?> IBAN<T>(this IRuleBuilder<T, string?> ruleBuilder)
        {
            return ruleBuilder.SetValidator(new IBANValidator<T>());
        }
        public static IRuleBuilderOptions<T, string?> BIC<T>(this IRuleBuilder<T, string?> ruleBuilder)
        {
            return ruleBuilder.SetValidator(new BICValidator<T>());
        }

    }

    public abstract class BaseCustomValidator : PropertyValidator
    {
        public virtual string Name => "Base";
        protected override string GetDefaultMessageTemplate()
        {
            return ValidatorOptions.Global.LanguageManager.GetString(Name);
        }
    }

    public class EncodingValidator<T> : BaseCustomValidator
    {
        public override string Name => "EncodingValidator";

        public string Encoding { get; set; }

        public EncodingValidator(string encoding)
        {
            Encoding = encoding;
        }

        protected override bool IsValid(PropertyValidatorContext context)
        {
            string? Value = context.PropertyValue as string;
            if (String.IsNullOrWhiteSpace(Value))
            {
                return true;
            }

            Encoding enc = System.Text.Encoding.GetEncoding(Encoding);

            byte[] bytes = enc.GetBytes(Value);
            string result = enc.GetString(bytes);

            if (result.Length != Value.Length)
                return false;

            context.MessageFormatter.AppendArgument("EncodingName", enc.EncodingName);


            List<char> unavailableChars = new List<char>();
            bool areEqual = true;
            for(int i = 0; i< result.Length; i++)
            {
                if(result[i] != Value[i])
                {
                    if(!unavailableChars.Contains(Value[i]))
                        unavailableChars.Add(Value[i]);
                    areEqual = false;
                }
            }

            context.MessageFormatter.AppendArgument("UnavailableCharacters", String.Join("','",unavailableChars));

            //return String.Equals(Value, result);
            return areEqual;
        }
    }

    public class PhoneValidator<T> : BaseCustomValidator
    {
        public override string Name => "PhoneValidator";

        private PhoneAttribute validationAttribute { get; set; }
        public PhoneValidator()
        {
            validationAttribute = new PhoneAttribute();
        }

        protected override bool IsValid(PropertyValidatorContext context)
        {
            string? Value = context.PropertyValue as string;
            if (String.IsNullOrWhiteSpace(Value))
            {
                return true;
            }

            return validationAttribute.IsValid(Value);
        }
    }

    public class UrlValidator<T> : BaseCustomValidator
    {
        public override string Name => "UrlValidator";
        protected override bool IsValid(PropertyValidatorContext context)
        {
            string? Value = context.PropertyValue as string;
            if (String.IsNullOrWhiteSpace(Value))
            {
                return true;
            }

            Uri? uriResult;
            if (!Uri.TryCreate(Value, UriKind.Absolute, out uriResult))
            {
                return false;
            }
            else
            {
                if (uriResult is null)
                {
                    return false;
                }
                else
                {
                    return (uriResult.Scheme == Uri.UriSchemeHttp || uriResult.Scheme == Uri.UriSchemeHttps);
                }
            }
        }
    }

    public class UstidValidator<T> : BaseCustomValidator
    {
        public override string Name => "UstidValidator";

        private const string Pattern = @"(?xi)^(
(AT)?U[0-9]{8} |                              # Austria
(BE)?0[0-9]{9} |                              # Belgium
(BG)?[0-9]{9,10} |                            # Bulgaria
(HR)?[0-9]{11} |                              # Croatia
(CY)?[0-9]{8}[A-Z] |                          # Cyprus
(CZ)?[0-9]{8,10} |                            # Czech Republic
(DE)?[0-9]{9} |                               # Germany
(DK)?[0-9]{8} |                               # Denmark
(EE)?[0-9]{9} |                               # Estonia
(EL)?[0-9]{9} |                               # Greece
ES[A-Z][0-9]{7}(?:[0-9]|[A-Z]) |              # Spain
(FI)?[0-9]{8} |                               # Finland
(FR)?[0-9A-Z]{2}[0-9]{9} |                    # France
(GB)?([0-9]{9}([0-9]{3})?|[A-Z]{2}[0-9]{3}) | # United Kingdom
(HU)?[0-9]{8} |                               # Hungary
(IE)?[0-9]{7}[A-Z]{1,2}   |                   # Ireland
(IE)?[0-9][A-Z][0-9]{5}[A-Z] |                # Ireland (2)
(IT)?[0-9]{11} |                              # Italy
(LT)?([0-9]{9}|[0-9]{12}) |                   # Lithuania
(LU)?[0-9]{8} |                               # Luxembourg
(LV)?[0-9]{11} |                              # Latvia
(MT)?[0-9]{8} |                               # Malta
(NL)?[0-9]{9}B[0-9]{2} |                      # Netherlands
(PL)?[0-9]{10} |                              # Poland
(PT)?[0-9]{9} |                               # Portugal
(RO)?[0-9]{2,10} |                            # Romania
(SE)?[0-9]{12} |                              # Sweden
(SI)?[0-9]{8} |                               # Slovenia
(SK)?[0-9]{10}                                # Slovakia
)$";
        protected override bool IsValid(PropertyValidatorContext context)
        {
            string? Value = context.PropertyValue as string;
            if (String.IsNullOrWhiteSpace(Value))
            {
                return true;
            }

            Regex regex = new Regex(Pattern, RegexOptions.Singleline | RegexOptions.IgnoreCase);
            var regexResult = regex.Match(Value);
            return regexResult.Success;
        }
    }

    public class IBANValidator<T> : BaseCustomValidator
    {
        public override string Name => "IBANValidator";

        protected override bool IsValid(PropertyValidatorContext context)
        {
            string? Value = context.PropertyValue as string;
            if (String.IsNullOrWhiteSpace(Value))
            {
                return true;
            }

            string iban = Value.ToUpper(); //IN ORDER TO COPE WITH THE REGEX BELOW
            if (Regex.IsMatch(iban, "^[A-Z0-9]") && iban.Length > 4)
            {
                iban = iban.Replace(" ", String.Empty);
                string bank = $"{iban[4..]}{iban.Substring(0, 4)}";
                int asciiShift = 55;
                StringBuilder sb = new StringBuilder();
                foreach (char c in bank)
                {
                    int v;
                    if (Char.IsLetter(c))
                    {
                        v = c - asciiShift;
                    }
                    else
                    {
                        v = int.Parse(c.ToString());
                    }

                    sb.Append(v);
                }
                string checkSumString = sb.ToString();
                int checksum = int.Parse(checkSumString.Substring(0, 1));
                for (int i = 1; i < checkSumString.Length; i++)
                {
                    int v = int.Parse(checkSumString.Substring(i, 1));
                    checksum *= 10;
                    checksum += v;
                    checksum %= 97;
                }
                return checksum == 1;
            }
            else
            {
                return false;
            }
        }
    }

    public class BICValidator<T> : BaseCustomValidator
    {
        public override string Name => "BICValidator";

        protected override bool IsValid(PropertyValidatorContext context)
        {
            string? Value = context.PropertyValue as string;
            if (String.IsNullOrWhiteSpace(Value))
            {
                return true;
            }

            string bic = Value.ToUpper();
            Regex regex = new Regex(@"[A-Z]{6,6}[A-Z2-9][A-NP-Z0-9]([A-Z0-9]{3,3}){0,1}");
            return regex.Match(bic).Success;
        }


    }
}
