using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

#nullable disable

namespace AsbCloudDb.Model
{
    //Scaffold-DbContext "Host=localhost;Database=postgres;Username=postgres;Password=q;Persist Security Info=True" Npgsql.EntityFrameworkCore.PostgreSQL -OutputDir Model -DataAnnotations
    public partial class AsbCloudDbContext : DbContext, IAsbCloudDbContext
    {
        public virtual DbSet<Cluster> Clusters { get; set; }
        public virtual DbSet<Company> Companies { get; set; }
        public virtual DbSet<DataSaubBase> DataSaubBases { get; set; }
        public virtual DbSet<Deposit> Deposits { get; set; }
        public virtual DbSet<Event> Events { get; set; }
        public virtual DbSet<Message> Messages { get; set; }
        public virtual DbSet<User> Users { get; set; }
        public virtual DbSet<UserRole> UserRoles { get; set; }
        public virtual DbSet<Well> Wells { get; set; }
        public virtual DbSet<ReportProperties> ReportProperties { get; set; }
        public virtual DbSet<File> Files { get; set; }
        public virtual DbSet<FileCategory> FileCategories { get; set; }
        public virtual DbSet<Telemetry> Telemetries { get; set; }
        public virtual DbSet<TelemetryUser> TelemetryUsers { get; set; }
        public virtual DbSet<TelemetryOperation> Operations { get; set; }
        public virtual DbSet<TelemetryAnalysis> TelemetryAnalysis { get; set; }
        public virtual DbSet<WellSection> WellSections { get; set; }
        public virtual DbSet<WellOperation> WellOperations { get; set; }
        public virtual DbSet<WellType> WellTypes { get; set; }
        public virtual DbSet<LastData> LastData { get; set; }

        //var options = new DbContextOptionsBuilder<AsbCloudDbContext>()
        //    .UseNpgsql("Host=localhost;Database=postgres;Username=postgres;Password=q;Persist Security Info=True")
        //    .Options;
        //var context = new AsbCloudDbContext(options);

        public AsbCloudDbContext(DbContextOptions<AsbCloudDbContext> options)
            : base(options)
        {
            Database.EnsureCreated();
        }

        //protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        //{
        //    if (!optionsBuilder.IsConfigured)
        //    {
        //        optionsBuilder.UseNpgsql(connectionString);
        //    }
        //}

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.HasPostgresExtension("adminpack")
                .HasAnnotation("Relational:Collation", "Russian_Russia.1251");

            modelBuilder.Entity<Cluster>(entity =>
            {
                entity.HasOne(d => d.Deposit)
                    .WithMany(p => p.Clusters)
                    .HasForeignKey(d => d.IdDeposit)
                    .HasConstraintName("t_cluster_t_deposit_id_fk");
            });

            modelBuilder.Entity<DataSaubBase>(entity =>
            {
                entity.HasOne(d => d.Telemetry)
                    .WithMany(p => p.DataSaubBases)
                    .HasForeignKey(d => d.IdTelemetry)
                    .OnDelete(DeleteBehavior.ClientSetNull)
                    .HasConstraintName("t_data_saub_base_t_telemetry_id_fk");
            });

            modelBuilder.Entity<Message>(entity =>
            {
                entity.HasOne(d => d.Telemetry)
                    .WithMany(p => p.Messages)
                    .HasForeignKey(d => d.IdTelemetry)
                    .HasConstraintName("t_messages_t_telemetry_id_fk");
            });

            modelBuilder.Entity<TelemetryUser>(entity =>
            {
                entity.HasKey(nameof(TelemetryUser.IdTelemetry), nameof(TelemetryUser.IdUser));
                entity.HasOne(d => d.Telemetry)
                    .WithMany(p => p.Users)
                    .HasForeignKey(d => d.IdTelemetry)
                    .OnDelete(DeleteBehavior.ClientSetNull)
                    .HasConstraintName("t_telemetry_user_t_telemetry_id_fk");
            });

            modelBuilder.Entity<TelemetryAnalysis>(entity =>
            {
                entity.HasOne(d => d.Telemetry)
                    .WithMany(p => p.Analysis)
                    .HasForeignKey(d => d.IdTelemetry)
                    .HasConstraintName("t_analysis_t_telemetry_id_fk");

                entity.HasOne(d => d.Operation)
                    .WithMany(p => p.Analysis)
                    .HasForeignKey(d => d.IdOperation)
                    .OnDelete(DeleteBehavior.SetNull)
                    .HasConstraintName("t_analysis_t_operation_id_fk");
            });

