using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using MTWorkHR.Core.Entities;
using MTWorkHR.Core.Entities.Base;
using MTWorkHR.Core.Global;
using MTWorkHR.Infrastructure.Entities;
using MTWorkHR.Infrastructure.Data;
using System.Reflection.Emit;
using MTWorkHR.Core.Entities.User;


namespace MTWorkHR.Infrastructure.DBContext
{
    public class HRDataContext : IdentityDbContext<ApplicationUser, ApplicationRole, string>
    {
        private readonly GlobalInfo _globalInfo;

        public HRDataContext(DbContextOptions<HRDataContext> options, GlobalInfo globalInfo) : base(options) {
            this._globalInfo = globalInfo;

        }
        public DbSet<AttachmentType> AttachmentTypes { get; set; }
        public DbSet<UserAttachment> UserAttachments { get; set; }
        public DbSet<Permission> Permissions { get; set; }
        public DbSet<RolePermission> RolePermissions { get; set; }



        //-------------------------------------
        public DbSet<Company> Companies { get; set; }
        public DbSet<Project> Projects { get; set; }
        //        public DbSet<AttachmentType> AttachmentTypes { get; set; }
        public DbSet<UserTaskStatus> UserTaskStatuses { get; set; }
        public DbSet<UserTask> UserTasks { get; set; }
        public DbSet<UserTaskAttachment> UserTaskAttachments { get; set; }
        public DbSet<UserTaskHistory> UserTaskHistories { get; set; }
        public DbSet<Team> Teams { get; set; }
        public DbSet<Meeting> Meetings { get; set; }
        public DbSet<Attendance> Attendances { get; set; }
        public DbSet<OrderType> OrderTypes { get; set; }
        public DbSet<LeaveType> LeaveTypes { get; set; }
        public DbSet<OrderAllocation> OrderAllocations { get; set; }
        public DbSet<OrderRequest> OrderRequests { get; set; }
        public DbSet<ChatMessage> ChatMessages { get; set; }
        public DbSet<HubConnection> HubConnections { get; set; }
        //-------------------Lookups---------------------------
        public DbSet<CountryLookup> CountryLookups { get; set; }
        public DbSet<Qualification> Qualifications { get; set; }
        public DbSet<University> Universities { get; set; }
        public DbSet<Industry> Industries { get; set; }
        public DbSet<JobTitle> JobTitles { get; set; }
        public DbSet<LoginOTP> LoginOTPs { get; set; }
        public DbSet<City> Cities { get; set; }
        //-----------
        public DbSet<Contract> Contracts { get; set; }
        public DbSet<Nationality> Nationalities{ get; set; }
        public DbSet<SubscriptionConfiguration> SubscriptionConfiguration { get; set; }
        public DbSet<Invoice> Invoices { get; set; }
        public DbSet<JobInterview> JobInterviews { get; set; }

        //------------------------Logs------------------------
        public DbSet<UserLog> UserLogs { get; set; }
        public DbSet<AuthLog> AuthLogs { get; set; }
        public DbSet<FileLog> FileLogs { get; set; }
        public DbSet<RoleLog> RoleLogs { get; set; }
        public DbSet<SettingLog> SettingLogs { get; set; }
        public DbSet<UserTaskLog> UserTaskLogs { get; set; }
        public DbSet<TeamLog> TeamLogs { get; set; }
        public DbSet<MeetingLog> MeetingLogs { get; set; }
        public DbSet<AttendanceLog> AttendanceLogs { get; set; }
        //----------------------------------------
        protected override void OnModelCreating(ModelBuilder builder)
        {
            base.OnModelCreating(builder);
            SetGlobalFilters(builder);
            builder.ApplyConfigurationsFromAssembly(typeof(HRDataContext).Assembly);
            CreateGlobalIndexes(builder);
        }

        private void SetGlobalFilters(ModelBuilder builder)
        {
            builder.SetQueryFilterOnAllEntities<ISoftDelete>(p => !p.IsDeleted);
            builder.SetQueryFilterOnAllEntities<IHaveCompany>(b => b.CompanyId == _globalInfo.CompanyId);

        }
        #region SaveChanges 
        public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
        {
            BeforeSaveProccess();

            return base.SaveChangesAsync(cancellationToken);
        }

        public override int SaveChanges()
        {
            BeforeSaveProccess();

            return base.SaveChanges();
        }

        public override int SaveChanges(bool acceptAllChangesOnSuccess)
        {
            BeforeSaveProccess();

            return base.SaveChanges(acceptAllChangesOnSuccess);
        }

        private void BeforeSaveProccess()
        {
            var changes = from e in this.ChangeTracker.Entries()
                          where e.State != EntityState.Unchanged
                          select e;

            foreach (var change in changes)
            {
                if (change.State == EntityState.Added)
                {
                    if (change.Entity is IAudit)
                    {
                        ((IAudit)change.Entity).CreateUser = _globalInfo.UserId;
                        ((IAudit)change.Entity).CreateDate = DateTime.UtcNow;
                    }
                    if ( _globalInfo.CompanyId > 0 && change.Entity is IHaveCompany)
                    {
                        ((IHaveCompany)change.Entity).CompanyId = (long)_globalInfo.CompanyId;
                    }
                }
                else if (change.State == EntityState.Modified)
                {
                    if (_globalInfo.CompanyId > 0 && change.Entity is IHaveCompany)
                    {
                        var originalValues = this.Entry(change.Entity).OriginalValues;
                        var original = (long)originalValues["CompanyId"];
                        if (original != (long)_globalInfo.CompanyId)
                            throw new AppException(ExceptionEnum.NotAuthorized);
                    }

                    if (change.Entity is IAudit
                        && ((change.Entity is IFullAudit && !((IFullAudit)change.Entity).IsDeleted) || change.Entity is not IFullAudit))
                    {
                        ((IAudit)change.Entity).UpdateUser = _globalInfo.UserId;
                        ((IAudit)change.Entity).UpdateDate = DateTime.UtcNow;
                    }

                    if (change.Entity is IFullAudit && ((IFullAudit)change.Entity).IsDeleted)
                    {
                        ((IFullAudit)change.Entity).DeleteUserId = _globalInfo.UserId;
                    }

                }
                else if (change.State == EntityState.Deleted)
                {

                }
            }
        }
        private void CreateGlobalIndexes(ModelBuilder builder)
        {
            builder.HasIndexOnAllEntities<IHaveCompany>(p => p.CompanyId);
        }
        #endregion       
    }
}
