// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage;
using System.Threading.Tasks;

namespace EntityFrameworkCore.Jet.FunctionalTests;

#nullable disable

public class GraphUpdatesJetOwnedTest(GraphUpdatesJetOwnedTest.JetFixture fixture)
    : GraphUpdatesJetTestBase<GraphUpdatesJetOwnedTest.JetFixture>(fixture)
{
    // No owned types
    public override Task Update_root_by_collection_replacement_of_inserted_first_level(bool async)
        => Task.CompletedTask;

    // No owned types
    public override Task Mark_explicitly_set_dependent_appropriately_with_any_inheritance_and_stable_generator(bool async, bool useAdd)
        => Task.CompletedTask;

    // No owned types
    public override Task Mark_explicitly_set_stable_dependent_appropriately(bool async, bool useAdd)
        => Task.CompletedTask;

    // No owned types
    public override Task Mark_explicitly_set_stable_dependent_appropriately_when_deep_in_graph(bool async, bool useAdd)
        => Task.CompletedTask;

    // No owned types
    public override Task Update_root_by_collection_replacement_of_deleted_first_level(bool async)
        => Task.CompletedTask;

    // No owned types
    public override Task Update_root_by_collection_replacement_of_inserted_second_level(bool async)
        => Task.CompletedTask;

    // No owned types
    public override Task Update_root_by_collection_replacement_of_deleted_second_level(bool async)
        => Task.CompletedTask;

    // No owned types
    public override Task Update_root_by_collection_replacement_of_inserted_first_level_level(bool async)
        => Task.CompletedTask;

    // No owned types
    public override Task Update_root_by_collection_replacement_of_deleted_third_level(bool async)
        => Task.CompletedTask;

    // No owned types
    public override Task Sever_relationship_that_will_later_be_deleted(bool async)
        => Task.CompletedTask;

    // No owned types
    public override Task Alternate_key_over_foreign_key_doesnt_bypass_delete_behavior(bool async)
        => Task.CompletedTask;

    // No owned types
    public override Task Shadow_skip_navigation_in_base_class_is_handled(bool async)
        => Task.CompletedTask;

    // Owned dependents are always loaded
    public override Task Required_one_to_one_are_cascade_deleted_in_store(
        CascadeTiming? cascadeDeleteTiming,
        CascadeTiming? deleteOrphansTiming)
        => Task.CompletedTask;

    public override Task Required_one_to_one_with_alternate_key_are_cascade_deleted_in_store(
        CascadeTiming? cascadeDeleteTiming,
        CascadeTiming? deleteOrphansTiming)
        => Task.CompletedTask;

    // No owned types
    public override Task Can_insert_when_composite_FK_has_default_value_for_one_part(bool async)
        => Task.CompletedTask;

    public override Task Required_one_to_one_relationships_are_one_to_one(CascadeTiming? deleteOrphansTiming)
        => Task.CompletedTask;

    public override Task Required_one_to_one_with_AK_relationships_are_one_to_one(CascadeTiming? deleteOrphansTiming)
        => Task.CompletedTask;

    // No owned types
    public override Task Can_insert_when_bool_PK_in_composite_key_has_sentinel_value(bool async, bool initialValue)
        => Task.CompletedTask;

    // No owned types
    public override Task Can_insert_when_int_PK_in_composite_key_has_sentinel_value(bool async, int initialValue)
        => Task.CompletedTask;

    // No owned types
    public override Task Can_insert_when_nullable_bool_PK_in_composite_key_has_sentinel_value(bool async, bool? initialValue)
        => Task.CompletedTask;

    // No owned types
    public override Task Throws_for_single_property_bool_key_with_default_value_generation(bool async, bool initialValue)
        => Task.CompletedTask;

    // No owned types
    public override Task Throws_for_single_property_nullable_bool_key_with_default_value_generation(bool async, bool? initialValue)
        => Task.CompletedTask;

    protected override void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction)
        => facade.UseTransaction(transaction.GetDbTransaction());

    public class JetFixture : GraphUpdatesJetFixtureBase
    {
        protected override string StoreName
            => "GraphOwnedUpdatesTest";

        protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context)
        {
            modelBuilder.Entity<OwnerRoot>(
                b =>
                {
                    b.OwnsOne(e => e.OptionalSingle).OwnsOne(e => e.Single);
                    b.OwnsOne(e => e.RequiredSingle).OwnsOne(e => e.Single);
                    b.OwnsMany(e => e.OptionalChildren).OwnsMany(e => e.Children);
                    b.OwnsMany(e => e.RequiredChildren).OwnsMany(e => e.Children);
                });

            modelBuilder.Entity<Root>(
                b =>
                {
                    b.Property(e => e.AlternateId).ValueGeneratedOnAdd();

                    // TODO: Owned inheritance support #9630
                    b.HasMany(e => e.RequiredChildren)
                        .WithOne(e => e.Parent)
                        .HasForeignKey(e => e.ParentId);

                    modelBuilder.Entity<Required1>()
                        .HasMany(e => e.Children)
                        .WithOne(e => e.Parent)
                        .HasForeignKey(e => e.ParentId);

                    modelBuilder.Entity<Required1Derived>();
                    modelBuilder.Entity<Required1MoreDerived>();
                    modelBuilder.Entity<Required2Derived>();
                    modelBuilder.Entity<Required2MoreDerived>();

                    b.HasMany(e => e.OptionalChildren)
                        .WithOne(e => e.Parent)
                        .HasForeignKey(e => e.ParentId)
                        .OnDelete(DeleteBehavior.SetNull);

                    b.OwnsOne(
                        e => e.RequiredSingle, r =>
                        {
                            r.WithOwner(e => e.Root)
                                .HasForeignKey(e => e.Id);

                            r.OwnsOne(e => e.Single)
                                .WithOwner(e => e.Back)
                                .HasForeignKey(e => e.Id);
                        });

                    b.HasOne(e => e.OptionalSingle)
                        .WithOne(e => e.Root)
                        .HasForeignKey<OptionalSingle1>(e => e.RootId)
                        .OnDelete(DeleteBehavior.SetNull);

                    b.HasOne(e => e.OptionalSingleDerived)
                        .WithOne(e => e.DerivedRoot)
                        .HasForeignKey<OptionalSingle1Derived>(e => e.DerivedRootId)
                        .OnDelete(DeleteBehavior.ClientSetNull);

                    b.HasOne(e => e.OptionalSingleMoreDerived)
                        .WithOne(e => e.MoreDerivedRoot)
                        .HasForeignKey<OptionalSingle1MoreDerived>(e => e.MoreDerivedRootId)
                        .OnDelete(DeleteBehavior.ClientSetNull);

                    // TODO: Owned inheritance support #9630
                    b.HasOne(e => e.RequiredNonPkSingle)
                        .WithOne(e => e.Root)
                        .HasForeignKey<RequiredNonPkSingle1>(e => e.RootId);

                    b.HasOne(e => e.RequiredNonPkSingleDerived)
                        .WithOne(e => e.DerivedRoot)
                        .HasForeignKey<RequiredNonPkSingle1Derived>(e => e.DerivedRootId)
                        .OnDelete(DeleteBehavior.Restrict);

                    b.HasOne(e => e.RequiredNonPkSingleMoreDerived)
                        .WithOne(e => e.MoreDerivedRoot)
                        .HasForeignKey<RequiredNonPkSingle1MoreDerived>(e => e.MoreDerivedRootId)
                        .OnDelete(DeleteBehavior.Restrict);

                    // TODO: Owned inheritance support #9630
                    b.HasMany(e => e.RequiredChildrenAk)
                        .WithOne(e => e.Parent)
                        .HasPrincipalKey(e => e.AlternateId)
                        .HasForeignKey(e => e.ParentId);

                    modelBuilder.Entity<RequiredAk1>(
                        b =>
                        {
                            b.Property(e => e.AlternateId)
                                .ValueGeneratedOnAdd();

                            b.HasMany(e => e.Children)
                                .WithOne(e => e.Parent)
                                .HasPrincipalKey(e => e.AlternateId)
                                .HasForeignKey(e => e.ParentId);

                            b.HasMany(e => e.CompositeChildren)
                                .WithOne(e => e.Parent)
                                .HasPrincipalKey(
                                    e => new { e.Id, e.AlternateId })
                                .HasForeignKey(
                                    e => new { e.ParentId, e.ParentAlternateId });
                        });

                    modelBuilder.Entity<RequiredAk2>()
                        .Property(e => e.AlternateId)
                        .ValueGeneratedOnAdd();

                    modelBuilder.Entity<RequiredAk2Derived>();
                    modelBuilder.Entity<RequiredAk2MoreDerived>();

                    modelBuilder.Entity<RequiredAk1Derived>();
                    modelBuilder.Entity<RequiredAk1MoreDerived>();

                    b.HasMany(e => e.OptionalChildrenAk)
                        .WithOne(e => e.Parent)
                        .HasPrincipalKey(e => e.AlternateId)
                        .HasForeignKey(e => e.ParentId)
                        .OnDelete(DeleteBehavior.SetNull);

                    b.OwnsOne(
                        e => e.RequiredSingleAk, r =>
                        {
                            r.WithOwner(e => e.Root)
                                .HasPrincipalKey(e => e.AlternateId)
                                .HasForeignKey(e => e.RootId);

                            r.HasKey(e => e.Id);

                            r.Property(e => e.AlternateId)
                                .ValueGeneratedOnAdd();

                            r.OwnsOne(
                                e => e.Single, r2 =>
                                {
                                    r2.WithOwner(e => e.Back)
                                        .HasForeignKey(e => e.BackId)
                                        .HasPrincipalKey(e => e.AlternateId);

                                    r2.HasKey(e => e.Id);

                                    r2.Property(e => e.Id)
                                        .ValueGeneratedOnAdd();

                                    r2.Property(e => e.AlternateId)
                                        .ValueGeneratedOnAdd();

                                    r2.ToTable("RequiredSingleAk2");
                                });

                            r.OwnsOne(
                                e => e.SingleComposite, r2 =>
                                {
                                    r2.WithOwner(e => e.Back)
                                        .HasForeignKey(e => new { e.BackId, e.BackAlternateId })
                                        .HasPrincipalKey(e => new { e.Id, e.AlternateId });

                                    r2.HasKey(e => e.Id);

                                    r2.ToTable("RequiredSingleComposite2");
                                });

                            // Table splitting using AK is not supported #23208
                            r.ToTable("RequiredSingleAk1");
                        });

                    b.HasOne(e => e.OptionalSingleAk)
                        .WithOne(e => e.Root)
                        .HasPrincipalKey<Root>(e => e.AlternateId)
                        .HasForeignKey<OptionalSingleAk1>(e => e.RootId)
                        .OnDelete(DeleteBehavior.SetNull);

                    b.HasOne(e => e.OptionalSingleAkDerived)
                        .WithOne(e => e.DerivedRoot)
                        .HasPrincipalKey<Root>(e => e.AlternateId)
                        .HasForeignKey<OptionalSingleAk1Derived>(e => e.DerivedRootId)
                        .OnDelete(DeleteBehavior.ClientSetNull);

                    b.HasOne(e => e.OptionalSingleAkMoreDerived)
                        .WithOne(e => e.MoreDerivedRoot)
                        .HasPrincipalKey<Root>(e => e.AlternateId)
                        .HasForeignKey<OptionalSingleAk1MoreDerived>(e => e.MoreDerivedRootId)
                        .OnDelete(DeleteBehavior.ClientSetNull);

                    b.HasOne(e => e.RequiredNonPkSingleAk)
                        .WithOne(e => e.Root)
                        .HasPrincipalKey<Root>(e => e.AlternateId)
                        .HasForeignKey<RequiredNonPkSingleAk1>(e => e.RootId);

                    b.HasOne(e => e.RequiredNonPkSingleAkDerived)
                        .WithOne(e => e.DerivedRoot)
                        .HasPrincipalKey<Root>(e => e.AlternateId)
                        .HasForeignKey<RequiredNonPkSingleAk1Derived>(e => e.DerivedRootId)
                        .OnDelete(DeleteBehavior.Restrict);

                    b.HasOne(e => e.RequiredNonPkSingleAkMoreDerived)
                        .WithOne(e => e.MoreDerivedRoot)
                        .HasPrincipalKey<Root>(e => e.AlternateId)
                        .HasForeignKey<RequiredNonPkSingleAk1MoreDerived>(e => e.MoreDerivedRootId)
                        .OnDelete(DeleteBehavior.Restrict);

                    b.HasMany(e => e.RequiredCompositeChildren)
                        .WithOne(e => e.Parent)
                        .HasPrincipalKey(e => e.AlternateId)
                        .HasForeignKey(e => e.ParentAlternateId);
                });

            modelBuilder.Entity<Optional1>(
                b =>
                {
                    b.HasMany(e => e.Children)
                        .WithOne(e => e.Parent)
                        .HasForeignKey(e => e.ParentId)
                        .OnDelete(DeleteBehavior.SetNull);

                    b.HasMany(e => e.CompositeChildren)
                        .WithOne(e => e.Parent2)
                        .HasForeignKey(e => new { e.Parent2Id });
                });

            modelBuilder.Entity<Optional1Derived>();
            modelBuilder.Entity<Optional1MoreDerived>();
            modelBuilder.Entity<Optional2Derived>();
            modelBuilder.Entity<Optional2MoreDerived>();

            modelBuilder.Entity<OptionalSingle1>()
                .HasOne(e => e.Single)
                .WithOne(e => e.Back)
                .HasForeignKey<OptionalSingle2>(e => e.BackId)
                .OnDelete(DeleteBehavior.SetNull);

            modelBuilder.Entity<OptionalSingle2>(
                b =>
                {
                    b.HasDiscriminator(e => e.Disc)
                        .HasValue<OptionalSingle2>(new MyDiscriminator(1))
                        .HasValue<OptionalSingle2Derived>(new MyDiscriminator(2))
                        .HasValue<OptionalSingle2MoreDerived>(new MyDiscriminator(3));

                    b.Property(e => e.Disc)
                        .HasConversion(
                            v => v.Value,
                            v => new MyDiscriminator(v),
                            new ValueComparer<MyDiscriminator>(
                                (l, r) => l.Value == r.Value,
                                v => v.Value.GetHashCode(),
                                v => new MyDiscriminator(v.Value)))
                        .Metadata
                        .SetAfterSaveBehavior(PropertySaveBehavior.Save);
                });

            modelBuilder.Entity<RequiredNonPkSingle1>()
                .HasOne(e => e.Single)
                .WithOne(e => e.Back)
                .HasForeignKey<RequiredNonPkSingle2>(e => e.BackId);

            modelBuilder.Entity<RequiredNonPkSingle2Derived>();
            modelBuilder.Entity<RequiredNonPkSingle2MoreDerived>();

            modelBuilder.Entity<RequiredNonPkSingleAk1>(
                b =>
                {
                    b.Property(e => e.AlternateId)
                        .ValueGeneratedOnAdd();

                    b.HasOne(e => e.Single)
                        .WithOne(e => e.Back)
                        .HasForeignKey<RequiredNonPkSingleAk2>(e => e.BackId)
                        .HasPrincipalKey<RequiredNonPkSingleAk1>(e => e.AlternateId);
                });

            modelBuilder.Entity<OptionalAk1>(
                b =>
                {
                    b.Property(e => e.AlternateId)
                        .ValueGeneratedOnAdd();

                    b.HasMany(e => e.Children)
                        .WithOne(e => e.Parent)
                        .HasPrincipalKey(e => e.AlternateId)
                        .HasForeignKey(e => e.ParentId)
                        .OnDelete(DeleteBehavior.SetNull);

                    b.HasMany(e => e.CompositeChildren)
                        .WithOne(e => e.Parent)
                        .HasPrincipalKey(
                            e => new { e.Id, e.AlternateId })
                        .HasForeignKey(
                            e => new { e.ParentId, e.ParentAlternateId });
                });

            modelBuilder.Entity<OptionalAk1Derived>();
            modelBuilder.Entity<OptionalAk1MoreDerived>();

            modelBuilder.Entity<OptionalAk2>()
                .Property(e => e.AlternateId)
                .ValueGeneratedOnAdd();

            modelBuilder.Entity<OptionalAk2Derived>();
            modelBuilder.Entity<OptionalAk2MoreDerived>();

            modelBuilder.Entity<RequiredNonPkSingleAk2>()
                .Property(e => e.AlternateId)
                .ValueGeneratedOnAdd();

            modelBuilder.Entity<RequiredNonPkSingleAk2Derived>();
            modelBuilder.Entity<RequiredNonPkSingleAk2MoreDerived>();

            modelBuilder.Entity<OptionalSingleAk1>(
                b =>
                {
                    b.Property(e => e.AlternateId)
                        .ValueGeneratedOnAdd();

                    b.HasOne(e => e.Single)
                        .WithOne(e => e.Back)
                        .HasForeignKey<OptionalSingleAk2>(e => e.BackId)
                        .HasPrincipalKey<OptionalSingleAk1>(e => e.AlternateId)
                        .OnDelete(DeleteBehavior.SetNull);

                    b.HasOne(e => e.SingleComposite)
                        .WithOne(e => e.Back)
                        .HasForeignKey<OptionalSingleComposite2>(
                            e => new { e.BackId, e.ParentAlternateId })
                        .HasPrincipalKey<OptionalSingleAk1>(
                            e => new { e.Id, e.AlternateId });
                });

            modelBuilder.Entity<OptionalSingleAk2>()
                .Property(e => e.AlternateId)
                .ValueGeneratedOnAdd();

            modelBuilder.Entity<OptionalSingleAk2Derived>();
            modelBuilder.Entity<OptionalSingleAk2MoreDerived>();

            modelBuilder.Entity<RequiredComposite1>(
                eb =>
                {
                    eb.Property(e => e.Id).ValueGeneratedNever();

                    eb.HasKey(
                        e => new { e.Id, e.ParentAlternateId });

                    eb.HasMany(e => e.CompositeChildren)
                        .WithOne(e => e.Parent)
                        .HasPrincipalKey(
                            e => new { e.Id, e.ParentAlternateId })
                        .HasForeignKey(
                            e => new { e.ParentId, e.ParentAlternateId });
                });

            modelBuilder.Entity<OptionalOverlapping2>(
                eb =>
                {
                    eb.Property(e => e.Id).ValueGeneratedNever();

                    eb.HasKey(
                        e => new { e.Id, e.ParentAlternateId });

                    eb.HasOne(e => e.Root)
                        .WithMany()
                        .HasPrincipalKey(e => e.AlternateId)
                        .HasForeignKey(e => e.ParentAlternateId);
                });

            modelBuilder.Entity<BadCustomer>();
            modelBuilder.Entity<BadOrder>();

            modelBuilder.Entity<QuestTask>();

            modelBuilder.Entity<QuizTask>()
                .HasMany(qt => qt.Choices)
                .WithOne()
                .HasForeignKey(tc => tc.QuestTaskId);

            modelBuilder.Entity<HiddenAreaTask>()
                .HasMany(hat => hat.Choices)
                .WithOne()
                .HasForeignKey(tc => tc.QuestTaskId);

            modelBuilder.Entity<TaskChoice>();
            modelBuilder.Entity<ParentAsAChild>();
            modelBuilder.Entity<ChildAsAParent>();

            modelBuilder.Entity<Poost>();
            modelBuilder.Entity<Bloog>();

            modelBuilder.Entity<Produce>()
                .HasIndex(e => e.BarCode)
                .IsUnique();

            modelBuilder.Entity<SharedFkRoot>(
                builder =>
                {
                    builder.HasMany(x => x.Dependants).WithOne(x => x.Root)
                        .HasForeignKey(x => new { x.RootId })
                        .HasPrincipalKey(x => x.Id)
                        .OnDelete(DeleteBehavior.Cascade);

                    builder.HasMany(x => x.Parents).WithOne(x => x.Root)
                        .HasForeignKey(x => new { x.RootId })
                        .HasPrincipalKey(x => x.Id)
                        .OnDelete(DeleteBehavior.Cascade);
                });

            modelBuilder.Entity<SharedFkParent>(
                builder =>
                {
                    builder.HasOne(x => x.Dependant).WithOne(x => x!.Parent).IsRequired(false)
                        .HasForeignKey<SharedFkParent>(x => new { x.RootId, x.DependantId })
                        .HasPrincipalKey<SharedFkDependant>(x => new { x.RootId, x.Id })
                        .OnDelete(DeleteBehavior.ClientSetNull);
                });

            modelBuilder.Entity<SharedFkDependant>();

            modelBuilder.Entity<Owner>();

            modelBuilder.Entity<OwnerWithKeyedCollection>(
                b =>
                {
                    b.Navigation(e => e.Owned).IsRequired();
                    b.Navigation(e => e.OwnedWithKey).IsRequired();

                    b.OwnsMany(
                        e => e.OwnedCollectionPrivateKey,
                        b => b.HasKey("OwnerWithKeyedCollectionId", "PrivateKey"));
                });

            modelBuilder
                .Entity<OwnerWithNonCompositeOwnedCollection>()
                .OwnsMany(e => e.Owned, owned => owned.HasKey("Id"));

            modelBuilder.Entity<OwnerNoKeyGeneration>(
                b =>
                {
                    b.Property(e => e.Id).ValueGeneratedNever();

                    b.OwnsOne(
                        e => e.Owned,
                        b => b.Property("OwnerNoKeyGenerationId").ValueGeneratedNever());
                    b.OwnsMany(
                        e => e.OwnedCollection,
                        b =>
                        {
                            b.Property<int>("OwnedNoKeyGenerationId").ValueGeneratedNever();
                            b.Property("OwnerNoKeyGenerationId").ValueGeneratedNever();
                        });
                });

            modelBuilder.Entity<Provider>().HasData(
                new Provider { Id = "prov1" },
                new Provider { Id = "prov2" });

            modelBuilder.Entity<Partner>().HasData(
                new Partner { Id = "partner1" });

            modelBuilder.Entity<ProviderContract>(
                b =>
                {
                    b.HasOne(p => p.Partner).WithMany().IsRequired().HasForeignKey("PartnerId");
                    b.HasOne<Provider>().WithMany().IsRequired().HasForeignKey("ProviderId");

                    b.HasDiscriminator<string>("ProviderId")
                        .HasValue<ProviderContract1>("prov1")
                        .HasValue<ProviderContract2>("prov2");
                });

            modelBuilder.Entity<EventDescriptorZ>(
                b =>
                {
                    b.Property<long>("EntityZId");
                    b.HasOne(e => e.EntityZ).WithMany().HasForeignKey("EntityZId").IsRequired();
                });

            modelBuilder.Entity<City>();

            modelBuilder.Entity<AccessState>(
                b =>
                {
                    b.Property(e => e.AccessStateId).ValueGeneratedNever();
                    b.HasData(new AccessState { AccessStateId = 1 });
                });

            modelBuilder.Entity<Cruiser>(
                b =>
                {
                    b.Property(e => e.IdUserState).HasDefaultValue(1);
                    b.HasOne(e => e.UserState).WithMany(e => e.Users).HasForeignKey(e => e.IdUserState);
                });

            modelBuilder.Entity<AccessStateWithSentinel>(
                b =>
                {
                    b.Property(e => e.AccessStateWithSentinelId).ValueGeneratedNever();
                    b.HasData(new AccessStateWithSentinel { AccessStateWithSentinelId = 1 });
                });

            modelBuilder.Entity<CruiserWithSentinel>(
                b =>
                {
                    b.Property(e => e.IdUserState).HasDefaultValue(1).HasSentinel(667);
                    b.HasOne(e => e.UserState).WithMany(e => e.Users).HasForeignKey(e => e.IdUserState);
                });

            modelBuilder.Entity<StringKeyAndIndexParent>(
                b =>
                {
                    b.HasAlternateKey(e => e.AlternateId);
                    b.OwnsOne(
                        x => x.Child, b =>
                        {
                            b.WithOwner(e => e.Parent).HasForeignKey(e => e.ParentId);
                        });
                });

            modelBuilder.Entity<SharedFkRoot>().Property(x => x.Id).HasColumnType("int");
            modelBuilder.Entity<SharedFkDependant>().Property(x => x.Id).HasColumnType("int");
            modelBuilder.Entity<SharedFkParent>().Property(x => x.Id).HasColumnType("int");
        }
    }
}