            modelBuilder.Entity<Event>(entity =>
            {
                entity.HasKey(nameof(Event.IdTelemetry), nameof(Event.IdEvent));
                entity.HasOne(d => d.Telemetry)
                    .WithMany(p => p.Events)
                    .HasForeignKey(d => d.IdTelemetry)
                    .HasConstraintName("t_event_t_telemetry_id_fk");
            });

            modelBuilder.Entity<User>(entity =>
            {
                entity.HasOne(d => d.Company)
                    .WithMany(p => p.Users)
                    .HasForeignKey(d => d.IdCompany)
                    .HasConstraintName("t_user_t_company_id_fk");

                entity.HasIndex(d => d.Login)
                    .IsUnique();
            });

            modelBuilder.Entity<Well>(entity =>
            {
                entity.HasOne(d => d.Cluster)
                    .WithMany(p => p.Wells)
                    .HasForeignKey(d => d.IdCluster)
                    .HasConstraintName("t_well_t_cluster_id_fk");

                entity.HasOne(d => d.Telemetry)
                    .WithOne(p => p.Well)
                    .HasForeignKey<Well>(d => d.IdTelemetry)
                    .HasConstraintName("t_well_t_telemetry_id_fk");
            });

            modelBuilder.Entity<RelationCompanyWell>(entity => {

                entity.HasKey(nameof(RelationCompanyWell.IdCompany), nameof(RelationCompanyWell.IdWell));

                entity.HasOne(r => r.Well)
                    .WithMany(w => w.RelationCompaniesWells)
                    .HasForeignKey(r => r.IdWell)
                    .HasConstraintName("t_relation_company_well_t_well_id_fk");

                entity.HasOne(r => r.Company)
                    .WithMany(w => w.RelationCompaniesWells)
                    .HasForeignKey(r => r.IdCompany)
                    .HasConstraintName("t_relation_company_well_t_company_id_fk");
            });

            FillData(modelBuilder);
            FillDemoData(modelBuilder);
        }

        private static void FillData(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<UserRole>(entity =>
            {
                entity.HasData(new List<UserRole>{
                    new UserRole{ Id = 1, Caption = "Администратор", },
                });
            });

            modelBuilder.Entity<User>(entity =>
            {
                entity.HasData(new List<User>{
                    new User{
                        Id = 1,
                        IdCompany = 1,
                        IdRole = 1,
                        Level = int.MaxValue,
                        Login = "dev",
                        PasswordHash = "Vlcj|4fa529103dde7ff72cfe76185f344d4aa87931f8e1b2044e8a7739947c3d18923464eaad93843e4f809c5e126d013072", // dev
                        Name = "Разработчик",
                    },
                });
            });

            modelBuilder.Entity<Company>(entity =>
            {
                entity.HasData(new List<Company>{
                    new Company{ Id = 1, Caption = "ООО \"АСБ\"", IdCompanyType = 3},
                });
            });

            modelBuilder.Entity<TelemetryOperation>(entity =>
            {
                entity.HasData(new List<TelemetryOperation> {
                    new TelemetryOperation {Id = 1, Name = "Невозможно определить операцию"},
                    new TelemetryOperation {Id = 2, Name = "Роторное бурение" },
                    new TelemetryOperation {Id = 3, Name = "Слайдирование" },
                    new TelemetryOperation {Id = 4, Name = "Подъем с проработкой" },
                    new TelemetryOperation {Id = 5, Name = "Спуск с проработкой" },
                    new TelemetryOperation {Id = 6, Name = "Подъем с промывкой" },
                    new TelemetryOperation {Id = 7, Name = "Спуск с промывкой" },
                    new TelemetryOperation {Id = 8, Name = "Спуск в скважину" },
                    new TelemetryOperation {Id = 9, Name = "Спуск с вращением" },
                    new TelemetryOperation {Id = 10, Name = "Подъем из скважины" },
                    new TelemetryOperation {Id = 11, Name = "Подъем с вращением" },
                    new TelemetryOperation {Id = 12, Name = "Промывка в покое" },
                    new TelemetryOperation {Id = 13, Name = "Промывка с вращением" },
                    new TelemetryOperation {Id = 14, Name = "Удержание в клиньях" },
                    new TelemetryOperation {Id = 15, Name = "Неподвижное состояние" },
                    new TelemetryOperation {Id = 16, Name = "Вращение без циркуляции" },
                    new TelemetryOperation {Id = 17, Name = "На поверхности" }
                });
            });

            modelBuilder.Entity<FileCategory>(entity =>
            {
                entity.HasData(new List<FileCategory> {
                    new FileCategory {Id = 1, Name = "Растворный сервис"},
                    new FileCategory {Id = 2, Name = "Цементирование"},
                    new FileCategory {Id = 3, Name = "ННБ"},
                    new FileCategory {Id = 4, Name = "ГТИ"},
                    new FileCategory {Id = 5, Name = "Документы по скважине"},
                    new FileCategory {Id = 6, Name = "Супервайзер"},
                    new FileCategory {Id = 7, Name = "Мастер"},
                    new FileCategory {Id = 8, Name = "Последние данные"},
                    new FileCategory {Id = 9, Name = "Последний замер бурового раствора"},
                    new FileCategory {Id = 10, Name = "Шламограмма"},
                    new FileCategory {Id = 11, Name = "Последние данные ННБ"},
                    new FileCategory {Id = 12, Name = "Рапорт"}
                });
            });
        }

