using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Reflection;
using System.Text.RegularExpressions;

namespace AyCode.Core.Serializers.Toons;

public static partial class AcToonSerializer
{
    /// <summary>
    /// Extract constraints from C# type system (value types, nullables, numeric ranges).
    /// </summary>
    private static string ExtractTypeConstraints(Type type)
    {
        var constraints = new List<string>();

        var nullableUnderlying = Nullable.GetUnderlyingType(type);
        var isNullable = nullableUnderlying != null || !type.IsValueType;

        // Nullable/Required: NE írjuk ki automatikusan, mert redundáns (a típusból látszik)
        // Csak explicit [Required] attribute esetén írjuk ki (azt az ExtractDataAnnotationConstraints kezeli)

        // Numeric/Boolean: NE írjuk ki, mert redundáns (a típusból látszik)
        // Range-eket megtartjuk, mert azok hasznos extra információk

        var baseType = nullableUnderlying ?? type;

        // Enum property: NE írjunk automatic constraint-et!
        // A típusból (pl. "TaxDisplayType: TaxDisplayType") már minden információ látszik.
        // Az underlying type már az enum definíciójában megtalálható a @types szekcióban.
        // Csak akkor kell constraint, ha explicit attribute-al van megjelölve.
        if (baseType.IsEnum)
        {
            return string.Empty;
        }

        var typeCode = Type.GetTypeCode(baseType);
        switch (typeCode)
        {
            case TypeCode.Byte:
                constraints.Add("range: 0-255");
                break;
            case TypeCode.SByte:
                constraints.Add("range: -128-127");
                break;
            case TypeCode.Int16:
                constraints.Add("range: -32768-32767");
                break;
            case TypeCode.UInt16:
                constraints.Add("range: 0-65535");
                break;
            case TypeCode.UInt32:
                constraints.Add("range: 0-4294967295");
                break;
            // Int32, Int64, UInt64, Float, Double, Decimal, Boolean: nincs constraint (redundáns lenne)
        }

        return constraints.Count > 0 ? string.Join(", ", constraints) : string.Empty;
    }

    /// <summary>
    /// Detect if property is readonly (no setter or init-only).
    /// </summary>
    private static bool IsReadOnlyProperty(PropertyInfo prop)
    {
        // No setter at all → readonly
        if (!prop.CanWrite) return true;

        // Has setter but it's init-only → readonly after construction
        var setMethod = prop.GetSetMethod(nonPublic: true);
        if (setMethod != null)
        {
            // Check for init-only (C# 9+ feature)
            var returnParameter = setMethod.ReturnParameter;
            var requiredModifiers = returnParameter.GetRequiredCustomModifiers();
            if (requiredModifiers.Any(x => x.FullName == "System.Runtime.CompilerServices.IsExternalInit"))
            {
                return true;
            }
        }

        return false;
    }

    /// <summary>
    /// Extract constraints from Microsoft DataAnnotations attributes.
    /// </summary>
    private static string ExtractDataAnnotationConstraints(PropertyInfo prop)
    {
        var constraints = new List<string>();

        // [Required]
        if (prop.GetCustomAttribute<RequiredAttribute>() != null)
            constraints.Add("required");

        // [Range]
        var range = prop.GetCustomAttribute<RangeAttribute>();
        if (range != null)
            constraints.Add($"range: {range.Minimum}-{range.Maximum}");

        // [MaxLength]
        var maxLen = prop.GetCustomAttribute<MaxLengthAttribute>();
        if (maxLen != null)
            constraints.Add($"max-length: {maxLen.Length}");

        // [MinLength]
        var minLen = prop.GetCustomAttribute<MinLengthAttribute>();
        if (minLen != null)
            constraints.Add($"min-length: {minLen.Length}");

        // [StringLength]
        var strLen = prop.GetCustomAttribute<StringLengthAttribute>();
        if (strLen != null)
        {
            if (strLen.MinimumLength > 0)
                constraints.Add($"length: {strLen.MinimumLength}-{strLen.MaximumLength}");
            else
                constraints.Add($"max-length: {strLen.MaximumLength}");
        }

        // [EmailAddress]
        if (prop.GetCustomAttribute<EmailAddressAttribute>() != null)
            constraints.Add("email-format");

        // [Phone]
        if (prop.GetCustomAttribute<PhoneAttribute>() != null)
            constraints.Add("phone-format");

        // [Url]
        if (prop.GetCustomAttribute<UrlAttribute>() != null)
            constraints.Add("url-format");

        // [CreditCard]
        if (prop.GetCustomAttribute<CreditCardAttribute>() != null)
            constraints.Add("credit-card-format");

        // [RegularExpression]
        var regex = prop.GetCustomAttribute<RegularExpressionAttribute>();
        if (regex != null)
            constraints.Add($"pattern: {regex.Pattern}");

        return string.Join(", ", constraints);
    }

