// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Linq;
using EntityFrameworkCore.Jet.FunctionalTests.TestUtilities;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.TestUtilities;
using Xunit;

// ReSharper disable InconsistentNaming
namespace EntityFrameworkCore.Jet.FunctionalTests
{
    public class JetQueryTriggersTest : IClassFixture<JetQueryTriggersTest.JetTriggersFixture>
    {
        public JetQueryTriggersTest(JetTriggersFixture fixture) => Fixture = fixture;

        private JetTriggersFixture Fixture { get; }

        [ConditionalFact]
        public void Triggers_with_subqueries_run_on_insert_update_and_delete()
        {
            using (var context = CreateContext())
            {
                var product = new Product { Name = "blah" };
                context.Products.Add(product);
                context.SaveChanges();

                Assert.Equal(1, product.StoreUpdated);

                product.Name = "fooh";
                context.SaveChanges();

                Assert.Equal(2, product.StoreUpdated);

                context.Products.Remove(product);
                context.SaveChanges();

                Assert.Empty(context.Products);
            }
        }

        [ConditionalFact]
        public void Triggers_with_subqueries_work_with_batch_operations()
        {
            using (var context = CreateContext())
            {
                var productToBeUpdated1 = new Product { Name = "u1" };
                var productToBeUpdated2 = new Product { Name = "u2" };
                context.Products.Add(productToBeUpdated1);
                context.Products.Add(productToBeUpdated2);

                var productToBeDeleted1 = new Product { Name = "d1" };
                var productToBeDeleted2 = new Product { Name = "d2" };
                context.Products.Add(productToBeDeleted1);
                context.Products.Add(productToBeDeleted2);

                context.SaveChanges();

                var productToBeAdded1 = new Product { Name = "a1" };
                var productToBeAdded2 = new Product { Name = "a2" };
                context.Products.Add(productToBeAdded1);
                context.Products.Add(productToBeAdded2);

                productToBeUpdated1.Name = "n1";
                productToBeUpdated2.Name = "n2";

                context.Products.Remove(productToBeDeleted1);
                context.Products.Remove(productToBeDeleted2);

                context.SaveChanges();

                Assert.Equal(1, productToBeAdded1.StoreUpdated);
                Assert.Equal(1, productToBeAdded2.StoreUpdated);
                Assert.Equal(2, productToBeUpdated1.StoreUpdated);
                Assert.Equal(2, productToBeUpdated2.StoreUpdated);

                var products = context.Products.ToList();
                Assert.Equal(4, products.Count);

                context.Products.RemoveRange(products);

                context.SaveChanges();
            }
        }

        protected QueryTriggersContext CreateContext() => (QueryTriggersContext)Fixture.CreateContext();

        protected class QueryTriggersContext : PoolableDbContext
        {
            public QueryTriggersContext(DbContextOptions options)
                : base(options)
            {
            }

            public virtual DbSet<Product> Products { get; set; }

            protected override void OnModelCreating(ModelBuilder modelBuilder)
            {
                modelBuilder.Entity<Product>(
                    eb =>
                    {
                        eb.Property(e => e.StoreUpdated)
                            .HasDefaultValue(0)
                            .ValueGeneratedOnAddOrUpdate();
                        eb.ToTable("UpdatedProducts");
                    });
            }
        }

        protected class Product
        {
            public virtual int Id { get; set; }
            public virtual byte[] Version { get; set; }
            public virtual string Name { get; set; }
            public virtual int StoreUpdated { get; set; }
        }

        public class JetTriggersFixture : SharedStoreFixtureBase<DbContext>
        {
            protected override string StoreName { get; } = "JetQueryTriggers";
            protected override Type ContextType { get; } = typeof(QueryTriggersContext);
            protected override ITestStoreFactory TestStoreFactory => JetTestStoreFactory.Instance;

            protected override void Seed(DbContext context)
            {
                context.Database.EnsureCreatedResiliently();

                context.Database.ExecuteSqlRaw(
                    @"
CREATE TRIGGER TRG_InsertUpdateProduct
ON UpdatedProducts
AFTER INSERT, UPDATE AS
BEGIN
	IF @@ROWCOUNT = 0
		return
	SET nocount on;

    UPDATE UpdatedProducts set StoreUpdated = StoreUpdated + 1
    WHERE Id IN(SELECT INSERTED.Id FROM INSERTED);
END");
            }
        }
    }
}
