# Generated by Django 5.2.1 on 2025-05-19 15:01
from typing import TYPE_CHECKING

import django.core.validators
import django.db.migrations.operations.special
import django.db.models.deletion
import taggit.managers
import utilities.json
from django.conf import settings
from django.db import migrations
from django.db import models
from extras.choices import CustomFieldTypeChoices
from extras.choices import CustomFieldUIEditableChoices
from extras.choices import CustomFieldUIVisibleChoices
from extras.choices import CustomLinkButtonClassChoices

from ipfabric_netbox.utilities.transform_map import build_transform_maps
from ipfabric_netbox.utilities.transform_map import get_transform_map

if TYPE_CHECKING:
    from django.apps import apps as apps_type
    from django.db.backends.base.schema import BaseDatabaseSchemaEditor

# This is squashed migration since when StagedChanges were removed, NetBox Branch model
# was moved to netbox branching. But IPFabricBranch was extending it and I had to remove it.
# We need to avoid creating IPFabricBranch on NetBox >4.3.0 since Branch is not there.
# So instead the squash does not create it and conditionally removed the table.


def create_custom_field(
    apps: "apps_type",
    db_alias: str,
    field_name: str,
    label: str,
    target_models: list,
    object_type=None,
    cf_type: str | None = "type_text",
):
    """Create a single custom field and link it to required models."""
    ObjectType = apps.get_model("core", "ObjectType")
    ContentType = apps.get_model("contenttypes", "ContentType")

    defaults = {
        "label": label,
        "related_object_type": (
            ObjectType.objects.get_for_model(object_type) if object_type else None
        ),
        "ui_visible": getattr(CustomFieldUIVisibleChoices, "ALWAYS"),
        "ui_editable": getattr(CustomFieldUIEditableChoices, "NO"),
    }

    custom_field, _ = (
        apps.get_model("extras", "CustomField")
        .objects.using(db_alias)
        .update_or_create(
            type=getattr(CustomFieldTypeChoices, cf_type.upper()),
            name=field_name,
            defaults=defaults,
        )
    )

    for model in target_models:
        custom_field.object_types.add(ContentType.objects.get_for_model(model))


def prepare_custom_fields(apps: "apps_type", schema_editor: "BaseDatabaseSchemaEditor"):
    """Forward migration to prepare ipfabric_netbox custom fields and links."""
    db_alias = schema_editor.connection.alias
    Device = apps.get_model("dcim", "Device")
    Site = apps.get_model("dcim", "Site")

    create_custom_field(
        apps,
        db_alias,
        "ipfabric_source",
        "IP Fabric Source",
        [Device, Site],
        cf_type="type_object",
        object_type=apps.get_model("ipfabric_netbox", "IPFabricSource"),
    )
    create_custom_field(
        apps,
        db_alias,
        "ipfabric_ingestion",
        "IP Fabric Last Ingestion",
        [Device, Site],
        cf_type="type_object",
        object_type=apps.get_model("ipfabric_netbox", "IPFabricIngestion"),
    )
    cl, _ = (
        apps.get_model("extras", "CustomLink")
        .objects.using(db_alias)
        .update_or_create(
            defaults={
                "link_text": "{% if object.custom_field_data.ipfabric_source is defined %}{% set SOURCE_ID = object.custom_field_data.ipfabric_source %}{% if SOURCE_ID %}IP Fabric{% endif %}{% endif %}",
                "link_url": '{% if object.custom_field_data.ipfabric_source is defined %}{% set SOURCE_ID = object.custom_field_data.ipfabric_source %}{% if SOURCE_ID %}{% set BASE_URL = object.custom_fields.filter(related_object_type__model="ipfabricsource").first().related_object_type.model_class().objects.get(pk=SOURCE_ID).url %}{{ BASE_URL }}/inventory/devices?options={"filters":{"sn": ["like","{{ object.serial }}"]}}{% endif %}{%endif%}',
                "new_window": True,
                "button_class": CustomLinkButtonClassChoices.BLUE,
            },
            name="ipfabric",
        )
    )
    cl.object_types.add(
        apps.get_model("contenttypes", "ContentType")
        .objects.db_manager(db_alias)
        .get_for_model(Device)
    )


