using Extrudex.Domain.Base;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
namespace Extrudex.Infrastructure.Data.Configurations;
///
/// Base configuration for all entities. Sets up common conventions:
/// - Table names in snake_case
/// - GUID primary keys stored as PostgreSQL UUID
/// - Automatic timestamp columns in snake_case
///
/// The entity type to configure.
public abstract class BaseEntityConfiguration : IEntityTypeConfiguration
where TEntity : BaseEntity
{
public virtual void Configure(EntityTypeBuilder builder)
{
// Table name in snake_case
builder.ToTable(ToSnakeCase(typeof(TEntity).Name));
// Primary key stored as UUID
builder.HasKey(e => e.Id);
builder.Property(e => e.Id)
.HasColumnName("id")
.ValueGeneratedNever();
// If the entity is auditable, configure the timestamp columns
if (typeof(AuditableEntity).IsAssignableFrom(typeof(TEntity)))
{
ConfigureAuditColumns(builder);
}
}
///
/// Configures audit timestamp columns (created_at, updated_at) for auditable entities.
/// Uses string-based property names since the generic type constraint is BaseEntity
/// and cannot be cast to AuditableEntity at compile time.
///
private static void ConfigureAuditColumns(EntityTypeBuilder builder)
{
builder.Property("CreatedAt")
.HasColumnName("created_at")
.HasDefaultValueSql("now() at time zone 'utc'");
builder.Property("UpdatedAt")
.HasColumnName("updated_at")
.HasDefaultValueSql("now() at time zone 'utc'");
}
///
/// Converts PascalCase or camelCase to snake_case.
///
protected static string ToSnakeCase(string name)
{
return string.Concat(
name.Select((ch, i) =>
i > 0 && char.IsUpper(ch) && (char.IsLower(name[i - 1]) || (i + 1 < name.Length && char.IsLower(name[i + 1])))
? "_" + ch
: ch.ToString()))
.ToLowerInvariant();
}
}