using AyCode.Core.Consts;
using Microsoft.EntityFrameworkCore;
using AyCode.Interfaces.TimeStampInfo;
using Microsoft.Extensions.Configuration;


namespace AyCode.Database.DbContexts;

public abstract class AcDbContextBase : DbContext, IAcDbContextBase
{
    private readonly string? _connString;
    public string Name { get; set; }

    public Guid SessionId { get; protected set; } = Guid.NewGuid();

    protected AcDbContextBase()
    {
        _connString = AcEnv.AppConfiguration.GetConnectionString("DeveloperDbConnection");
        //DbInterception.Add(new UtcDateTimeDbCommandInterceptor());
    }

    protected AcDbContextBase(string name) : this()
    {
        Name = name;
    }

    protected AcDbContextBase(DbContextOptions<DbContext> options, string name) : base(options)
    {
        Name = name;
        
        _connString = AcEnv.AppConfiguration.GetConnectionString("DeveloperDbConnection");
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseLazyLoadingProxies(true);

        optionsBuilder.UseSqlServer(_connString);
    }

    public override int SaveChanges()
    {
        UpdateTimeStampInfoProperties();

        return base.SaveChanges();
    }

    public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = new CancellationToken())
    {
        UpdateTimeStampInfoProperties();

        return base.SaveChangesAsync(cancellationToken);
    }

    public override Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = new CancellationToken())
    {
        UpdateTimeStampInfoProperties();

        return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken);
    }

    private void UpdateTimeStampInfoProperties()
    {
        var entries = ChangeTracker.Entries().Where(e =>
            e.Entity is not ITimeStampDisableAutoSet && (e is { State: EntityState.Added, Entity: ITimeStampCreated } || e.State is EntityState.Modified or EntityState.Added && e.Entity is ITimeStampModified));

        var utcNow = DateTime.UtcNow;
        foreach (var entityEntry in entries)
        {
            if (entityEntry.Entity is ITimeStampModified timeStampModified)
                timeStampModified.Modified = utcNow;

            if (entityEntry.Entity is not ITimeStampCreated timeStampCreated) 
                continue;

            if (entityEntry.State == EntityState.Added)
            {
                timeStampCreated.Created = utcNow;
                continue;
            }

            //entityEntry.State = EntityState.Unchanged;
            entityEntry.Property(nameof(timeStampCreated.Created)).IsModified = false;
        }
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        //modelBuilder.ComplexType<IPAddress>();
        //modelBuilder.ComplexType<PhysicalAddress>();

        //modelBuilder.Conventions.Delete<PluralizingTableNameConvention>();

        //modelBuilder.Conventions.Delete<OneToManyCascadeDeleteConvention>();
        //modelBuilder.Conventions.Delete<ManyToManyCascadeDeleteConvention>();
        //modelBuilder.Conventions.Delete<OneToOneConstraintIntroductionConvention>();

        //var dateTimeConverter = new ValueConverter<DateTime, DateTime>(v => v, v => DateTime.SpecifyKind(v, DateTimeKind.Utc));
        //modelBuilder.UseValueConverterForType<DateTime>(dateTimeConverter);

        modelBuilder.UseDateTimeUtc();

        modelBuilder.EntitiesOfType<ITimeStampCreated>((type, builder) =>
        {
            builder.Property<DateTime>(nameof(ITimeStampCreated.Created)).IsRequired(); //.HasDefaultValueSql("getutcdate()");
        });

        modelBuilder.EntitiesOfType<ITimeStampModified>((type, builder) =>
        {
            builder.Property<DateTime>(nameof(ITimeStampModified.Modified)).IsRequired(); //HasComputedColumnSql("getutcdate()");
        });
    }

    public override string ToString()
    {
        return $"{GetType().Name}, {Name} - ({SessionId})";
    }
}