def cleanup_custom_fields(apps: "apps_type", schema_editor: "BaseDatabaseSchemaEditor"):
    """Reverse migration to prepare ipfabric_netbox custom fields and links."""
    db_alias = schema_editor.connection.alias
    for field_name in ["ipfabric_source", "ipfabric_ingestion"]:
        custom_field = (
            apps.get_model("extras", "CustomField")
            .objects.using(db_alias)
            .get(name=field_name)
        )
        for model in custom_field.object_types.all()[:]:
            custom_field.object_types.remove(model)
        custom_field.delete()


def prepare_transform_maps(
    apps: "apps_type", schema_editor: "BaseDatabaseSchemaEditor"
):
    """Create transform maps if they do not exist yet.
    They used to be created during plugin.ready() so they might be present on older DBs.
    """
    build_transform_maps(
        data=get_transform_map(), apps=apps, db_alias=schema_editor.connection.alias
    )


class Migration(migrations.Migration):
    replaces = [
        ("ipfabric_netbox", "0001_initial"),
        ("ipfabric_netbox", "0002_ipfabricsnapshot_status"),
        ("ipfabric_netbox", "0003_ipfabricsource_type_and_more"),
        ("ipfabric_netbox", "0004_ipfabricsync_auto_merge"),
        (
            "ipfabric_netbox",
            "0005_alter_ipfabricrelationshipfield_source_model_and_more",
        ),
        ("ipfabric_netbox", "0006_alter_ipfabrictransformmap_target_model"),
        ("ipfabric_netbox", "0007_prepare_custom_fields"),
        ("ipfabric_netbox", "0008_prepare_transform_maps"),
        ("ipfabric_netbox", "0009_transformmap_changes_for_netbox_v4_2"),
        ("ipfabric_netbox", "0010_remove_uuid_from_get_or_create"),
        ("ipfabric_netbox", "0011_update_part_number_DCIM_inventory_item_template"),
        ("ipfabric_netbox", "0012_remove_status_field"),
        ("ipfabric_netbox", "0013_switch_to_branching_plugin"),
    ]

    dependencies = [
        ("contenttypes", "0002_remove_content_type_name"),
        ("core", "0012_job_object_type_optional"),
        ("dcim", "0191_module_bay_rebuild"),
        ("extras", "0123_journalentry_kind_default"),
        ("ipam", "0070_vlangroup_vlan_id_ranges"),
        ("netbox_branching", "0003_rename_indexes"),
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
    ]

    operations = [
        migrations.CreateModel(
            name="IPFabricSource",
            fields=[
                (
                    "id",
                    models.BigAutoField(
                        auto_created=True, primary_key=True, serialize=False
                    ),
                ),
                ("created", models.DateTimeField(auto_now_add=True, null=True)),
                ("last_updated", models.DateTimeField(auto_now=True, null=True)),
                (
                    "custom_field_data",
                    models.JSONField(
                        blank=True,
                        default=dict,
                        encoder=utilities.json.CustomFieldJSONEncoder,
                    ),
                ),
                ("description", models.CharField(blank=True, max_length=200)),
                ("comments", models.TextField(blank=True)),
                ("name", models.CharField(max_length=100, unique=True)),
                ("url", models.CharField(max_length=200)),
                (
                    "status",
                    models.CharField(default="new", editable=False, max_length=50),
                ),
                ("parameters", models.JSONField(blank=True, null=True)),
                ("last_synced", models.DateTimeField(blank=True, null=True)),
                (
                    "tags",
                    taggit.managers.TaggableManager(
                        through="extras.TaggedItem", to="extras.Tag"
                    ),
                ),
                ("type", models.CharField(default="local", max_length=50)),
            ],
            options={
                "verbose_name": "IP Fabric Source",
                "verbose_name_plural": "IP Fabric Sources",
                "ordering": ("name",),
            },
        ),
        migrations.CreateModel(
            name="IPFabricSnapshot",
            fields=[
                (
                    "id",
                    models.BigAutoField(
                        auto_created=True, primary_key=True, serialize=False
                    ),
                ),
                ("created", models.DateTimeField(auto_now_add=True)),
                ("last_updated", models.DateTimeField(editable=False)),
                ("name", models.CharField(max_length=200)),
                (
                    "source",
                    models.ForeignKey(
                        editable=False,
                        on_delete=django.db.models.deletion.CASCADE,
                        related_name="snapshots",
                        to="ipfabric_netbox.ipfabricsource",
                    ),
                ),
                ("snapshot_id", models.CharField(max_length=100)),
                ("status", models.CharField(default="unloaded", max_length=50)),
                ("data", models.JSONField(blank=True, null=True)),
                ("date", models.DateTimeField(blank=True, editable=False, null=True)),
            ],
            options={
                "verbose_name": "IP Fabric Snapshot",
                "verbose_name_plural": "IP Fabric Snapshots",
                "ordering": ("source", "-date"),
            },
        ),
        migrations.CreateModel(
            name="IPFabricTransformMap",
            fields=[
                (
                    "id",
                    models.BigAutoField(
                        auto_created=True, primary_key=True, serialize=False
                    ),
                ),
                ("created", models.DateTimeField(auto_now_add=True, null=True)),
                ("last_updated", models.DateTimeField(auto_now=True, null=True)),
                (
                    "custom_field_data",
                    models.JSONField(
                        blank=True,
                        default=dict,
                        encoder=utilities.json.CustomFieldJSONEncoder,
                    ),
                ),
                ("name", models.CharField(max_length=100, unique=True)),
                ("source_model", models.CharField(max_length=50)),
                (
                    "tags",
                    taggit.managers.TaggableManager(
                        through="extras.TaggedItem", to="extras.Tag"
                    ),
                ),
                (
                    "target_model",
                    models.ForeignKey(
                        limit_choices_to=models.Q(
                            models.Q(
                                models.Q(("app_label", "dcim"), ("model", "site")),
                                models.Q(
                                    ("app_label", "dcim"), ("model", "manufacturer")
                                ),
                                models.Q(("app_label", "dcim"), ("model", "platform")),
                                models.Q(
                                    ("app_label", "dcim"), ("model", "devicerole")
                                ),
                                models.Q(
                                    ("app_label", "dcim"), ("model", "devicetype")
                                ),
                                models.Q(("app_label", "dcim"), ("model", "device")),
                                models.Q(
                                    ("app_label", "dcim"), ("model", "virtualchassis")
                                ),
                                models.Q(("app_label", "dcim"), ("model", "interface")),
                                models.Q(
                                    ("app_label", "dcim"), ("model", "macaddress")
                                ),
                                models.Q(("app_label", "ipam"), ("model", "vlan")),
                                models.Q(("app_label", "ipam"), ("model", "vrf")),
                                models.Q(("app_label", "ipam"), ("model", "prefix")),
                                models.Q(("app_label", "ipam"), ("model", "ipaddress")),
                                models.Q(
                                    ("app_label", "contenttypes"),
                                    ("model", "contenttype"),
                                ),
                                models.Q(("app_label", "tenancy"), ("model", "tenant")),
                                models.Q(
                                    ("app_label", "dcim"), ("model", "inventoryitem")
                                ),
                                _connector="OR",
                            )
                        ),
                        on_delete=django.db.models.deletion.PROTECT,
                        related_name="+",
                        to="contenttypes.contenttype",
                    ),
                ),
            ],
            options={
                "verbose_name": "IP Fabric Transform Map",
                "verbose_name_plural": "IP Fabric Transform Maps",
            },
        ),
        migrations.CreateModel(
            name="IPFabricTransformField",
            fields=[
                (
                    "id",
                    models.BigAutoField(
                        auto_created=True, primary_key=True, serialize=False
                    ),
                ),
                ("source_field", models.CharField(max_length=100)),
                ("target_field", models.CharField(max_length=100)),
                ("coalesce", models.BooleanField(default=False)),
                ("template", models.TextField(blank=True, default="")),
                (
                    "transform_map",
                    models.ForeignKey(
                        on_delete=django.db.models.deletion.CASCADE,
                        related_name="field_maps",
                        to="ipfabric_netbox.ipfabrictransformmap",
                    ),
                ),
            ],
            options={
                "verbose_name": "IP Fabric Transform Field",
                "verbose_name_plural": "IP Fabric Transform Fields",
                "ordering": ("transform_map",),
            },
        ),
        migrations.CreateModel(
            name="IPFabricSync",
            fields=[
                (
                    "id",
                    models.BigAutoField(
                        auto_created=True, primary_key=True, serialize=False
                    ),
                ),
                ("created", models.DateTimeField(auto_now_add=True, null=True)),
                ("last_updated", models.DateTimeField(auto_now=True, null=True)),
                ("name", models.CharField(max_length=100, unique=True)),
                ("type", models.CharField(default="dcim", max_length=50)),
                (
                    "status",
                    models.CharField(default="new", editable=False, max_length=50),
                ),
                ("parameters", models.JSONField(blank=True, null=True)),
                (
                    "last_synced",
                    models.DateTimeField(blank=True, editable=False, null=True),
                ),
                ("scheduled", models.DateTimeField(blank=True, null=True)),
                (
                    "interval",
                    models.PositiveIntegerField(
                        blank=True,
                        null=True,
                        validators=[django.core.validators.MinValueValidator(1)],
                    ),
                ),
                (
                    "snapshot_data",
                    models.ForeignKey(
                        on_delete=django.db.models.deletion.CASCADE,
                        related_name="snapshots",
                        to="ipfabric_netbox.ipfabricsnapshot",
                    ),
                ),
                (
                    "tags",
                    taggit.managers.TaggableManager(
                        through="extras.TaggedItem", to="extras.Tag"
                    ),
                ),
                (
                    "user",
                    models.ForeignKey(
                        blank=True,
                        null=True,
                        on_delete=django.db.models.deletion.SET_NULL,
                        related_name="+",
                        to=settings.AUTH_USER_MODEL,
                    ),
                ),
                ("auto_merge", models.BooleanField(default=False)),
            ],
            options={
                "verbose_name": "IP Fabric Sync",
                "ordering": ["pk"],
            },
        ),
        migrations.CreateModel(
            name="IPFabricData",
            fields=[
                (
                    "id",
                    models.BigAutoField(
                        auto_created=True, primary_key=True, serialize=False
                    ),
                ),
                ("data", models.JSONField(blank=True, null=True)),
                ("type", models.CharField(max_length=50)),
                (
                    "snapshot_data",
                    models.ForeignKey(
                        on_delete=django.db.models.deletion.CASCADE,
                        related_name="ipf_data",
                        to="ipfabric_netbox.ipfabricsnapshot",
                    ),
                ),
            ],
        ),
        migrations.CreateModel(
            name="IPFabricRelationshipField",
            fields=[
                (
                    "id",
                    models.BigAutoField(
                        auto_created=True, primary_key=True, serialize=False
                    ),
                ),
                ("target_field", models.CharField(max_length=100)),
                ("coalesce", models.BooleanField(default=False)),
                ("template", models.TextField(blank=True, default="")),
                (
                    "source_model",
                    models.ForeignKey(
                        limit_choices_to=models.Q(
                            models.Q(
                                ("app_label", "dcim"),
                                ("app_label", "ipam"),
                                ("app_label", "tenancy"),
                                models.Q(
                                    ("app_label", "contenttypes"),
                                    ("model", "contenttype"),
                                ),
                                _connector="OR",
                            )
                        ),
                        on_delete=django.db.models.deletion.PROTECT,
                        related_name="ipfabric_transform_fields",
                        to="contenttypes.contenttype",
                    ),
                ),
                (
                    "transform_map",
                    models.ForeignKey(
                        on_delete=django.db.models.deletion.CASCADE,
                        related_name="relationship_maps",
                        to="ipfabric_netbox.ipfabrictransformmap",
                    ),
                ),
            ],
            options={
                "verbose_name": "IP Fabric Relationship Field",
                "verbose_name_plural": "IP Fabric Relationship Fields",
                "ordering": ("transform_map",),
            },
        ),
        migrations.CreateModel(
            name="IPFabricIngestion",
            fields=[
                (
                    "id",
                    models.BigAutoField(
                        auto_created=True, primary_key=True, serialize=False
                    ),
                ),
                (
                    "branch",
                    models.OneToOneField(
                        null=True,
                        on_delete=django.db.models.deletion.SET_NULL,
                        to="netbox_branching.branch",
                    ),
                ),
                (
                    "job",
                    models.ForeignKey(
                        null=True,
                        on_delete=django.db.models.deletion.SET_NULL,
                        to="core.job",
                    ),
                ),
                (
                    "sync",
                    models.ForeignKey(
                        on_delete=django.db.models.deletion.CASCADE,
                        to="ipfabric_netbox.ipfabricsync",
                    ),
                ),
            ],
            options={
                "verbose_name": "IP Fabric Ingestion",
                "verbose_name_plural": "IP Fabric Ingestion",
                "ordering": ("pk",),
            },
        ),
        migrations.RunPython(
            code=prepare_custom_fields,
            reverse_code=cleanup_custom_fields,
        ),
        migrations.RunPython(
            code=prepare_transform_maps,
            reverse_code=migrations.RunPython.noop,
        ),
    ]
