using Extrudex.Domain.Base;
using Extrudex.Domain.Entities;
using Microsoft.EntityFrameworkCore;

namespace Extrudex.Infrastructure.Data;

/// <summary>
/// Main EF Core database context for the Extrudex system.
/// Handles entity registration, snake_case naming, and automatic timestamp management.
/// </summary>
public class ExtrudexDbContext : DbContext
{
    public ExtrudexDbContext(DbContextOptions<ExtrudexDbContext> options) : base(options) { }

    // Lookup tables
    public DbSet<MaterialBase> MaterialBases => Set<MaterialBase>();
    public DbSet<MaterialFinish> MaterialFinishes => Set<MaterialFinish>();
    public DbSet<MaterialModifier> MaterialModifiers => Set<MaterialModifier>();

    // Core entities
    public DbSet<Spool> Spools => Set<Spool>();
    public DbSet<Printer> Printers => Set<Printer>();
    public DbSet<AmsUnit> AmsUnits => Set<AmsUnit>();
    public DbSet<AmsSlot> AmsSlots => Set<AmsSlot>();
    public DbSet<PrintJob> PrintJobs => Set<PrintJob>();
    public DbSet<FilamentUsage> FilamentUsages => Set<FilamentUsage>();
    public DbSet<UsageLog> UsageLogs => Set<UsageLog>();

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        // Apply all entity type configurations from the assembly
        modelBuilder.ApplyConfigurationsFromAssembly(typeof(ExtrudexDbContext).Assembly);

        // Apply seed data
        modelBuilder.Entity<MaterialBase>().HasData(SeedData.MaterialBases);
        modelBuilder.Entity<MaterialFinish>().HasData(SeedData.MaterialFinishes);
        modelBuilder.Entity<MaterialModifier>().HasData(SeedData.MaterialModifiers);
    }

    /// <summary>
    /// Automatically set UpdatedAt on auditable entities during SaveChanges.
    /// </summary>
    public override int SaveChanges(bool acceptAllChangesOnSuccess)
    {
        SetAuditTimestamps();
        return base.SaveChanges(acceptAllChangesOnSuccess);
    }

    public override async Task<int> SaveChangesAsync(
        bool acceptAllChangesOnSuccess,
        CancellationToken cancellationToken = default)
    {
        SetAuditTimestamps();
        return await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
    }

    /// <summary>
    /// Sets UpdatedAt on all auditable entities that have been modified.
    /// Sets CreatedAt on all auditable entities that are being added.
    /// </summary>
    private void SetAuditTimestamps()
    {
        var entries = ChangeTracker.Entries<AuditableEntity>();

        foreach (var entry in entries)
        {
            if (entry.State == EntityState.Added)
            {
                entry.Entity.CreatedAt = DateTime.UtcNow;
                entry.Entity.UpdatedAt = DateTime.UtcNow;
            }
            else if (entry.State == EntityState.Modified)
            {
                entry.Entity.UpdatedAt = DateTime.UtcNow;
            }
        }
    }
}