using System.ComponentModel;
using System.Reflection;
using static AyCode.Core.Helpers.JsonUtilities;

namespace AyCode.Core.Serializers.Toons;

public static partial class AcToonSerializer
{
    /// <summary>
    /// Get description for a type (can be extended with XML comments or attributes).
    /// </summary>
    private static string GetTypeDescription(Type type)
    {
        // For now, generate simple descriptions
        // TODO: In the future, this could read XML documentation comments

        if (type.IsEnum)
            return $"Enum type with values: {string.Join(", ", Enum.GetNames(type))}";

        var metadata = GetTypeMetadata(type);

        // Only treat as collection if ElementType is not System.Object (fallback value from GetCollectionElementType)
        if (metadata.IsCollection && metadata.ElementType != null && metadata.ElementType != typeof(object))
            return $"Collection of {metadata.ElementType.Name}";

        if (metadata.IsDictionary)
            return "Dictionary mapping keys to values";

        return $"Object of type {type.Name}";
    }

    /// <summary>
    /// Get description for a property (can be extended with XML comments or attributes).
    /// </summary>
    private static string GetPropertyDescription(Type declaringType, string propertyName, Type propertyType)
    {
        // Enhanced description based on common patterns
        var isNullable = Nullable.GetUnderlyingType(propertyType) != null || !propertyType.IsValueType;
        var baseType = Nullable.GetUnderlyingType(propertyType) ?? propertyType;

        // Common property name patterns
        if (propertyName.Equals("Id", StringComparison.OrdinalIgnoreCase))
            return $"Unique identifier for {declaringType.Name}";
        if (propertyName.Equals("Name", StringComparison.OrdinalIgnoreCase))
            return $"Name of the {declaringType.Name}";
        if (propertyName.Contains("Email", StringComparison.OrdinalIgnoreCase))
            return "Email address";
        if (propertyName.Contains("Phone", StringComparison.OrdinalIgnoreCase))
            return "Phone number";
        if (propertyName.Contains("Address", StringComparison.OrdinalIgnoreCase))
            return "Physical or mailing address";
        if (propertyName.Contains("Date", StringComparison.OrdinalIgnoreCase) || baseType == typeof(DateTime))
            return $"Date/time value for {propertyName}";
        if (propertyName.StartsWith("Is", StringComparison.OrdinalIgnoreCase) && baseType == typeof(bool))
            return $"Boolean flag indicating {propertyName.Substring(2)}";
        if (propertyName.StartsWith("Has", StringComparison.OrdinalIgnoreCase) && baseType == typeof(bool))
            return $"Boolean flag indicating possession of {propertyName.Substring(3)}";
        if (propertyName.EndsWith("Count", StringComparison.OrdinalIgnoreCase) && IsIntegerType(baseType))
            return $"Count of {propertyName.Replace("Count", "")}";

        // Collection types (csak ha tényleg nem object az elem)
        var elementType = GetCollectionElementType(propertyType);
        if (elementType != null && elementType != typeof(object))
            return $"Collection of {elementType.Name} for {declaringType.Name}";

        // Dictionary types
        if (IsDictionaryType(propertyType, out _, out _))
            return $"Dictionary mapping for {propertyName} in {declaringType.Name}";

        // Default
        return $"Property {propertyName} of type {baseType.Name}{(isNullable ? " (nullable)" : "")}";
    }

