using System.Reflection;

namespace AyCode.Core.Serializers.Toons;

public static partial class AcToonSerializer
{
    /// <summary>
    /// Write type definition with property descriptions.
    /// </summary>
    private static void WriteTypeDefinition(Type type, ToonSerializationContext context)
    {
        // Handle enum types specially
        if (type.IsEnum)
        {
            WriteEnumTypeDefinition(type, context);
            return;
        }

        var metadata = GetTypeMetadata(type);

        // Type description with fallback chain and placeholder resolution
        var typeDescription = GetFinalTypeDescription(type, metadata);
        context.WriteIndentedLine($"{type.Name}: \"{typeDescription}\"");

        // Növeljük az indent-et a type body-hoz (type-level metadata ÉS property-k)
        context.CurrentIndentLevel++;

        // Type-level metadata (enhanced mode only)
        if (context.Options.UseEnhancedMetadata)
        {
            // Table name
            var tableName = DetectTableName(type, metadata.CustomDescription);
            if (!string.IsNullOrEmpty(tableName))
            {
                context.WriteIndentedLine($"table-name: \"{tableName}\"");
            }

            // Related type
            var typeRelation = metadata.CustomDescription?.TypeRelation;
            var relatedTypes = metadata.CustomDescription?.RelatedTypes;
            if (!string.IsNullOrEmpty(typeRelation) && relatedTypes != null && relatedTypes.Length > 0)
            {
                var relatedTypeStr = FormatRelatedType(typeRelation, relatedTypes);
                if (!string.IsNullOrEmpty(relatedTypeStr))
                {
                    context.WriteIndentedLine($"related-type: \"{relatedTypeStr}\"");
                }
            }

            // Purpose
            var typePurpose = GetFinalTypePurpose(type, metadata);
            if (!string.IsNullOrEmpty(typePurpose))
            {
                context.WriteIndentedLine($"purpose: \"{typePurpose}\"");
            }
        }

        if (metadata.Properties.Length == 0)
        {
            context.CurrentIndentLevel--;
            return;
        }

        // Property descriptions (már indent szint 1-en vagyunk, property-k is ide kerülnek)
        foreach (var prop in metadata.Properties)
        {
            // Get final values with fallback chain + placeholder resolution
            var propDescription = GetFinalPropertyDescription(prop, type);
            var purpose = GetFinalPropertyPurpose(prop, type);
            var constraints = GetFinalPropertyConstraints(prop, type);
            var examples = GetFinalPropertyExamples(prop);

            // Use cached navigation metadata from property accessor
            var nav = prop.Navigation;

            var typeHint = prop.TypeDisplayName;

            if (context.Options.UseEnhancedMetadata)
            {
                // Enhanced format with constraints and purpose
                context.WriteIndentedLine($"{prop.Name}: {typeHint}");
                context.CurrentIndentLevel++;

                // Only write description if explicitly provided (not null/empty)
                if (!string.IsNullOrEmpty(propDescription))
                    context.WriteIndentedLine($"description: \"{propDescription}\"");

                if (!string.IsNullOrEmpty(purpose))
                    context.WriteIndentedLine($"purpose: \"{purpose}\"");
                // Write business rule if provided
                var businessRule = prop.CustomDescription?.BusinessRule;
                if (!string.IsNullOrEmpty(businessRule))
                    context.WriteIndentedLine($"business-logic: \"{businessRule}\"");

                if (!string.IsNullOrEmpty(constraints))
                    context.WriteIndentedLine($"constraints: \"{constraints}\"");
                if (!string.IsNullOrEmpty(examples))
                    context.WriteIndentedLine($"examples: \"{examples}\"");

                // Add relationship metadata from cached navigation info
                if (nav.IsPrimaryKey)
                    context.WriteIndentedLine($"primary-key: true");

                // Only write foreign-key for navigation properties, not for FK properties themselves
                if (!string.IsNullOrEmpty(nav.ForeignKey) && nav.NavigationType.HasValue)
                    context.WriteIndentedLine($"foreign-key: \"{nav.ForeignKey}\"");

                if (!string.IsNullOrEmpty(nav.OtherKey))
                    context.WriteIndentedLine($"other-key: \"{nav.OtherKey}\"");
                if (nav.NavigationType.HasValue)
                {
                    var navType = nav.NavigationType.Value.ToString();
                    var navTypeFormatted = string.Concat(navType.Select((c, i) => i > 0 && char.IsUpper(c) ? "-" + char.ToLower(c) : char.ToLower(c).ToString()));
                    context.WriteIndentedLine($"navigation: \"{navTypeFormatted}\"");
                }
                if (!string.IsNullOrEmpty(nav.InverseProperty))
                    context.WriteIndentedLine($"inverse-property: \"{nav.InverseProperty}\"");

                context.CurrentIndentLevel--;
            }
            else
            {
                // Simple format
                var relationshipHint = GetRelationshipHint(nav);

                // Only write description if there's explicit description OR relationship metadata
                if (!string.IsNullOrEmpty(propDescription) || !string.IsNullOrEmpty(relationshipHint))
                {
                    var fullDescription = string.IsNullOrEmpty(relationshipHint)
                        ? propDescription
                        : string.IsNullOrEmpty(propDescription)
                            ? relationshipHint
                            : $"{propDescription} ({relationshipHint})";
                    context.WriteIndentedLine($"{prop.Name}: {typeHint} \"{fullDescription}\"");
                }
                else
                {
                    // No description and no relationship metadata - just write type hint
                    context.WriteIndentedLine($"{prop.Name}: {typeHint}");
                }
            }
        }
        context.CurrentIndentLevel--;

        // Token optimization: skip empty line when indentation is disabled
        if (context.Options.UseIndentation)
        {
            context.WriteLine();
        }
    }

