#nullable disable

using AipGateway.API.Domain.Entities;
using AipGateway.API.Domain.IServices.IUtilities;
using AipGateway.API.Domain.Models;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data;
using System.Reflection;

namespace AipGateway.API.Infrastructure.Persistence
{
    public class ApplicationDbContext : DbContext
    {
        private readonly ILogger<ApplicationDbContext> _log;
        private readonly IDomainEventDispatcher _dispatcher;
        private readonly ConnectionInfo _connectionInfo;
        public ApplicationDbContext(ILogger<ApplicationDbContext> logger, DbContextOptions<ApplicationDbContext> options, ConnectionInfo connectionInfo) : base(options)
        {
            _log = logger;
            _connectionInfo = connectionInfo;
            _log.LogError("ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo: ApplicationDbContext");
        }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer(_connectionInfo.ConnectionString, b => b.MigrationsAssembly(typeof(ApplicationDbContext).Assembly.FullName));
        }

        protected override void OnModelCreating(ModelBuilder builder)
        {
#if false
            //ApplicationDbContextSeed.SeedSampleDataAsync(modelBuilder);   ==> 테스트 데이터 넣을 경우
            foreach (var entityType in builder.Model.GetEntityTypes())
            {
                //get first property of entity with DatabaseGeneratedAttribute = DatabaseGeneratedOption.Identity
                var idProp = entityType.GetProperties()
                    .FirstOrDefault(x => x.PropertyInfo != null
                     && x.PropertyInfo.GetCustomAttribute<DatabaseGeneratedAttribute>()
                     ?.DatabaseGeneratedOption == DatabaseGeneratedOption.Identity);
                //if it exists, set it to identity
                if (idProp != null)
                {
                    builder.Entity(entityType.ClrType)
                        .Property(idProp.Name)
                        .ValueGeneratedOnAddOrUpdate()
                        .UseIdentityColumn();
                }
            }
#endif
        }
        public override int SaveChanges()
        {
            //var userID = _currentUserService.ID;
            //if (userID == 0)
            //{
            //    return base.SaveChanges();
            //}
            foreach (var entry in ChangeTracker.Entries<BaseEntity>())
            {
                switch (entry.State)
                {
                    case EntityState.Added:
                        //entry.Entity.CreatedBy = userID;
                        entry.Entity.CreatedAt = DateTime.UtcNow;
                        entry.Entity.IsActive = true;
                        entry.Entity.IsDeleted = false;
                        break;
                    case EntityState.Modified:
                        //entry.Entity.ModifiedBy = userID;
                        entry.Entity.ModifiedAt = DateTime.UtcNow;
                        break;
                }
            }
            
            //foreach (var entry in ChangeTracker.Entries<AuditableEntity>())
            //{
            //    switch (entry.State)
            //    {
            //        case EntityState.Added:
            //            entry.Entity.CreatedBy = userID;
            //            entry.Entity.CreatedAt = DateTime.UtcNow;
            //            break;
            //        case EntityState.Modified:
            //            entry.Entity.ModifiedBy = userID;
            //            entry.Entity.ModifiedAt = DateTime.UtcNow;
            //            break;
            //    }
            //}
            //foreach (var entry in ChangeTracker.Entries<UserRole>())
            //{
            //    switch (entry.State)
            //    {
            //        case EntityState.Added:
            //            entry.Entity.CreatedBy = userID;
            //            entry.Entity.CreatedAt = DateTime.UtcNow;
            //            break;
            //        case EntityState.Modified:
            //            entry.Entity.ModifiedBy = userID;
            //            entry.Entity.ModifiedAt = DateTime.UtcNow;
            //            break;
            //    }
            //}

            PreSaveChanges().GetAwaiter().GetResult();
            var response = base.SaveChanges();
            PostSaveChanges().GetAwaiter().GetResult();

            return response;
        }
        private async Task PreSaveChanges()
        {
            await Task.CompletedTask;
        }
        private async Task PostSaveChanges()
        {
            await DispatchDomainEvents();
        }
        private async Task DispatchDomainEvents()
        {
            var domainEventEntities = ChangeTracker.Entries<IHasDomainEventEntity>()
                .Select(po => po.Entity)
                .Where(po => po.DomainEvents.Any())
                .ToArray();

            foreach (var entity in domainEventEntities)
            {
                while (entity.DomainEvents.TryTake(out IDomainEvent dev))
                    await _dispatcher.Dispatch(dev);
            }
        }

        public IEnumerable<T> SqlQuery<T>(FormattableString sql)
        {
            try
            {
                return Database.SqlQuery<T>(sql);
            }
            catch (Exception ex)
            {
                _log.LogError("SqlQuery: {0}", sql.ToString());
                _log.LogError("SqlQuery: {0}", ex.Message);
            }
            return null;
        }
        public int ExecuteSql(FormattableString sql)
        {
            try
            {
                return Database.ExecuteSql(sql);
            }
            catch (Exception ex)
            {
                _log.LogError("ExecuteSql: {0}", sql.ToString());
                _log.LogError("ExecuteSql: {0}", ex.Message);
            }
            return -1;
        }

        public virtual DbSet<TbLinkedSystem> LinkedSystems { get; set; }
        public virtual DbSet<TbLinkedServer> LinkedServers { get; set; }
        public virtual DbSet<TbLinkedDecryptKey> LinkedDecryptKeys { get; set; }
        public virtual DbSet<TbLinkedApiKey> LinkedApiKeys { get; set; }

        public virtual DbSet<TbAipLabel> AipLabels { get; set; }
        public virtual DbSet<TbAipPolicy> AipPolicies { get; set; }
        public virtual DbSet<TbAipProtection> AipProtections { get; set; }

        public virtual DbSet<TbAipServer> AipServers { get; set; }
        public virtual DbSet<TbAipConfig> AipConfigs { get; set; }

        public virtual DbSet<TbAipFileJobLog> AipFileJobLogs { get; set; }
        public virtual DbSet<TbAipApiCallLog> AipApiCallLogs { get; set; }

    }
}