        private static void FillDemoData(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<CompanyType>(entity =>
            {
                entity.HasData(new List<CompanyType>{
                    new CompanyType{ Id = 1, Caption = "Недрапользователь", },
                    new CompanyType{ Id = 2, Caption = "Буровой подрядчик", },
                    new CompanyType{ Id = 3, Caption = "Сервис автоматизации бурения", },
                });
            });

            modelBuilder.Entity<Deposit>(entity =>
            {
                entity.HasData(new List<Deposit> {
                    new Deposit{Id = 1, Caption = "м/р 1", Latitude = 60.8705722222222, Longitude = 70.3811888888889 },
                });
            });

            modelBuilder.Entity<Cluster>(entity =>
            {
                entity.HasData(new List<Cluster> {
                    new Cluster{Id = 1, Caption = "к39.1",  IdDeposit = 1, Latitude = 60.8705722222222, Longitude = 70.3811888888889},
                    new Cluster{Id = 2, Caption = "к151",  IdDeposit = 1, Latitude = 60.8205750000000, Longitude = 70.1343833333334},
                    new Cluster{Id = 3, Caption = "к611",  IdDeposit = 1, Latitude = 60.8100666666667, Longitude = 69.7778388888889},
                    new Cluster{Id = 4, Caption = "к203",  IdDeposit = 1, Latitude = 60.8928805555556, Longitude = 70.3272055555556},
                    new Cluster{Id = 5, Caption = "к221", IdDeposit = 1, Latitude = 60.6672055555556, Longitude = 69.6603861111111},
                });
            });

            modelBuilder.Entity<Telemetry>(entity =>
            {
                entity.HasData(new List<Telemetry>{
                    new Telemetry{
                        Id = 1,
                        RemoteUid = "123",
                        Info = new TelemetryInfo
                            {
                                Well = "1",
                                Cluster = "1",
                                Comment = "",
                                Deposit = "1",
                                Customer = "1",
                                HmiVersion = "1",
                                PlcVersion = "1",
                                TimeZoneId = "1",
                                DrillingStartDate = DateTime.Parse("2021-06-29T12:01:19.000000"),
                                TimeZoneOffsetTotalHours = 5.0
                            },
                    }
                });
            });

            modelBuilder.Entity<Well>(entity =>
            {
                entity.HasData(new List<Well> {
                    new Well{Id = 1, IdCluster = 1, IdTelemetry = 1, Caption = "скв 16314", Latitude = 60.8705722222222, Longitude = 70.3811888888889},
                    new Well{Id = 2, IdCluster = 1, Caption = "скв 16311", Latitude = 60.8705722222222, Longitude = 70.3811888888889},
                    new Well{Id = 3, IdCluster = 2, Caption = "скв 16315", Latitude = 60.8205750000000, Longitude = 70.1343833333334},
                    new Well{Id = 4, IdCluster = 2, Caption = "скв 16318", Latitude = 60.8205750000000, Longitude = 70.1343833333334},
                    new Well{Id = 5, IdCluster = 3, Caption = "скв 16310", Latitude = 60.8100666666667, Longitude = 69.7778388888889},
                    new Well{Id = 6, IdCluster = 4, Caption = "скв 16316", Latitude = 60.8928805555556, Longitude = 70.3272055555556},
                    new Well{Id = 7, IdCluster = 5, Caption = "скв 16312", Latitude = 60.6672055555556, Longitude = 69.6603861111111},
                    new Well{Id = 8, IdCluster = 5, Caption = "скв 16313", Latitude = 60.6672055555556, Longitude = 69.6603861111111},
                    new Well{Id = 9, IdCluster = 5, Caption = "скв 42669", Latitude = 60.6672055555556, Longitude = 69.6603861111111},
                });
            });

            modelBuilder.Entity<RelationCompanyWell>(entity => {
                entity.HasData(new List<RelationCompanyWell> {
                    new RelationCompanyWell{ IdWell = 1, IdCompany = 1},
                    new RelationCompanyWell{ IdWell = 2, IdCompany = 1},
                    new RelationCompanyWell{ IdWell = 3, IdCompany = 1},
                    new RelationCompanyWell{ IdWell = 4, IdCompany = 1},
                    new RelationCompanyWell{ IdWell = 5, IdCompany = 1},
                    new RelationCompanyWell{ IdWell = 6, IdCompany = 1},
                    new RelationCompanyWell{ IdWell = 7, IdCompany = 1},
                    new RelationCompanyWell{ IdWell = 8, IdCompany = 1},
                    new RelationCompanyWell{ IdWell = 9, IdCompany = 1},
                });
            });

            modelBuilder.Entity<TelemetryAnalysis>(entity =>
            {
                entity.HasData(new List<TelemetryAnalysis>{
                    new TelemetryAnalysis
                    {
                        Id = 1,
                        IdTelemetry = 1,
                        IdOperation = 17,
                        UnixDate = 1626870355,
                        DurationSec = 10,
                        OperationStartDepth = null,
                        OperationEndDepth = 206,
                        IsWellDepthIncreasing = false,
                        IsWellDepthDecreasing = false,
                        IsBitPositionIncreasing = false,
                        IsBitPositionDecreasing = false,
                        IsBitPositionLt20 = true,
                        IsBlockPositionIncreasing = false,
                        IsBlockPositionDecreasing = false,
                        IsRotorSpeedLt3 = true,
                        IsRotorSpeedGt3 = false,
                        IsPressureLt20 = true,
                        IsPressureGt20 = false,
                        IsHookWeightNotChanges = true,
                        IsHookWeightLt3 = true
                    },
                    new TelemetryAnalysis
                    {
                        Id = 2,
                        IdTelemetry = 1,
                        IdOperation = 8,
                        UnixDate = 1626870364,
                        DurationSec = 6,
                        OperationStartDepth = 206,
                        OperationEndDepth = 206,
                        IsWellDepthIncreasing = false,
                        IsWellDepthDecreasing = false,
                        IsBitPositionIncreasing = true,
                        IsBitPositionDecreasing = false,
                        IsBitPositionLt20 = true,
                        IsBlockPositionIncreasing = true,
                        IsBlockPositionDecreasing = false,
                        IsRotorSpeedLt3 = true,
                        IsRotorSpeedGt3 = false,
                        IsPressureLt20 = true,
                        IsPressureGt20 = false,
                        IsHookWeightNotChanges = true,
                        IsHookWeightLt3 = true
                    },
                    new TelemetryAnalysis
                    {
                        Id = 3,
                        IdTelemetry = 1,
                        IdOperation = 10,
                        UnixDate = 1626870370,
                        DurationSec = 2,
                        OperationStartDepth = 206,
                        OperationEndDepth = 206,
                        IsWellDepthIncreasing = false,
                        IsWellDepthDecreasing = false,
                        IsBitPositionIncreasing = false,
                        IsBitPositionDecreasing = true,
                        IsBitPositionLt20 = true,
                        IsBlockPositionIncreasing = false,
                        IsBlockPositionDecreasing = true,
                        IsRotorSpeedLt3 = true,
                        IsRotorSpeedGt3 = false,
                        IsPressureLt20 = true,
                        IsPressureGt20 = false,
                        IsHookWeightNotChanges = true,
                        IsHookWeightLt3 = true
                    },
                    new TelemetryAnalysis
                    {
                        Id = 4,
                        IdTelemetry = 1,
                        IdOperation = 17,
                        UnixDate = 1626870372,
                        DurationSec = 7,
                        OperationStartDepth = 206,
                        OperationEndDepth = 206,
                        IsWellDepthIncreasing = false,
                        IsWellDepthDecreasing = false,
                        IsBitPositionIncreasing = false,
                        IsBitPositionDecreasing = false,
                        IsBitPositionLt20 = true,
                        IsBlockPositionIncreasing = false,
                        IsBlockPositionDecreasing = false,
                        IsRotorSpeedLt3 = true,
                        IsRotorSpeedGt3 = false,
                        IsPressureLt20 = true,
                        IsPressureGt20 = false,
                        IsHookWeightNotChanges = true,
                        IsHookWeightLt3 = true
                    },
                    new TelemetryAnalysis
                    {
                        Id = 5,
                        IdTelemetry = 1,
                        IdOperation = 8,
                        UnixDate = 1626870379,
                        DurationSec = 7,
                        OperationStartDepth = 206,
                        OperationEndDepth = 206,
                        IsWellDepthIncreasing = false,
                        IsWellDepthDecreasing = false,
                        IsBitPositionIncreasing = true,
                        IsBitPositionDecreasing = false,
                        IsBitPositionLt20 = true,
                        IsBlockPositionIncreasing = true,
                        IsBlockPositionDecreasing = false,
                        IsRotorSpeedLt3 = true,
                        IsRotorSpeedGt3 = false,
                        IsPressureLt20 = true,
                        IsPressureGt20 = false,
                        IsHookWeightNotChanges = true,
                        IsHookWeightLt3 = true
                    }
                });
            });
        }

