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
    {
        //private readonly string connectionString;
        public virtual DbSet<Cluster> Clusters { get; set; }
        public virtual DbSet<Customer> Customers { 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<Telemetry> Telemetries { get; set; }
        public virtual DbSet<TelemetryUser> TelemetryUsers { 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<Report> Reports { get; set; }

        //public AsbCloudDbContext(string connectionString = "Host=localhost;Database=postgres;Username=postgres;Password=q;Persist Security Info=True")
        //{
        //    this.connectionString = connectionString;
        //    Database.EnsureCreated();
        //}

        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<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<UserRole>(entity =>
            {
                entity.HasData(new List<UserRole>{
                    new UserRole{ Id = 1, Caption = "Администратор", },
                });
            });

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

            modelBuilder.Entity<User>(entity =>
            {
                entity.HasOne(d => d.Customer)
                    .WithMany(p => p.Users)
                    .HasForeignKey(d => d.IdCustomer)
                    .HasConstraintName("t_user_t_customer_id_fk");

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

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

            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.Customer)
                    .WithMany(p => p.Wells)
                    .HasForeignKey(d => d.IdCustomer)
                    .HasConstraintName("t_well_t_customer_id_fk");

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


            });

            FillData(modelBuilder);
        }

        private static void FillData(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Deposit>(entity =>
            {
                entity.HasData(new List<Deposit> {
                    new Deposit{Id = 1, Caption = "м/р 1" },
                });
            });

            modelBuilder.Entity<Cluster>(entity =>
            {
                entity.HasData(new List<Cluster> {
                    new Cluster{Id = 1, Caption = "куст 1", IdDeposit = 1 },
                });
            });

            modelBuilder.Entity<Well>(entity =>
            {
                entity.HasData(new List<Well> {
                    new Well{Id = 1, IdCluster = 1, IdCustomer = 1, Caption = "скв 1" },
                    new Well{Id = 2, IdCluster = 1, IdCustomer = 1, Caption = "скв 2" },
                });
            });
        }

        public IQueryable<Well> GetWellsByCustomer(int idCustomer)
        {
            return from well in Wells
                   .Include(w => w.Customer)
                   .Include(w => w.Cluster)
                   .ThenInclude(c => c.Deposit)
                   where well.IdCustomer == idCustomer
                   select well;
        }

        public IQueryable<User> GetUsersByLogin(string login)
            => Users
            .Include(e => e.Role)
            .Include(e => e.Customer)
            .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 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);
        }
    }
}