    /// <summary>
    /// Merge constraints with priority: custom > ms > inferred > type.
    /// Handles deduplication and cleanup.
    /// </summary>
    private static string MergeConstraints(
        string? typeConstraints,
        string? msConstraints,
        string? inferredConstraints,
        string? customConstraints)
    {
        var all = new HashSet<string>();

        // Add in reverse priority order (lower priority first)
        AddConstraintsToSet(all, typeConstraints);
        AddConstraintsToSet(all, inferredConstraints);
        AddConstraintsToSet(all, msConstraints);
        AddConstraintsToSet(all, customConstraints);

        return string.Join(", ", all.OrderBy(GetConstraintPriority));
    }

    private static void AddConstraintsToSet(HashSet<string> set, string? constraints)
    {
        if (string.IsNullOrWhiteSpace(constraints)) return;

        foreach (var c in constraints.Split(',').Select(x => x.Trim()).Where(x => !string.IsNullOrEmpty(x)))
        {
            // Remove duplicates based on constraint type
            var constraintType = c.Split(':')[0].Trim();

            // Remove existing constraints of same type (e.g., old range before adding new range)
            set.RemoveWhere(x => x.Split(':')[0].Trim().Equals(constraintType, StringComparison.OrdinalIgnoreCase));

            set.Add(c);
        }
    }

    private static int GetConstraintPriority(string constraint)
    {
        var lower = constraint.ToLowerInvariant();

        // Priority order
        if (lower.StartsWith("required")) return 1;
        if (lower.StartsWith("nullable")) return 2;
        if (lower.StartsWith("range:")) return 3;
        if (lower.StartsWith("length:")) return 4;
        if (lower.StartsWith("max-length:")) return 5;
        if (lower.StartsWith("min-length:")) return 6;
        if (lower.Contains("-format")) return 7;
        if (lower.StartsWith("pattern:")) return 8;
        if (lower.StartsWith("enum:")) return 9;

        return 100; // Other constraints last
    }

    /// <summary>
    /// Resolve [#...] placeholders in description string.
    /// </summary>
    private static string ResolveDescriptionPlaceholders(
        string template,
        ToonPropertyAccessor prop,
        Type declaringType)
    {
        var result = template;

        // [#Description] → Microsoft [Description]
        if (result.Contains("[#Description]"))
        {
            var msDesc = prop.PropertyInfo.GetCustomAttribute<DescriptionAttribute>();
            var value = msDesc?.Description ?? "";
            result = result.Replace("[#Description]", value);
        }

        // [#DisplayName] → Microsoft [DisplayName]
        if (result.Contains("[#DisplayName]"))
        {
            var displayName = prop.PropertyInfo.GetCustomAttribute<DisplayNameAttribute>();
            var value = displayName?.DisplayName ?? prop.Name;
            result = result.Replace("[#DisplayName]", value);
        }

        // [#SmartDescription] → Smart inference
        if (result.Contains("[#SmartDescription]"))
        {
            var value = GetPropertyDescription(declaringType, prop.Name, prop.PropertyType);
            result = result.Replace("[#SmartDescription]", value);
        }

        return CleanupPlaceholders(result);
    }

    /// <summary>
    /// Resolve [#...] placeholders in purpose string.
    /// </summary>
    private static string ResolvePurposePlaceholders(
        string template,
        ToonPropertyAccessor prop,
        Type declaringType)
    {
        var result = template;

        // [#SmartPurpose] → Smart inference
        if (result.Contains("[#SmartPurpose]"))
        {
            var value = GetPropertyPurpose(declaringType, prop.Name);
            result = result.Replace("[#SmartPurpose]", value);
        }

        return CleanupPlaceholders(result);
    }