        public IQueryable<Well> GetWellsForCompany(int idCompany)
        {
            return from well in Wells
                   .Include(w => w.RelationCompaniesWells)
                   .ThenInclude(r => r.Company)
                   .Include(w => w.Cluster)
                   .ThenInclude(c => c.Deposit)
                   where well.RelationCompaniesWells.Any(c => c.IdCompany == idCompany)
                   select well;
        }

        public IQueryable<User> GetUsersByLogin(string login)
            => Users
            .Include(e => e.Role)
            .Include(e => e.Company)
            .Where(e => e.Login == login);

        public (DateTime From, DateTime To) GetDatesRange<TEntity>(int idTelemetry)
            where TEntity : class, IIdTelemetryDate
        {
            var dbSet = Set<TEntity>();

            var datesRange = (from m in dbSet
                              where m.IdTelemetry == idTelemetry
                              group m by m.IdTelemetry into g
                              select new
                              {
                                  From = g.Min(d => d.Date),
                                  To = g.Max(d => d.Date)
                              }).FirstOrDefault();

            if (datesRange is null)
                return (DateTime.MinValue, DateTime.MaxValue);

            return (datesRange.From, datesRange.To);
        }

        public IEnumerable<(double? MinDepth, double? MaxDepth, DateTime BeginPeriodDate)> GetDepthToInterval(int telemetryId,
            int intervalHoursTimestamp, int workStartTimestamp, double timezoneOffset)
        {
            //TODO: Сменить на LINQ группирование
            using var command = Database.GetDbConnection().CreateCommand();

            command.CommandText = $@"SELECT Min(t.bit_depth) AS MinDepth, Max(t.bit_depth) AS MaxDepth, Min(t.Date) AS dateStart
                                     FROM t_data_saub_base AS t
                                     WHERE id_telemetry = {telemetryId} AND t.Id % 10 = 0
                                     GROUP BY floor((extract(epoch from t.date) - {workStartTimestamp}  + {timezoneOffset}) / {intervalHoursTimestamp});";

            Database.OpenConnection();
            using var reader = command.ExecuteReader();
            if (reader.HasRows)
            {
                while (reader.Read())
                {
                    yield return
                    (
                        (double?)reader.GetValue(0),
                        (double?)reader.GetValue(1),
                        (DateTime)reader.GetValue(2)
                    );
                }
            }
        }

        public async Task<int> CreatePartitionAsync<TEntity>(string propertyName, int id, CancellationToken token = default)
            where TEntity : class
        {
            var dbSet = Set<TEntity>();
            var baseTableName = dbSet.EntityType.GetTableName();
            var schema = dbSet.EntityType.GetSchema();
            var tableObject = Microsoft.EntityFrameworkCore.Metadata.StoreObjectIdentifier.Table(baseTableName, schema);
            var tableName = baseTableName.Replace("_base", "");
            var property = dbSet.EntityType.GetProperty(propertyName).GetColumnName(tableObject);

            var query = $"CREATE TABLE {tableName}_{id} (like {baseTableName} including all, constraint partitioning_check check ({property} = 1)) INHERITS ({baseTableName});";

            return await Database.ExecuteSqlRawAsync(query, token).ConfigureAwait(false);
        }
    }
}