using System;
using System.Collections.Generic;

namespace AyCode.Core.Serializers.Toons;

/// <summary>
/// Defines the type of relationship between entities.
/// </summary>
public enum ToonRelationType
{
    /// <summary>
    /// Many-to-one relationship (e.g., Person.Company -> Company).
    /// </summary>
    ManyToOne,

    /// <summary>
    /// One-to-many relationship (e.g., Company.Employees -> Person[]).
    /// </summary>
    OneToMany,

    /// <summary>
    /// One-to-one relationship.
    /// </summary>
    OneToOne,

    /// <summary>
    /// Many-to-many relationship.
    /// </summary>
    ManyToMany
}

/// <summary>
/// Provides custom description metadata for Toon serialization with flexible fallback and placeholder support.
/// This attribute can be applied to classes and properties to provide rich contextual information
/// that will be included in the @types section of serialized output.
///
/// <para><b>KEY FEATURES:</b></para>
/// <list type="bullet">
///   <item><b>Partial Support:</b> You can specify only some properties (e.g., just Constraints), others will use fallbacks</item>
///   <item><b>Placeholder System:</b> Use [#AttributeName] to reference Microsoft DataAnnotations or smart inference</item>
///   <item><b>Fallback Chain:</b> ToonDescription → Microsoft Attributes → Smart Inference (automatic)</item>
/// </list>
///
/// <para><b>FALLBACK PRIORITIES:</b></para>
/// <list type="number">
///   <item><b>Description:</b> ToonDescription.Description → [Description] → Smart Inference</item>
///   <item><b>Purpose:</b> ToonDescription.Purpose → Smart Inference</item>
///   <item><b>Constraints:</b> ToonDescription.Constraints → [Range]/[Required]/etc → Type Constraints → Smart Inference</item>
///   <item><b>Examples:</b> ToonDescription.Examples (no automatic fallback)</item>
/// </list>
/// </summary>
///
/// <remarks>
/// <para><b>SUPPORTED PLACEHOLDERS:</b></para>
/// <list type="table">
///   <listheader>
///     <term>Placeholder</term>
///     <description>Resolves To (Where: C=Class, D=Description, P=Purpose, C=Constraints, E=Examples)</description>
///   </listheader>
///   <item><term>[#Description]</term><description>Microsoft [Description] attribute (Class.D, Property.D)</description></item>
///   <item><term>[#DisplayName]</term><description>Microsoft [DisplayName] attribute (Class.D, Property.D)</description></item>
///   <item><term>[#SmartDescription]</term><description>Auto-inferred description (Class.D, Property.D)</description></item>
///   <item><term>[#SmartPurpose]</term><description>Auto-inferred purpose (Property.P only, empty for classes)</description></item>
///   <item><term>[#Range]</term><description>Microsoft [Range] attribute → "range: min-max" (Property.C)</description></item>
///   <item><term>[#Required]</term><description>Microsoft [Required] attribute → "required" (Property.C)</description></item>
///   <item><term>[#MaxLength]</term><description>Microsoft [MaxLength] attribute → "max-length: N" (Property.C)</description></item>
///   <item><term>[#MinLength]</term><description>Microsoft [MinLength] attribute → "min-length: N" (Property.C)</description></item>
///   <item><term>[#StringLength]</term><description>Microsoft [StringLength] attribute → "length: min-max" (Property.C)</description></item>
///   <item><term>[#EmailAddress]</term><description>Microsoft [EmailAddress] attribute → "email-format" (Property.C)</description></item>
///   <item><term>[#Phone]</term><description>Microsoft [Phone] attribute → "phone-format" (Property.C)</description></item>
///   <item><term>[#Url]</term><description>Microsoft [Url] attribute → "url-format" (Property.C)</description></item>
///   <item><term>[#CreditCard]</term><description>Microsoft [CreditCard] attribute → "credit-card-format" (Property.C)</description></item>
///   <item><term>[#RegularExpression]</term><description>Microsoft [RegularExpression] → "pattern: ..." (Property.C)</description></item>
///   <item><term>[#SmartTypeConstraints]</term><description>Type-derived constraints (nullable, numeric, etc.) (Property.C)</description></item>
///   <item><term>[#SmartInferenceConstraints]</term><description>Auto-inferred constraints (email-format, range, etc.) (Property.C)</description></item>
///   <item><term>[#SmartGeneratedExample]</term><description>Auto-generated example value (Property.E)</description></item>
/// </list>
///
/// <para><b>USAGE MODES:</b></para>
///
/// <para><b>1. FULL CUSTOM (all properties specified):</b></para>
/// <code>
/// [ToonDescription("User email address",
///                  Purpose = "Authentication and notifications",
///                  Constraints = "required, email-format, unique",
///                  Examples = "user@example.com, admin@company.com")]
/// public string Email { get; set; }
/// </code>
///
/// <para><b>2. PARTIAL (only some properties, rest auto-filled):</b></para>
/// <code>
/// [Description("Contact email")]  // Microsoft attribute
/// [Required]
/// [EmailAddress]
/// [ToonDescription(Constraints = "[#Required], [#EmailAddress], unique")]  // Only constraints specified
/// public string Email { get; set; }
/// // Result:
/// //   Description: "Contact email" (from Microsoft [Description])
/// //   Constraints: "required, email-format, unique" (merged with placeholders)
/// </code>
///
/// <para><b>3. PLACEHOLDER APPEND MODE (merge with Microsoft attributes):</b></para>
/// <code>
/// [Range(0, 150)]
/// [Required]
/// [ToonDescription(Constraints = "[#Required], [#Range], verified-by-admin")]
/// public int Age { get; set; }
/// // Result: "required, range: 0-150, verified-by-admin" (merged)
/// </code>
///
/// <para><b>4. REPLACE MODE (no placeholders = full override):</b></para>
/// <code>
/// [Range(0, 150)]  // This is IGNORED
/// [ToonDescription(Constraints = "custom-validation-only")]
/// public int Score { get; set; }
/// // Result: "custom-validation-only" (Microsoft attributes ignored)
/// </code>
///
/// <para><b>5. FULL AUTOMATIC (no ToonDescription at all):</b></para>
/// <code>
/// [Required]
/// [EmailAddress]
/// [MaxLength(100)]
/// public string Email { get; set; }
/// // Result:
/// //   Description: "Email address" (smart inference)
/// //   Constraints: "required, email-format, max-length: 100" (from Microsoft attributes)
/// </code>
///
/// <para><b>6. COMBINING PLACEHOLDERS IN DESCRIPTION:</b></para>
/// <code>
/// [DisplayName("User Email")]
/// [Description("Contact email address")]
/// [ToonDescription(Description = "[#DisplayName]: [#Description] (primary contact)")]
/// public string Email { get; set; }
/// // Result: "User Email: Contact email address (primary contact)"
/// </code>
///
/// <para><b>7. CLASS-LEVEL WITH PLACEHOLDERS AND FALLBACK:</b></para>
/// <code>
/// // With Microsoft attribute
/// [Description("Base user entity")]
/// [ToonDescription("[#Description] with extended functionality",
///                  Purpose = "Manages user authentication and authorization")]
/// public class User { }
/// // Result:
/// //   Description: "Base user entity with extended functionality"
/// //   Purpose: "Manages user authentication and authorization"
///
/// // With inheritance (Inherited = true)
/// [ToonDescription("Admin user account",
///                  Purpose = "Administrative users with elevated privileges")]
/// public class AdminUser : User { }
/// // Result: "Admin user account" (own description)
///
/// public class SuperUser : AdminUser { }
/// // Result: "Admin user account" (inherited from AdminUser)
///
/// // Without any ToonDescription
/// public class GuestUser : User { }
/// // Result: "Object of type GuestUser" (smart inference)
/// </code>
///
/// <para><b>BEST PRACTICES:</b></para>
/// <list type="bullet">
///   <item>Use placeholders ([#...]) when you want to MERGE with existing Microsoft attributes</item>
///   <item>Omit placeholders when you want to REPLACE (full custom control)</item>
///   <item>Leave properties empty to use automatic fallbacks</item>
///   <item>Combine placeholders with custom text for rich, DRY documentation</item>
/// </list>
/// </remarks>
///
/// <example>
/// <para><b>COMPLETE EXAMPLE:</b></para>
/// <code>
/// [ToonDescription("Represents a user account in the system",
///                  Purpose = "User authentication and profile management")]
/// public class Person
/// {
///     // Full custom
///     [ToonDescription("Unique identifier",
///                      Purpose = "Primary key / database identity",
///                      Constraints = "required, auto-increment, positive")]
///     public int Id { get; set; }
///
///     // Merge with Microsoft attributes
///     [Range(18, 120)]
///     [Required]
///     [ToonDescription(Constraints = "[#Required], [#Range], verified")]
///     public int Age { get; set; }
///     // Result: "required, range: 18-120, verified"
///
///     // Partial - only constraints, rest auto-filled
///     [Description("Contact email")]
///     [EmailAddress]
///     [ToonDescription(Constraints = "[#EmailAddress], unique")]
///     public string Email { get; set; }
///     // Description: "Contact email" (from Microsoft)
///     // Constraints: "email-format, unique" (merged)
///
///     // Fully automatic - no ToonDescription
///     [Required]
///     [MaxLength(100)]
///     public string Name { get; set; }
///     // Description: "Name of the Person" (smart inference)
///     // Constraints: "required, max-length: 100" (from Microsoft attributes)
///
///     // Placeholder in description
///     [DisplayName("Account Balance")]
///     [ToonDescription(Description = "[#DisplayName] in USD",
///                      Constraints = "range: 0-999999, decimal(18,2)",
///                      Examples = "0.00, 1234.56, 999999.99")]
///     public decimal Balance { get; set; }
///     // Description: "Account Balance in USD"
/// }
/// </code>
/// </example>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
public sealed class ToonDescriptionAttribute : Attribute
{
    /// <summary>
    /// Gets or sets the human-readable description of the property or type.
    /// This appears in the @types section to help LLMs understand the data structure.
    /// If not specified, fallback chain will be used (Microsoft [Description] -> Smart inference).
    /// </summary>
    public string? Description { get; set; }