    /// <summary>
    /// Resolve [#...] placeholders in constraints string.
    /// </summary>
    private static string ResolveConstraintPlaceholders(
        string template,
        ToonPropertyAccessor prop)
    {
        var result = template;

        // [#Range]
        if (result.Contains("[#Range]"))
        {
            var attr = prop.PropertyInfo.GetCustomAttribute<RangeAttribute>();
            var value = attr != null ? $"range: {attr.Minimum}-{attr.Maximum}" : "";
            result = result.Replace("[#Range]", value);
        }

        // [#Required]
        if (result.Contains("[#Required]"))
        {
            var value = prop.PropertyInfo.GetCustomAttribute<RequiredAttribute>() != null ? "required" : "";
            result = result.Replace("[#Required]", value);
        }

        // [#MaxLength]
        if (result.Contains("[#MaxLength]"))
        {
            var attr = prop.PropertyInfo.GetCustomAttribute<MaxLengthAttribute>();
            var value = attr != null ? $"max-length: {attr.Length}" : "";
            result = result.Replace("[#MaxLength]", value);
        }

        // [#MinLength]
        if (result.Contains("[#MinLength]"))
        {
            var attr = prop.PropertyInfo.GetCustomAttribute<MinLengthAttribute>();
            var value = attr != null ? $"min-length: {attr.Length}" : "";
            result = result.Replace("[#MinLength]", value);
        }

        // [#StringLength]
        if (result.Contains("[#StringLength]"))
        {
            var attr = prop.PropertyInfo.GetCustomAttribute<StringLengthAttribute>();
            string value = "";
            if (attr != null)
            {
                value = attr.MinimumLength > 0
                    ? $"length: {attr.MinimumLength}-{attr.MaximumLength}"
                    : $"max-length: {attr.MaximumLength}";
            }
            result = result.Replace("[#StringLength]", value);
        }

        // [#EmailAddress]
        if (result.Contains("[#EmailAddress]"))
        {
            var value = prop.PropertyInfo.GetCustomAttribute<EmailAddressAttribute>() != null ? "email-format" : "";
            result = result.Replace("[#EmailAddress]", value);
        }

        // [#Phone]
        if (result.Contains("[#Phone]"))
        {
            var value = prop.PropertyInfo.GetCustomAttribute<PhoneAttribute>() != null ? "phone-format" : "";
            result = result.Replace("[#Phone]", value);
        }

        // [#Url]
        if (result.Contains("[#Url]"))
        {
            var value = prop.PropertyInfo.GetCustomAttribute<UrlAttribute>() != null ? "url-format" : "";
            result = result.Replace("[#Url]", value);
        }

        // [#CreditCard]
        if (result.Contains("[#CreditCard]"))
        {
            var value = prop.PropertyInfo.GetCustomAttribute<CreditCardAttribute>() != null ? "credit-card-format" : "";
            result = result.Replace("[#CreditCard]", value);
        }

        // [#RegularExpression]
        if (result.Contains("[#RegularExpression]"))
        {
            var attr = prop.PropertyInfo.GetCustomAttribute<RegularExpressionAttribute>();
            var value = attr != null ? $"pattern: {attr.Pattern}" : "";
            result = result.Replace("[#RegularExpression]", value);
        }

        // [#SmartTypeConstraints] → Type-derived constraints
        if (result.Contains("[#SmartTypeConstraints]"))
        {
            var value = ExtractTypeConstraints(prop.PropertyType);
            result = result.Replace("[#SmartTypeConstraints]", value);
        }

        // [#SmartInferenceConstraints] → Smart inference
        if (result.Contains("[#SmartInferenceConstraints]"))
        {
            var value = GetInferredConstraints(prop.PropertyType, prop.Name);
            result = result.Replace("[#SmartInferenceConstraints]", value);
        }

        return CleanupPlaceholders(result);
    }

    /// <summary>
    /// Resolve [#...] placeholders in examples string.
    /// </summary>
    private static string ResolveExamplesPlaceholders(
        string template,
        ToonPropertyAccessor prop)
    {
        var result = template;

        // [#GeneratedExample] → Generate example based on type
        if (result.Contains("[#GeneratedExample]"))
        {
            var value = GenerateExampleValue(prop.PropertyType);
            result = result.Replace("[#GeneratedExample]", value);
        }

        return CleanupPlaceholders(result);
    }

    /// <summary>
    /// Clean up empty placeholders and formatting artifacts.
    /// </summary>
    private static string CleanupPlaceholders(string text)
    {
        // Remove unresolved placeholders
        text = Regex.Replace(text, @"\[#\w+\]", "");

        // Remove double commas
        text = Regex.Replace(text, @",\s*,", ",");

        // Remove leading/trailing commas
        text = Regex.Replace(text, @"^,\s*", "");
        text = Regex.Replace(text, @",\s*$", "");

        // Remove multiple spaces
        text = Regex.Replace(text, @"\s{2,}", " ");

        return text.Trim();
    }

    /// <summary>
    /// Generate example value for a type.
    /// </summary>
    private static string GenerateExampleValue(Type type)
    {
        var typeCode = Type.GetTypeCode(type);
        return typeCode switch
        {
            TypeCode.Int32 => "42",
            TypeCode.Int64 => "1000",
            TypeCode.String => "example",
            TypeCode.Boolean => "true",
            TypeCode.DateTime => DateTime.Now.ToString("yyyy-MM-dd"),
            TypeCode.Decimal => "99.99",
            TypeCode.Double => "3.14",
            TypeCode.Single => "2.71",
            TypeCode.Byte => "255",
            _ => "value"
        };
    }

    /// <summary>
    /// Get inferred constraints based on property name patterns.
    /// </summary>
    private static string GetInferredConstraints(Type propertyType, string propertyName)
    {
        var constraints = new List<string>();
        var baseType = Nullable.GetUnderlyingType(propertyType) ?? propertyType;

        // Email
        if (baseType == typeof(string) && propertyName.Contains("Email", StringComparison.OrdinalIgnoreCase))
            constraints.Add("email-format");

        // URL
        if (baseType == typeof(string) && propertyName.Contains("Url", StringComparison.OrdinalIgnoreCase))
            constraints.Add("url-format");

        // Age
        if (IsIntegerType(baseType) && propertyName.Contains("Age", StringComparison.OrdinalIgnoreCase))
            constraints.Add("range: 0-150");

        // Count (non-negative)
        if (IsIntegerType(baseType) && propertyName.EndsWith("Count", StringComparison.OrdinalIgnoreCase))
            constraints.Add("non-negative");

        return string.Join(", ", constraints);
    }
}
