"""Tests that exercise the INodeTypeForwarding interface"""
import omni.graph.core._unstable as ogu
import omni.graph.core.tests as ogts
import omni.kit.test
import omni.usd


# ==============================================================================================================
class TestNodeTypeForwarding(omni.kit.test.AsyncTestCase):
    async def test_bindings(self):
        """Tests that the bindings for the ABI operate as expected"""
        interface_class = ogu.INodeTypeForwarding
        ogts.validate_abi_interface(
            interface_class,
            instance_methods=[
                "define_forward",
                "remove_forward",
                "remove_forwarded_type",
            ],
            static_methods=[
                "find_forward",
                "get_forwarding",
            ],
            properties=["forward_count"],
        )

        get_interface = ogu.get_node_type_forwarding_interface
        self.assertIsNotNone(get_interface)
        interface = get_interface()
        self.assertIsNotNone(interface)
        self.assertTrue(isinstance(interface, ogu.INodeTypeForwarding))

        try:
            # Try defining a simple forward
            original_forwarding = interface.forward_count
            replacement = ("ReplacementNodeType", 1, "omni.test.extension")
            original = ("ForwardedNodeType", 2)
            self.assertTrue(interface.define_forward(*original, *replacement))
            self.assertEqual(original_forwarding + 1, interface.forward_count)

            all_forwarding = interface.get_forwarding()
            self.assertEqual(all_forwarding[original], replacement)

            found_forward = interface.find_forward(*original)
            self.assertEqual(found_forward, replacement)

            # Define a second forward
            secondary_replacement = ("SecondNodeType", 4, "omni.test.extension")
            secondary = ("SomeOtherNodeType", 2)
            self.assertTrue(interface.define_forward(*secondary, *secondary_replacement))
            self.assertEqual(original_forwarding + 2, interface.forward_count)
            all_forwarding = interface.get_forwarding()
            self.assertEqual(all_forwarding[original], replacement)
            self.assertEqual(all_forwarding[secondary], secondary_replacement)

            # Define a chained forward ("ChainedNodeType", 1) -> ("ReplacementNodeType", 1) -> ("ForwardedNodeType", 2)
            chained_replacement = ("ChainedNodeType", 1, "omni.test.extension")
            chained = (replacement[0], replacement[1])
            self.assertTrue(interface.define_forward(*chained, *chained_replacement))
            found_forward = interface.find_forward(*chained)
            self.assertEqual(found_forward, chained_replacement)
            found_forward = interface.find_forward(*original)
            self.assertEqual(found_forward, chained_replacement)

            # Define a few forwarding for the same node type at different versions
            # ("ReplacementNodeType", 1) -> ("ForwardedNodeType", 2, "omni.test.extension")
            # ("ReplacementNodeType", 3) -> ("BetterNodeType", 1, "omni.borg.extension")
            # ("ReplacementNodeType", 7) -> ("SecondaryNodeType", 4, "omni.test.extension")
            better_replacement = ("BetterNodeType", 4, "omni.borg.extension")
            latest_forward = (replacement[0], replacement[1] + 2)
            self.assertTrue(interface.define_forward(*latest_forward, *better_replacement))
            prototype_forward = (replacement[0], replacement[1] + 6)
            self.assertTrue(interface.define_forward(*prototype_forward, *secondary_replacement))
            # What the "replacement" forward is expected to map to for the version equal to the index of the list...
            expected_versions = [
                chained_replacement,
                chained_replacement,
                better_replacement,
                better_replacement,
                better_replacement,
                better_replacement,
                secondary_replacement,
                secondary_replacement,
                secondary_replacement,
            ]
            for index, version_to_test in enumerate(expected_versions):
                self.assertEqual(
                    version_to_test,
                    interface.find_forward(replacement[0], index + 1),
                    f"Checking {replacement[0]}-{index + 1} maps to {version_to_test}",
                )

            # Ensure that attempting to look up a version earlier than the first forward returns nothing
            early_version = (replacement[0], 0)
            with self.assertRaises(ValueError):
                interface.find_forward(*early_version)

            # Attempt to define a circular forward, which should not be allowed
            circular = (chained_replacement[0], chained_replacement[1])
            with ogts.ExpectedError():
                self.assertFalse(interface.define_forward(*circular, *chained_replacement))

            # Attempt to define a multiple-step circular forward, which should not be allowed
            # ("ChainedNodeType", 1) -> ("ReplacementNodeType", 1) -> ("ForwardedNodeType", 2) -> ("ChainedNodeType", 1)
            with ogts.ExpectedError():
                self.assertFalse(interface.define_forward(*chained, original[0], original[1], "omni.test.extension"))

            # Attempt to redefine the same forward, which should not be allowed
            with ogts.ExpectedError():
                self.assertFalse(interface.define_forward(*chained, *chained_replacement))

        finally:
            # Remove all of the forwarding, in reverse order so that earlier failures clean up after themselves
            self.assertTrue(interface.remove_forward(*prototype_forward))
            self.assertTrue(interface.remove_forward(*latest_forward))
            self.assertTrue(interface.remove_forward(*chained))
            self.assertTrue(interface.remove_forward(*secondary))
            self.assertTrue(interface.remove_forward(*original))

            self.assertEqual(original_forwarding, interface.forward_count)