    /// <summary>
    /// Gets or sets the purpose of this property (what it's used for).
    /// Examples: "Primary key", "User authentication", "Audit trail".
    /// </summary>
    public string? Purpose { get; set; }

    /// <summary>
    /// Gets or sets the constraints or validation rules for this property.
    /// Examples: "required, email-format", "range: 0-150", "max-length: 100".
    /// </summary>
    public string? Constraints { get; set; }

    /// <summary>
    /// Gets or sets example values for this property.
    /// Helps LLMs understand the expected format and content.
    /// </summary>
    public string? Examples { get; set; }

    /// <summary>
    /// Gets or sets whether this property is a primary key.
    /// If not explicitly set, convention-based detection will be used (e.g., property named "Id").
    /// </summary>
    public bool? IsPrimaryKey { get; set; }

    /// <summary>
    /// Gets or sets the foreign key navigation property name.
    /// If not explicitly set, convention-based detection will be used (e.g., "CompanyId" -> "Company").
    /// Example: For a property "CompanyId", set ForeignKey = "Company" to indicate it references the Company navigation property.
    /// </summary>
    public string? ForeignKey { get; set; }

    /// <summary>
    /// Gets or sets the relationship type for navigation properties.
    /// If not explicitly set, convention-based detection will be used based on property type.
    /// </summary>
    public ToonRelationType? Navigation { get; set; }

