from django.core.exceptions import ImproperlyConfigured
from netbox.plugins import PluginConfig

__author__ = "Andy Norwood"
__version__ = "0.4.6"


class LibreNMSSyncConfig(PluginConfig):
    name = "netbox_librenms_plugin"
    verbose_name = "NetBox Librenms Plugin"
    description = "Netbox plugin to sync data between LibreNMS and Netbox."
    author = __author__
    version = __version__
    base_url = "librenms_plugin"
    min_version = "4.2.0"
    required_settings = []  # Custom validation in ready() method
    default_settings = {
        "enable_caching": True,
        "verify_ssl": True,
        "interface_name_field": "ifName",
    }

    def ready(self):
        """
        Perform custom validation for plugin configuration.
        Supports both legacy single-server and new multi-server configurations.
        """
        super().ready()

        from django.conf import settings
        from django.db.models.signals import post_migrate

        plugin_config = getattr(settings, "PLUGINS_CONFIG", {}).get(self.name, {})

        # Check if using new multi-server configuration
        if "servers" in plugin_config:
            self._validate_multi_server_config(plugin_config["servers"])
        else:
            self._validate_legacy_config(plugin_config)

        # Auto-create the librenms_id custom field after migrations complete
        post_migrate.connect(
            _ensure_librenms_id_custom_field,
            dispatch_uid="netbox_librenms_plugin_ensure_cf",
        )

    def _validate_multi_server_config(self, servers_config):
        """Validate multi-server configuration."""
        if not servers_config or not isinstance(servers_config, dict):
            raise ImproperlyConfigured(
                f"Plugin {self.name} requires at least one server configuration in the 'servers' section."
            )

        for server_key, server_config in servers_config.items():
            if not isinstance(server_config, dict):
                raise ImproperlyConfigured(f"Plugin {self.name} server '{server_key}' must be a dictionary.")

            for setting in ["librenms_url", "api_token"]:
                if setting not in server_config:
                    raise ImproperlyConfigured(f"Plugin {self.name} server '{server_key}' requires '{setting}'.")

    def _validate_legacy_config(self, plugin_config):
        """Validate legacy single-server configuration."""
        for setting in ["librenms_url", "api_token"]:
            if setting not in plugin_config:
                raise ImproperlyConfigured(
                    f"Plugin {self.name} requires either 'servers' configuration or legacy '{setting}' setting."
                )


def _ensure_librenms_id_custom_field(sender, **kwargs):
    """
    Auto-create (or migrate) the 'librenms_id' custom field.
    Runs after migrations via post_migrate signal to ensure tables exist.
    Uses dispatch_uid to avoid duplicate connections.

    librenms_id stores a per-server JSON mapping {"server_key": device_id}.
    Legacy installations may have this field typed as 'integer'; we upgrade it
    to 'json' automatically so the UI and API accept the dict format.
    """
    # Track per-alias execution so each database alias is bootstrapped exactly once.
    db_alias = kwargs.get("using") or "default"
    executed_aliases = getattr(_ensure_librenms_id_custom_field, "_executed_aliases", set())
    if db_alias in executed_aliases:
        return

    import logging

    try:
        from django.contrib.contenttypes.models import ContentType

        from extras.models import CustomField

        cf, created = CustomField.objects.using(db_alias).get_or_create(
            name="librenms_id",
            defaults={
                "type": "json",
                "label": "LibreNMS ID",
                "description": "LibreNMS Device ID for synchronization (auto-created by plugin)",
                "required": False,
                "ui_visible": "if-set",
                "ui_editable": "yes",
                "is_cloneable": False,
            },
        )

        # Migrate legacy integer-typed field to JSON so the multi-server
        # dict format {"server_key": device_id} is accepted by the UI/API.
        if not created and cf.type == "integer":
            cf.type = "json"
            cf.save(using=db_alias, update_fields=["type"])
            logging.getLogger("netbox_librenms_plugin").info(
                "Migrated 'librenms_id' custom field type from integer to json"
            )

        # Ensure the field is assigned to the required object types
        from dcim.models import Device, Interface
        from virtualization.models import VirtualMachine, VMInterface

        required_models = [Device, VirtualMachine, Interface, VMInterface]
        current_types = set(cf.object_types.values_list("pk", flat=True))

        for model in required_models:
            ct = ContentType.objects.db_manager(db_alias).get_for_model(model)
            if ct.pk not in current_types:
                cf.object_types.add(ct)

        if created:
            logging.getLogger("netbox_librenms_plugin").info(
                "Auto-created 'librenms_id' custom field for Device, VirtualMachine, Interface, VMInterface"
            )

        # Mark this alias as executed after successful completion to allow retry on failure.
        executed_aliases.add(db_alias)
        _ensure_librenms_id_custom_field._executed_aliases = executed_aliases
    except Exception as e:
        # Don't break startup if custom field creation fails (e.g., during initial migration),
        # but log the error so it's not silently swallowed.
        logging.getLogger("netbox_librenms_plugin").exception("Failed to auto-create 'librenms_id' custom field: %s", e)


config = LibreNMSSyncConfig