    /// <summary>
    /// Get property constraints (nullable, required, etc.).
    /// </summary>
    private static string GetPropertyConstraints(Type propertyType, string propertyName)
    {
        var constraints = new List<string>();

        var isNullable = Nullable.GetUnderlyingType(propertyType) != null || !propertyType.IsValueType;
        if (isNullable)
            constraints.Add("nullable");
        else
            constraints.Add("required");

        var baseType = Nullable.GetUnderlyingType(propertyType) ?? propertyType;

        // Type-specific constraints
        if (baseType == typeof(string))
        {
            if (propertyName.Contains("Email", StringComparison.OrdinalIgnoreCase))
                constraints.Add("email-format");
            if (propertyName.Contains("Url", StringComparison.OrdinalIgnoreCase))
                constraints.Add("url-format");
        }

        if (IsIntegerType(baseType))
        {
            if (propertyName.Contains("Age", StringComparison.OrdinalIgnoreCase))
                constraints.Add("range: 0-150");
            else if (propertyName.EndsWith("Count", StringComparison.OrdinalIgnoreCase))
                constraints.Add("non-negative");
        }

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

    /// <summary>
    /// Get property purpose (what it's used for).
    /// </summary>
    private static string GetPropertyPurpose(Type declaringType, string propertyName)
    {
        // Common purposes based on property patterns
        if (propertyName.Equals("Id", StringComparison.OrdinalIgnoreCase))
            return "Primary key / unique identification";
        if (propertyName.Contains("CreatedAt", StringComparison.OrdinalIgnoreCase))
            return "Timestamp when entity was created";
        if (propertyName.Contains("UpdatedAt", StringComparison.OrdinalIgnoreCase) ||
            propertyName.Contains("ModifiedAt", StringComparison.OrdinalIgnoreCase))
            return "Timestamp of last update";
        if (propertyName.Contains("DeletedAt", StringComparison.OrdinalIgnoreCase))
            return "Soft delete timestamp";
        if (propertyName.StartsWith("Is", StringComparison.OrdinalIgnoreCase))
            return "Status flag";
        if (propertyName.Contains("Version", StringComparison.OrdinalIgnoreCase))
            return "Version tracking / concurrency control";

        return "";
    }

    /// <summary>
    /// Check if type is an integer type.
    /// </summary>
    private static bool IsIntegerType(Type type)
    {
        var typeCode = Type.GetTypeCode(type);
        return typeCode is TypeCode.Byte or TypeCode.SByte or TypeCode.Int16 or
               TypeCode.UInt16 or TypeCode.Int32 or TypeCode.UInt32 or
               TypeCode.Int64 or TypeCode.UInt64;
    }

    /// <summary>
    /// Get final property description with fallback chain and placeholder resolution.
    /// Priority: ToonDescription (with placeholders) > Microsoft [Description] > null (no fallback)
    /// Returns null if no explicit description is provided (to avoid redundant output).
    /// </summary>
    private static string? GetFinalPropertyDescription(ToonPropertyAccessor prop, Type declaringType)
    {
        // 1. ToonDescription.Description (if not empty)
        var customDesc = prop.CustomDescription?.Description;

        if (!string.IsNullOrWhiteSpace(customDesc))
        {
            // Has [#...] placeholder? → Resolve it
            if (customDesc.Contains("[#"))
            {
                return ResolveDescriptionPlaceholders(customDesc, prop, declaringType);
            }
            // No placeholder → use as-is
            return customDesc;
        }

        // 2. Microsoft [Description] attribute
        var msDesc = prop.PropertyInfo.GetCustomAttribute<DescriptionAttribute>();
        if (msDesc != null && !string.IsNullOrWhiteSpace(msDesc.Description))
            return msDesc.Description;

        // 3. No fallback - return null to avoid redundant output
        return null;
    }

    /// <summary>
    /// Get final property purpose with fallback chain and placeholder resolution.
    /// Priority: ToonDescription.Purpose (with placeholders) > Smart inference
    /// </summary>
    private static string GetFinalPropertyPurpose(ToonPropertyAccessor prop, Type declaringType)
    {
        var customPurpose = prop.CustomDescription?.Purpose;

        if (!string.IsNullOrWhiteSpace(customPurpose))
        {
            // Has [#...] placeholder? → Resolve it
            if (customPurpose.Contains("[#"))
            {
                return ResolvePurposePlaceholders(customPurpose, prop, declaringType);
            }
            return customPurpose;
        }

        // Fallback: Smart inference
        return GetPropertyPurpose(declaringType, prop.Name);
    }

    /// <summary>
    /// Get final property constraints with fallback chain and placeholder resolution.
    /// Priority: ToonDescription.Constraints (with placeholders merged) > Microsoft attributes > Type constraints > Smart inference
    /// </summary>
    private static string GetFinalPropertyConstraints(ToonPropertyAccessor prop, Type declaringType)
    {
        var customConstraints = prop.CustomDescription?.Constraints;

        // --- NotMapped/NotColumn detection ---
        var notMapped = false;
        var attrs = prop.PropertyInfo.GetCustomAttributes(inherit: true);
        foreach (var attr in attrs)
        {
            var attrName = attr.GetType().Name;
            if (attrName == "NotMappedAttribute" || attrName == "NotColumnAttribute")
            {
                notMapped = true;
                break;
            }
        }

        string result;

        if (!string.IsNullOrWhiteSpace(customConstraints))
        {
            // Has [#...] placeholder? → Resolve and merge
            if (customConstraints.Contains("[#"))
            {
                // Resolve placeholders first
                var resolved = ResolveConstraintPlaceholders(customConstraints, prop);

                // Merge with type/smart constraints if resolved contains content
                if (!string.IsNullOrWhiteSpace(resolved))
                {
                    var typeConstraints = ExtractTypeConstraints(prop.PropertyType);
                    var inferredConstraints = GetInferredConstraints(prop.PropertyType, prop.Name);
                    result = MergeConstraints(typeConstraints, null, inferredConstraints, resolved);
                }
                else
                {
                    result = string.Empty;
                }
            }
            else
            {
                // No placeholder → custom wins (replace mode)
                result = customConstraints;
            }
        }
        else
        {
            // No custom constraint → Fallback chain
            var msConstraints = ExtractDataAnnotationConstraints(prop.PropertyInfo);
            var typeConstraints2 = ExtractTypeConstraints(prop.PropertyType);
            var inferredConstraints2 = GetInferredConstraints(prop.PropertyType, prop.Name);
            result = MergeConstraints(typeConstraints2, msConstraints, inferredConstraints2, null);
        }

        // Add readonly constraint if needed
        if (IsReadOnlyProperty(prop.PropertyInfo) && !result.Contains("readonly"))
        {
            result = string.IsNullOrEmpty(result) ? "readonly" : result + ", readonly";
        }

        // Add not-mapped constraint if needed (only once, at the end)
        if (notMapped && !result.Contains("not-mapped"))
        {
            result = string.IsNullOrEmpty(result) ? "not-mapped" : result + ", not-mapped";
        }

        // Add enum-type constraint if this is an enum backing field
        var enumType = FindEnumTypeForBackingField(prop.PropertyInfo, declaringType);
        if (enumType != null && !result.Contains("enum-type:"))
        {
            var enumConstraint = $"enum-type: {enumType.Name}";
            result = string.IsNullOrEmpty(result) ? enumConstraint : result + ", " + enumConstraint;
        }

        return result;
    }

    /// <summary>
    /// Get final property examples with placeholder resolution.
    /// </summary>
    private static string GetFinalPropertyExamples(ToonPropertyAccessor prop)
    {
        var customExamples = prop.CustomDescription?.Examples;

        if (!string.IsNullOrWhiteSpace(customExamples))
        {
            // Has [#...] placeholder? → Resolve it
            if (customExamples.Contains("[#"))
            {
                return ResolveExamplesPlaceholders(customExamples, prop);
            }
            return customExamples;
        }

        // No examples
        return string.Empty;
    }

    /// <summary>
    /// Get final type description with fallback chain and placeholder resolution.
    /// Priority: ToonDescription (with placeholders) > Microsoft [Description] > Smart inference
    /// </summary>
    private static string GetFinalTypeDescription(Type type, ToonSerializeTypeMetadata metadata)
    {
        // 1. ToonDescription.Description (if not empty)
        var customDesc = metadata.CustomDescription?.Description;

        if (!string.IsNullOrWhiteSpace(customDesc))
        {
            // Has [#...] placeholder? → Resolve it
            if (customDesc.Contains("[#"))
            {
                return ResolveTypeDescriptionPlaceholders(customDesc, type);
            }
            // No placeholder → use as-is
            return customDesc;
        }

        // 2. Microsoft [Description] attribute
        var msDesc = type.GetCustomAttribute<System.ComponentModel.DescriptionAttribute>();
        if (msDesc != null && !string.IsNullOrWhiteSpace(msDesc.Description))
            return msDesc.Description;

        // 3. Smart inference (fallback)
        return GetTypeDescription(type);
    }

    /// <summary>
    /// Get final type purpose with fallback chain and placeholder resolution.
    /// Priority: ToonDescription.Purpose (with placeholders) > Smart inference (empty for classes)
    /// </summary>
    private static string GetFinalTypePurpose(Type type, ToonSerializeTypeMetadata metadata)
    {
        var customPurpose = metadata.CustomDescription?.Purpose;

        if (!string.IsNullOrWhiteSpace(customPurpose))
        {
            // Has [#...] placeholder? → Resolve it
            if (customPurpose.Contains("[#"))
            {
                return ResolveTypePurposePlaceholders(customPurpose, type);
            }
            return customPurpose;
        }

        // No smart inference for class-level purpose - return empty
        return string.Empty;
    }

    /// <summary>
    /// Get final enum description with fallback chain.
    /// Priority: ToonDescription.Description -> Microsoft [Description] -> null (no fallback)
    /// Returns null if no explicit description to avoid redundancy (values already in values: section).
    /// </summary>
    private static string? GetFinalEnumDescription(Type enumType, ToonDescriptionAttribute? customDescription)
    {
        // 1. ToonDescription.Description
        if (!string.IsNullOrWhiteSpace(customDescription?.Description))
        {
            return customDescription.Description;
        }

        // 2. Microsoft [Description] attribute
        var msDesc = enumType.GetCustomAttribute<System.ComponentModel.DescriptionAttribute>();
        if (msDesc != null && !string.IsNullOrWhiteSpace(msDesc.Description))
        {
            return msDesc.Description;
        }

        // 3. No fallback - avoid redundancy (values already listed in values: section)
        return null;
    }
}
