using Microsoft.EntityFrameworkCore;
using DiunaBI.Domain.Entities;

namespace DiunaBI.Infrastructure.Data;

public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options)
{
    public DbSet<User> Users { get; init; }
    public DbSet<Layer> Layers { get; init; }
    public DbSet<Record> Records { get; init; }
    public DbSet<ProcessSource> ProcessSources { get; init; }
    public DbSet<DataInbox> DataInbox { get; init; }
    public DbSet<QueueJob> QueueJobs { get; init; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<User>().HasKey(x => x.Id);
        modelBuilder.Entity<User>().Property(x => x.Email).HasMaxLength(50);
        modelBuilder.Entity<User>().Property(x => x.UserName).HasMaxLength(50);

        modelBuilder.Entity<Layer>().HasKey(x => x.Id);
        modelBuilder.Entity<Layer>().Property(x => x.Number).IsRequired();
        modelBuilder.Entity<Layer>().Property(x => x.Name).IsRequired().HasMaxLength(50);
        modelBuilder.Entity<Layer>().Property(x => x.Type).IsRequired().HasConversion<int>();
        modelBuilder.Entity<Layer>().Property(x => x.CreatedAt).IsRequired();
        modelBuilder.Entity<Layer>().Property(x => x.ModifiedAt).IsRequired();
        modelBuilder.Entity<Layer>().Property(x => x.IsDeleted).IsRequired().HasDefaultValue(false);
        modelBuilder.Entity<Layer>().Property(x => x.IsCancelled).IsRequired().HasDefaultValue(false);
        modelBuilder.Entity<Layer>().Property(x => x.CreatedById).IsRequired();
        modelBuilder.Entity<Layer>().Property(x => x.ModifiedById).IsRequired();

        modelBuilder.Entity<Layer>()
            .HasOne(x => x.CreatedBy)
            .WithMany()
            .HasForeignKey(x => x.CreatedById)
            .OnDelete(DeleteBehavior.Restrict);

        modelBuilder.Entity<Layer>()
            .HasOne(x => x.ModifiedBy)
            .WithMany()
            .HasForeignKey(x => x.ModifiedById)
            .OnDelete(DeleteBehavior.Restrict);

        modelBuilder.Entity<Layer>()
            .HasMany(x => x.Records)
            .WithOne()
            .HasForeignKey(r => r.LayerId)
            .OnDelete(DeleteBehavior.Cascade);

        modelBuilder.Entity<Record>().HasKey(x => x.Id);
        modelBuilder.Entity<Record>().Property(x => x.Code).IsRequired().HasMaxLength(50);
        modelBuilder.Entity<Record>().Property(x => x.Desc1).HasMaxLength(10000);
        modelBuilder.Entity<Record>().Property(x => x.CreatedAt);
        modelBuilder.Entity<Record>().Property(x => x.ModifiedAt);
        modelBuilder.Entity<Record>().Property(x => x.IsDeleted);
        modelBuilder.Entity<Record>().Property(x => x.CreatedById).IsRequired();
        modelBuilder.Entity<Record>().Property(x => x.ModifiedById).IsRequired();
        modelBuilder.Entity<Record>().Property(x => x.LayerId).IsRequired();

        modelBuilder.Entity<Record>()
            .HasOne(x => x.CreatedBy)
            .WithMany()
            .HasForeignKey(x => x.CreatedById)
            .OnDelete(DeleteBehavior.Restrict);

        modelBuilder.Entity<Record>()
            .HasOne(x => x.ModifiedBy)
            .WithMany()
            .HasForeignKey(x => x.ModifiedById)
            .OnDelete(DeleteBehavior.Restrict);

        modelBuilder.Entity<Record>()
            .HasOne<Layer>()
            .WithMany(l => l.Records!)
            .HasForeignKey(x => x.LayerId)
            .OnDelete(DeleteBehavior.Cascade);

        modelBuilder.Entity<ProcessSource>().HasKey(x => new { x.LayerId, x.SourceId });
        modelBuilder.Entity<ProcessSource>().Property(x => x.LayerId).IsRequired();
        modelBuilder.Entity<ProcessSource>().Property(x => x.SourceId).IsRequired();

        modelBuilder.Entity<ProcessSource>()
            .HasOne<Layer>()
            .WithMany()
            .HasForeignKey(x => x.LayerId)
            .OnDelete(DeleteBehavior.Cascade);

        modelBuilder.Entity<ProcessSource>()
            .HasOne(x => x.Source)
            .WithMany()
            .HasForeignKey(x => x.SourceId)
            .OnDelete(DeleteBehavior.Restrict);

        modelBuilder.Entity<DataInbox>().HasKey(x => x.Id);
        modelBuilder.Entity<DataInbox>().Property(x => x.Name).IsRequired().HasMaxLength(50);
        modelBuilder.Entity<DataInbox>().Property(x => x.Source).IsRequired().HasMaxLength(50);
        modelBuilder.Entity<DataInbox>().Property(x => x.Data).IsRequired();
        modelBuilder.Entity<DataInbox>().Property(x => x.CreatedAt);

        modelBuilder.Entity<QueueJob>().HasKey(x => x.Id);
        modelBuilder.Entity<QueueJob>().Property(x => x.LayerId).IsRequired();
        modelBuilder.Entity<QueueJob>().Property(x => x.LayerName).IsRequired().HasMaxLength(200);
        modelBuilder.Entity<QueueJob>().Property(x => x.PluginName).IsRequired().HasMaxLength(100);
        modelBuilder.Entity<QueueJob>().Property(x => x.JobType).IsRequired().HasConversion<int>();
        modelBuilder.Entity<QueueJob>().Property(x => x.Priority);
        modelBuilder.Entity<QueueJob>().Property(x => x.CreatedAt).IsRequired();
        modelBuilder.Entity<QueueJob>().Property(x => x.RetryCount);
        modelBuilder.Entity<QueueJob>().Property(x => x.MaxRetries);
        modelBuilder.Entity<QueueJob>().Property(x => x.Status).IsRequired().HasConversion<int>();
        modelBuilder.Entity<QueueJob>().Property(x => x.LastError).HasMaxLength(1000);
        modelBuilder.Entity<QueueJob>().Property(x => x.LastAttemptAt);
        modelBuilder.Entity<QueueJob>().Property(x => x.CompletedAt);
        modelBuilder.Entity<QueueJob>().Property(x => x.CreatedById).IsRequired();
        modelBuilder.Entity<QueueJob>().Property(x => x.CreatedAtUtc).IsRequired();
        modelBuilder.Entity<QueueJob>().Property(x => x.ModifiedById).IsRequired();
        modelBuilder.Entity<QueueJob>().Property(x => x.ModifiedAtUtc).IsRequired();
        
        // Configure automatic timestamps for entities with CreatedAt/ModifiedAt
        ConfigureTimestamps(modelBuilder);
    }

    private void ConfigureTimestamps(ModelBuilder modelBuilder)
    {
        foreach (var entityType in modelBuilder.Model.GetEntityTypes())
        {
            // Check if entity has CreatedAt property
            var createdAtProperty = entityType.FindProperty("CreatedAt");
            if (createdAtProperty != null)
            {
                modelBuilder.Entity(entityType.ClrType)
                    .Property("CreatedAt")
                    .HasDefaultValueSql("GETUTCDATE()");
            }

            // Check if entity has ModifiedAt property
            var modifiedAtProperty = entityType.FindProperty("ModifiedAt");
            if (modifiedAtProperty != null)
            {
                modelBuilder.Entity(entityType.ClrType)
                    .Property("ModifiedAt")
                    .HasDefaultValueSql("GETUTCDATE()");
            }
        }
    }

    public override int SaveChanges()
    {
        UpdateTimestamps();
        return base.SaveChanges();
    }

    public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
    {
        UpdateTimestamps();
        return base.SaveChangesAsync(cancellationToken);
    }

    private void UpdateTimestamps()
    {
        var entities = ChangeTracker.Entries()
            .Where(e => e.State == EntityState.Added || e.State == EntityState.Modified);

        foreach (var entity in entities)
        {
            // Try to set CreatedAt for new entities
            if (entity.State == EntityState.Added)
            {
                var createdAtProperty = entity.Properties.FirstOrDefault(p => p.Metadata.Name == "CreatedAt");
                if (createdAtProperty != null)
                {
                    createdAtProperty.CurrentValue = DateTime.UtcNow;
                }

                // Ensure IsDeleted and IsCancelled have default values for Layer entities
                if (entity.Entity is Layer)
                {
                    var isDeletedProperty = entity.Properties.FirstOrDefault(p => p.Metadata.Name == "IsDeleted");
                    if (isDeletedProperty != null && isDeletedProperty.CurrentValue == null)
                    {
                        isDeletedProperty.CurrentValue = false;
                    }

                    var isCancelledProperty = entity.Properties.FirstOrDefault(p => p.Metadata.Name == "IsCancelled");
                    if (isCancelledProperty != null && isCancelledProperty.CurrentValue == null)
                    {
                        isCancelledProperty.CurrentValue = false;
                    }
                }
            }

            // Always update ModifiedAt
            var modifiedAtProperty = entity.Properties.FirstOrDefault(p => p.Metadata.Name == "ModifiedAt");
            if (modifiedAtProperty != null)
            {
                modifiedAtProperty.CurrentValue = DateTime.UtcNow;
            }
        }
    }
}