    /// <summary>
    /// Write enum type definition with values and metadata.
    /// </summary>
    private static void WriteEnumTypeDefinition(Type enumType, ToonSerializationContext context)
    {
        var customDescription = enumType.GetCustomAttribute<ToonDescriptionAttribute>();

        // Type description with fallback
        var typeDescription = GetFinalEnumDescription(enumType, customDescription);
        context.WriteIndentedLine($"{enumType.Name}: enum");

        if (context.Options.UseEnhancedMetadata)
        {
            context.CurrentIndentLevel++;

            // Description
            if (!string.IsNullOrEmpty(typeDescription))
            {
                context.WriteIndentedLine($"description: \"{typeDescription}\"");
            }

            // Purpose
            var typePurpose = customDescription?.Purpose;
            if (!string.IsNullOrEmpty(typePurpose))
            {
                context.WriteIndentedLine($"purpose: \"{typePurpose}\"");
            }

            // Underlying type
            var underlyingType = Enum.GetUnderlyingType(enumType);
            var underlyingTypeName = AcSerializerCommon.GetCSharpTypeName(underlyingType, useShortNames: true);
            context.WriteIndentedLine($"underlying-type: \"{underlyingTypeName}\"");

            // Default value (first enum member)
            var enumValues = Enum.GetValues(enumType);
            if (enumValues.Length > 0)
            {
                var firstValue = enumValues.GetValue(0);
                var firstValueNumeric = Convert.ChangeType(firstValue, underlyingType);
                context.WriteIndentedLine($"default-value: {firstValueNumeric}");
            }

            // Enum members with descriptions
            context.WriteIndentedLine("values:");
            context.CurrentIndentLevel++;

            var names = Enum.GetNames(enumType);
            foreach (var name in names)
            {
                var field = enumType.GetField(name);
                var value = field?.GetValue(null);
                var numericValue = Convert.ChangeType(value, underlyingType);

                // Get member description from ToonDescription attribute
                var memberDescription = field?.GetCustomAttribute<ToonDescriptionAttribute>();
                var description = memberDescription?.Description;

                // Token optimization: format depends on indentation setting
                var separator = context.Options.UseIndentation ? " = " : "=";

                if (!string.IsNullOrEmpty(description))
                {
                    context.WriteIndentedLine($"{name}{separator}{numericValue}");
                    context.CurrentIndentLevel++;
                    context.WriteIndentedLine($"description: \"{description}\"");
                    context.CurrentIndentLevel--;
                }
                else
                {
                    context.WriteIndentedLine($"{name}{separator}{numericValue}");
                }
            }

            context.CurrentIndentLevel--;
            context.CurrentIndentLevel--;
        }
        else
        {
            // Simple format - just show values
            var names = Enum.GetNames(enumType);
            var underlyingType = Enum.GetUnderlyingType(enumType);
            context.CurrentIndentLevel++;

            // Token optimization: format depends on indentation setting
            var separator = context.Options.UseIndentation ? " = " : "=";

            foreach (var name in names)
            {
                var field = enumType.GetField(name);
                var value = field?.GetValue(null);
                var numericValue = Convert.ChangeType(value, underlyingType);
                context.WriteIndentedLine($"{name}{separator}{numericValue}");
            }

            context.CurrentIndentLevel--;
        }

        // Token optimization: skip empty line when indentation is disabled
        if (context.Options.UseIndentation)
        {
            context.WriteLine();
        }
    }

    /// <summary>
    /// Get relationship hint string for simple format output.
    /// </summary>
    private static string GetRelationshipHint(AcNavigationPropertyInfo nav)
    {
        var hints = new List<string>();

        if (nav.IsPrimaryKey)
            hints.Add("primary-key");

        if (!string.IsNullOrEmpty(nav.ForeignKey))
            hints.Add($"fk->{nav.ForeignKey}");

        if (!string.IsNullOrEmpty(nav.OtherKey))
            hints.Add($"other-key:{nav.OtherKey}");

        if (nav.NavigationType.HasValue)
        {
            var navType = nav.NavigationType.Value switch
            {
                ToonRelationType.ManyToOne => "many-to-one",
                ToonRelationType.OneToMany => "one-to-many",
                ToonRelationType.OneToOne => "one-to-one",
                ToonRelationType.ManyToMany => "many-to-many",
                _ => null
            };
            if (navType != null)
                hints.Add(navType);
        }

        if (!string.IsNullOrEmpty(nav.InverseProperty))
            hints.Add($"inverse:{nav.InverseProperty}");

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

    /// <summary>
    /// Format related type metadata for output.
    /// Example: "dto-of Order, OrderModel"
    /// </summary>
    private static string FormatRelatedType(string typeRelation, Type[] relatedTypes)
    {
        if (string.IsNullOrEmpty(typeRelation) || relatedTypes == null || relatedTypes.Length == 0)
            return string.Empty;

        var typeNames = string.Join(", ", relatedTypes.Select(t => t.Name));
        return $"{typeRelation} {typeNames}";
    }
}
