from dcim.models import DeviceRole, DeviceType, Manufacturer, Site, Location, Region, SiteGroup, Rack
from extras.choices import CustomFieldTypeChoices
from extras.models import CustomField, CustomFieldChoiceSet, ConfigTemplate
from extras.models.tags import Tag
from core.models import ObjectType
from django.db.models import Q, Transform, CharField, TextField

from .. import get_config
from .device import SlurpitImportedDevice, SlurpitStagedDevice
from .planning import SlurpitPlanning, SlurpitSnapshot
from .setting import SlurpitSetting
from .mapping import SlurpitMapping
from .ipam import SlurpitInitIPAddress
from .interface import SlurpitInterface
from .prefix import SlurpitPrefix
__all__ = [
    'SlurpitImportedDevice', 'SlurpitStagedDevice', 'SlurpitInitIPAddress',
    'post_migration', 'SlurpitSetting'
]

def ensure_slurpit_tags(*items):
    """Ensure Slurp'it tag exists and apply it to items.
    
    Creates the 'slurpit' tag if it doesn't exist, configures it for applicable
    object types (devices, prefixes, etc.), and applies it to the provided items.
    Uses caching to avoid repeated database queries.
    
    Args:
        *items: Variable number of model instances to tag
    
    Returns:
        Set containing the Slurp'it tag
    """
    if (tags := getattr(ensure_slurpit_tags, 'cache', None)) is None:
        tag, _ = Tag.objects.get_or_create(name='slurpit', slug='slurpit', defaults={'description':'Slurp\'it onboarded', 'color': 'F09640'})

        dcim_applicable_to = 'device', 'devicerole', 'devicetype', 'manufacturer', 'site'
        ipam_applicable_to = 'iprange', 'prefix'
        slurpit_netbox_applicable_to = 'slurpitinitipaddress', 'slurpitinterface', 'slurpitprefix'

        dcim_Q = Q(app_label='dcim', model__in=dcim_applicable_to)
        ipam_Q = Q(app_label='ipam', model__in=ipam_applicable_to)
        slurpit_Q = Q(app_label='slurpit_netbox', model__in=slurpit_netbox_applicable_to)

        tagged_types = ObjectType.objects.filter(ipam_Q | dcim_Q | slurpit_Q)
        tag.object_types.set(tagged_types.all())
        tags = {tag}
        ensure_slurpit_tags.cache = tags
    for item in items:
        item.tags.set(tags)
    return tags

def create_custom_fields():
    """Create custom fields on Device model for Slurp'it metadata.
    
    Creates the following custom fields if they don't exist:
    - slurpit_hostname
    - slurpit_fqdn
    - slurpit_platform
    - slurpit_manufacturer
    - slurpit_devicetype
    - slurpit_ipv4
    - slurpit_site
    - slurpit_serial
    - slurpit_os_version
    - slurpit_snmp_uptime
    
    All fields are text type and grouped under "Slurp'it".
    """   
    device = ObjectType.objects.get(app_label='dcim', model='device')
    cf, _ = CustomField.objects.get_or_create(
                name='slurpit_hostname',   
                defaults={            
                    "type":CustomFieldTypeChoices.TYPE_TEXT,
                    "description":"",
                    "is_cloneable":True,
                    "label":'Hostname',
                    "group_name":"Slurp'it"
                })
    cf.object_types.set({device})

    cf, _ = CustomField.objects.get_or_create(
                name='slurpit_fqdn',  
                defaults={                     
                    "type":CustomFieldTypeChoices.TYPE_TEXT,
                    "description":"",
                    "is_cloneable":True,
                    "label":'Fqdn',
                    "group_name":"Slurp'it"
                })
    cf.object_types.set({device})
        
    cf, _ = CustomField.objects.get_or_create(
                name='slurpit_platform',
                defaults={            
                    "type":CustomFieldTypeChoices.TYPE_TEXT,
                    "description":"",
                    "is_cloneable":True,
                    "label":'Platform',
                    "group_name":"Slurp'it",
                })
    cf.object_types.set({device})

    cf, _ = CustomField.objects.get_or_create(
                name='slurpit_manufacturer', 
                defaults={            
                    "type":CustomFieldTypeChoices.TYPE_TEXT,
                    "description":"",
                    "is_cloneable":True,
                    "label":'Manufacturer',
                    "group_name":"Slurp'it",
                })
    cf.object_types.set({device})
    
    cf, _ = CustomField.objects.get_or_create(
                name='slurpit_devicetype',
                defaults={            
                    "type":CustomFieldTypeChoices.TYPE_TEXT,
                    "description":"",
                    "is_cloneable":True,
                    "label":'Device Type',
                    "group_name":"Slurp'it",
                })
    cf.object_types.set({device})
    
    cf, _ = CustomField.objects.get_or_create(
                name='slurpit_ipv4',
                defaults={            
                    "type":CustomFieldTypeChoices.TYPE_TEXT,
                    "description":"",
                    "is_cloneable":True,
                    "label":'Ipv4',
                    "group_name":"Slurp'it",
                })
    cf.object_types.set({device})

    cf, _ = CustomField.objects.get_or_create(
                name='slurpit_site',
                defaults={            
                    "type":CustomFieldTypeChoices.TYPE_TEXT,
                    "description":"",
                    "is_cloneable":True,
                    "label":'Site',
                    "group_name":"Slurp'it",
                })
    cf.object_types.set({device})

    cf, _ = CustomField.objects.get_or_create(
                name='slurpit_serial',
                defaults={            
                    "type":CustomFieldTypeChoices.TYPE_TEXT,
                    "description":"",
                    "is_cloneable":True,
                    "label":'Serialname',
                    "group_name":"Slurp'it",
                })
    cf.object_types.set({device})

    cf, _ = CustomField.objects.get_or_create(
                name='slurpit_os_version',
                defaults={            
                    "type":CustomFieldTypeChoices.TYPE_TEXT,
                    "description":"",
                    "is_cloneable":True,
                    "label":'Os Version',
                    "group_name":"Slurp'it",
                })
    cf.object_types.set({device})

    cf, _ = CustomField.objects.get_or_create(
                name='slurpit_snmp_uptime',
                defaults={            
                    "type":CustomFieldTypeChoices.TYPE_TEXT,
                    "description":"",
                    "is_cloneable":True,
                    "label":'Snmp Uptime',
                    "group_name":"Slurp'it",
                })
    cf.object_types.set({device})