    /// <summary>
    /// Gets or sets the inverse navigation property name for bidirectional relationships.
    /// Example: For Company.Employees, set InverseProperty = "Company" to indicate the inverse property on Person.
    /// </summary>
    public string? InverseProperty { get; set; }

    /// <summary>
    /// Gets or sets the business rule or validation logic for this property.
    /// Uses a pseudo-code expression format that is both LLM-readable and developer-friendly.
    /// The expression uses "this" to refer to the current property value.
    /// Examples:
    ///   - "this >= NetWeight" (comparison with other property)
    ///   - "this > 0" (simple constraint)
    ///   - "this.Length <= 100" (property constraint)
    ///   - "this != null && this.Count > 0" (compound rule)
    /// Note: This is descriptive only, not executed. Actual validation should use FluentValidation or similar.
    /// </summary>
    public string? BusinessRule { get; set; }

    /// <summary>
    /// Gets or sets the database table name for this entity (class-level only).
    /// If not explicitly set, will fallback to EF Core [Table] or Linq2Db [Table] attributes, then to class name.
    /// Example: TableName = "tbl_Persons" for custom table naming.
    /// </summary>
    public string? TableName { get; set; }

    /// <summary>
    /// Gets or sets the type relationship string (class-level only).
    /// Use constants from ToonTypeRelation class (e.g., ToonTypeRelation.DtoOf).
    /// Example: TypeRelation = ToonTypeRelation.DtoOf
    /// </summary>
    public string? TypeRelation { get; set; }

    /// <summary>
    /// Gets or sets the related types for the type relationship (class-level only).
    /// Used together with TypeRelation to declare type relationships.
    /// Example: RelatedTypes = new[] { typeof(Order), typeof(OrderModel) }
    /// </summary>
    public Type[]? RelatedTypes { get; set; }

    /// <summary>
    /// Initializes a new instance of the ToonDescriptionAttribute.
    /// All properties are optional and can be set using property initializers.
    /// </summary>
    public ToonDescriptionAttribute()
    {
    }

    /// <summary>
    /// Initializes a new instance of the ToonDescriptionAttribute with the specified description.
    /// </summary>
    /// <param name="description">Human-readable description of the property or type.</param>
    public ToonDescriptionAttribute(string description)
    {
        Description = description;
    }

    /// <summary>
    /// Returns a string representation of this attribute for debugging.
    /// </summary>
    public override string ToString()
    {
        var parts = new List<string> { $"Description: {Description}" };
        if (!string.IsNullOrEmpty(Purpose))
            parts.Add($"Purpose: {Purpose}");
        if (!string.IsNullOrEmpty(Constraints))
            parts.Add($"Constraints: {Constraints}");
        if (!string.IsNullOrEmpty(Examples))
            parts.Add($"Examples: {Examples}");
        return string.Join(", ", parts);
    }
}
