# (c) cavaliba.com - tests / app_home.migrator.migrate_ipam_subnet_keyname

from django.test import TestCase

import app_home.cache as cache
from app_data.data import Instance
from app_data.models import DataInstance
from app_home.migrator import migrate_ipam_subnet_keyname
from tests import helper


def _revert_to_pre_migration_schema():
    """Simulate the pre-4.9.0 ipam_subnet schema: freeform keyname (edit mode)
    plus the old "subnet" ipv4 field - lets a test create an instance whose
    keyname doesn't match its subnet value, exactly what a real database
    upgrading from before this schema change could have."""
    helper.add_schema(
        classname="ipam_subnet",
        options={"keyname_mode": "edit"},
        field_definition={
            "subnet": {
                "dataformat": "ipv4",
                "dataformat_ext": "subnet strict",
                "displayname": "IPv4 Subnet",
            },
        },
    )


def _restore_dataformat_schema():
    """Re-apply the real (post-change) ipam_subnet keyname_mode, as the
    reload_builtin() step earlier in the real migration would have done
    before migrate_ipam_subnet_keyname() runs."""
    helper.add_schema(
        classname="ipam_subnet",
        options={"keyname_mode": "dataformat ipv4 subnet strict"},
    )


class MigrateIpamSubnetKeynameTest(TestCase):
    def setUp(self):
        cache.clear()
        _revert_to_pre_migration_schema()

    def test_matching_keyname_is_untouched(self):
        helper.add_instance(
            classname="ipam_subnet",
            keyname="203.0.113.0/24",
            fields={"subnet": "203.0.113.0/24"},
        )
        _restore_dataformat_schema()

        migrate_ipam_subnet_keyname()

        instance = Instance.from_keyname(classname="ipam_subnet", keyname="203.0.113.0/24")
        self.assertIsNotNone(instance)

    def test_mismatched_keyname_is_renamed_to_subnet_value(self):
        helper.add_instance(
            classname="ipam_subnet",
            keyname="office-net",
            fields={"subnet": "203.0.113.0/24"},
        )
        _restore_dataformat_schema()

        migrate_ipam_subnet_keyname()

        self.assertIsNone(Instance.from_keyname(classname="ipam_subnet", keyname="office-net"))
        renamed = Instance.from_keyname(classname="ipam_subnet", keyname="203.0.113.0/24")
        self.assertIsNotNone(renamed)

    def test_vlan_referrer_updated_by_rename_cascade(self):
        helper.add_instance(
            classname="ipam_subnet",
            keyname="office-net",
            fields={"subnet": "203.0.113.0/24"},
        )
        helper.add_instance(
            classname="ipam_vlan",
            keyname="vlan_office",
            fields={"displayname": "Office VLAN", "subnet": ["office-net"]},
        )
        _restore_dataformat_schema()

        migrate_ipam_subnet_keyname()

        vlan = Instance.from_keyname(classname="ipam_vlan", keyname="vlan_office")
        self.assertEqual(vlan.fields["subnet"].get_value(), ["203.0.113.0/24"])

    def test_invalid_subnet_value_left_unmigrated(self):
        helper.add_instance(
            classname="ipam_subnet",
            keyname="office-net",
            fields={"subnet": "not-a-cidr"},
        )
        _restore_dataformat_schema()

        migrate_ipam_subnet_keyname()

        self.assertIsNotNone(Instance.from_keyname(classname="ipam_subnet", keyname="office-net"))

    def test_no_subnet_value_left_unmigrated(self):
        helper.add_instance(classname="ipam_subnet", keyname="office-net")
        _restore_dataformat_schema()

        migrate_ipam_subnet_keyname()

        self.assertIsNotNone(Instance.from_keyname(classname="ipam_subnet", keyname="office-net"))

    def test_colliding_subnet_values_second_one_skipped(self):
        helper.add_instance(
            classname="ipam_subnet",
            keyname="office-net-a",
            fields={"subnet": "203.0.113.0/24"},
        )
        helper.add_instance(
            classname="ipam_subnet",
            keyname="office-net-b",
            fields={"subnet": "203.0.113.0/24"},
        )
        _restore_dataformat_schema()

        migrate_ipam_subnet_keyname()

        # one of the two won the rename, the other was skipped (logged) rather
        # than crashing the migration loop
        remaining = DataInstance.objects.filter(
            classname="ipam_subnet", keyname__in=["office-net-a", "office-net-b"]
        ).count()
        self.assertEqual(remaining, 1)
        self.assertIsNotNone(
            Instance.from_keyname(classname="ipam_subnet", keyname="203.0.113.0/24")
        )

    def test_idempotent_second_run_is_noop(self):
        helper.add_instance(
            classname="ipam_subnet",
            keyname="office-net",
            fields={"subnet": "203.0.113.0/24"},
        )
        _restore_dataformat_schema()

        migrate_ipam_subnet_keyname()
        migrate_ipam_subnet_keyname()

        renamed = Instance.from_keyname(classname="ipam_subnet", keyname="203.0.113.0/24")
        self.assertIsNotNone(renamed)