def create_default_data_mapping():
    """Create default field mappings between Slurp'it and NetBox.
    
    Deletes all existing mappings and creates default mappings:
    - hostname -> device|name
    - fqdn -> device|primary_ip4
    - ipv4 -> device|primary_ip4
    - device_os -> device|platform
    - device_type -> device|device_type
    """
    SlurpitMapping.objects.all().delete()
    
    mappings = [
        {"source_field": "hostname", "target_field": "device|name"},
        {"source_field": "fqdn", "target_field": "device|primary_ip4"},
        {"source_field": "ipv4", "target_field": "device|primary_ip4"},
        {"source_field": "device_os", "target_field": "device|platform"},
        {"source_field": "device_type", "target_field": "device|device_type"},
    ]
    for mapping in mappings:
        SlurpitMapping.objects.get_or_create(**mapping)

def add_default_mandatory_objects(tags):
    """Create default Site, Manufacturer, DeviceType, and DeviceRole objects.
    
    Creates mandatory NetBox objects required for device onboarding using
    plugin configuration. Applies Slurp'it tags to all created objects.
    Also creates default data mappings.
    
    Args:
        tags: Set of Tag objects to apply to created objects
    """
    site, _ = Site.objects.get_or_create(**get_config('Site'))
    site.tags.set(tags)

    manu, _ = Manufacturer.objects.get_or_create(**get_config('Manufacturer'))
    manu.tags.set(tags)

    dtype, _ = DeviceType.objects.get_or_create(manufacturer=manu, **get_config('DeviceType'))
    dtype.tags.set(tags)

    role, _ = DeviceRole.objects.get_or_create(**get_config('DeviceRole'))
    role.tags.set(tags)    

    create_default_data_mapping()


def post_migration(sender, **kwargs):
    """Post-migration hook to initialize plugin data.
    
    Called after database migrations complete. Creates custom fields, ensures
    Slurp'it tags exist, and creates default mandatory objects.
    
    Args:
        sender: App config that sent the signal
        **kwargs: Additional signal arguments
    """
    create_custom_fields()
    tags = ensure_slurpit_tags()
    add_default_mandatory_objects(tags)

class LowerCase(Transform):
    """Django database transform for case-insensitive lookups.
    
    Enables 'lower' lookup on CharField and TextField for case-insensitive
    queries (e.g., Model.objects.filter(name__lower='value')).
    """
    lookup_name = "lower"
    function = "LOWER"

CharField.register_lookup(LowerCase)
TextField.register_lookup(LowerCase)