"""
Views for device onboarding functionality.

This module handles the onboarding process for Slurpit devices into NetBox, including:
- Listing imported devices with different states (to onboard, onboarded, conflicted, migrated)
- Bulk onboarding operations
- Conflict resolution
- Migration of existing devices
- Importing devices from Slurpit server
"""

import requests

from django.contrib import messages
from django.core.exceptions import ValidationError, ObjectDoesNotExist
from django.db import transaction, connection
from django.db.models import F, Q, Func
from django.db.models.fields.json import KeyTextTransform
from django.utils.decorators import method_decorator
from django.http import JsonResponse
from django.shortcuts import redirect, render
from django.urls import reverse
from django.views import View
from django.http import HttpResponseRedirect
from django.utils.text import slugify

from .. import forms, importer, models, tables
from ..models import SlurpitImportedDevice, SlurpitSetting
from ..management.choices import *
from ..importer import get_dcim_device, import_from_queryset, run_import, get_devices, BATCH_SIZE, import_devices, process_import, start_device_import, sync_sites, get_ip_device
from ..decorators import slurpit_plugin_registered
from ..references import base_name, custom_field_data_name
from ..references.generic import (
    create_form, get_form_device_data, SlurpitViewMixim, get_default_objects, 
    set_device_custom_fields, get_create_dcim_objects, status_active,
    get_management_ip, get_device_custom_fields_dict, update_device_site, 
    assign_device_platform, assign_primary_ip_address
)
from ..references.imports import * 
from ..filtersets import SlurpitImportedDeviceFilterSet
from dcim.models import DeviceType, Interface
from ipam.models import IPAddress, Prefix
from django.db.models.functions import Cast


class TrimCIDR(Func):
    """Custom database function to extract IP address from CIDR notation.
    
    Removes the '/XX' suffix from IP addresses in CIDR format (e.g., '192.168.1.1/24' -> '192.168.1.1').
    Used for comparing IP addresses without network masks.
    
    Example:
        TrimCIDR('192.168.1.1/24') -> '192.168.1.1'
    """
    function = 'substring'
    template = "%(function)s(%(expressions)s FROM 1 FOR POSITION('/' IN %(expressions)s) - 1)"


@method_decorator(slurpit_plugin_registered, name='dispatch')
class SlurpitImportedDeviceListView(SlurpitViewMixim, generic.ObjectListView):
    """List view for imported Slurpit devices with different states.
    
    This view displays devices in different states:
    - To Onboard: Devices ready to be onboarded (not yet mapped to NetBox devices)
    - Onboarded: Devices that have been successfully onboarded
    - Conflicted: Devices that have conflicts (name or IP already exists in NetBox)
    - Migrate: Devices that need migration (mapped but have differences with Slurpit data)
    
    The view switches between different querysets and tables based on the 'tab' query parameter.
    """
    
    # Queryset for devices with conflicts (name or IP already exists in NetBox)
    conflicted_queryset = models.SlurpitImportedDevice.objects.filter(
        mapped_device_id__isnull=True
    ).filter(
        # Conflict by hostname (case-insensitive match)
        Q(hostname__lower__in=Device.objects.values('name__lower')) |
        # Conflict by IP address (extract IP from CIDR and compare)
        Q(ipv4__in=Device.objects.annotate(
            primary_ip4_trimmed=TrimCIDR(Cast(F('primary_ip4__address'), output_field=models.CharField()))
        ).filter(
           Q(primary_ip4__address__regex=r'/32$')  # Only /32 addresses (single host)
        ).values('primary_ip4_trimmed')) |
        # Conflict by serial number (ignore blank serials in both sides)
        Q(
            ~Q(serial='') & ~Q(serial__isnull=True) & 
            Q(serial__in=Device.objects.exclude(Q(serial='') | Q(serial__isnull=True)).values('serial'))
        )
    )
    
    # Queryset for devices ready to onboard (no conflicts, not yet mapped)
    to_onboard_queryset = models.SlurpitImportedDevice.objects.filter(
        mapped_device_id__isnull=True
    ).exclude(pk__in=conflicted_queryset.values('pk'))
    
    # Queryset for devices that have been successfully onboarded
    onboarded_queryset = models.SlurpitImportedDevice.objects.filter(
        mapped_device_id__isnull=False
    )
    
    # Queryset for devices that need migration (mapped but data differs from Slurpit)
    migrate_queryset = models.SlurpitImportedDevice.objects.filter(
        mapped_device_id__isnull=False
    ).annotate(
        # Extract Slurpit custom field values from mapped device
        slurpit_devicetype=KeyTextTransform('slurpit_devicetype', 'mapped_device__' + custom_field_data_name),
        slurpit_hostname=KeyTextTransform('slurpit_hostname', 'mapped_device__' + custom_field_data_name),
        slurpit_fqdn=KeyTextTransform('slurpit_fqdn', 'mapped_device__' + custom_field_data_name),
        slurpit_platform=KeyTextTransform('slurpit_platform', 'mapped_device__' + custom_field_data_name),
        slurpit_manufacturer=KeyTextTransform('slurpit_manufacturer', 'mapped_device__' + custom_field_data_name),
        slurpit_ipv4=KeyTextTransform('slurpit_ipv4', 'mapped_device__' + custom_field_data_name),
        slurpit_site=KeyTextTransform('slurpit_site', 'mapped_device__' + custom_field_data_name),
        # Create aliases for current imported device fields
        fdevicetype=F('device_type'),
        fhostname=F('hostname'),
        ffqdn=F('fqdn'),
        fipv4=F('ipv4'),
        fdeviceos=F('device_os'),
        fbrand=F('brand'),
        fsite=F('site')
    ).exclude(
        # Exclude devices where all fields match (no migration needed)
        Q(slurpit_devicetype=F('fdevicetype')) & 
        Q(slurpit_hostname=F('fhostname')) & 
        Q(slurpit_fqdn=F('ffqdn')) & 
        Q(slurpit_platform=F('fdeviceos')) & 
        Q(slurpit_manufacturer=F('fbrand')) &
        Q(slurpit_ipv4=F('fipv4')) &
        Q(slurpit_site=F('fsite'))
    )
    
    queryset = to_onboard_queryset
    action_buttons = []
    table = tables.SlurpitImportedDeviceTable
    template_name = f"{base_name}/onboard_device.html"
    filterset = SlurpitImportedDeviceFilterSet
    
    def get(self, request, *args, **kwargs):
        """Handle GET requests and switch queryset/table based on tab parameter.
        
        Args:
            request: HTTP request object
            *args: Variable positional arguments
            **kwargs: Variable keyword arguments
        
        Returns:
            Rendered template with appropriate queryset and table
        """
        self.queryset = self.to_onboard_queryset

        # Switch queryset and table based on tab parameter
        if request.GET.get('tab') == "migrate":
            self.queryset = self.migrate_queryset
            self.table = tables.MigratedDeviceTable
        elif request.GET.get('tab') == "conflicted":
            self.queryset = self.conflicted_queryset
            self.table = tables.ConflictDeviceTable
        elif request.GET.get('tab') == "onboarded":
            self.queryset = self.onboarded_queryset
            self.table = tables.SlurpitOnboardedDeviceTable
            
        return super().get(request, *args, **kwargs)
    
    def post(self, request):
        """Handle POST requests for bulk importing devices.
        
        Args:
            request: HTTP request object containing selected device PKs
        
        Returns:
            Redirect to the same page after import
        """
        if request.POST.get('_all'):
            # Import all devices in current queryset
            qs = self.queryset
        else:
            # Import only selected devices
            pks = map(int, request.POST.getlist('pk'))
            qs = self.queryset.filter(pk__in=pks, mapped_device_id__isnull=True)
        import_from_queryset(qs)
        return redirect(request.path)

    def slurpit_extra_context(self):
        """Add extra context data for the template.
        
        Returns:
            Dictionary containing:
            - Counts for each device state (to_onboard, onboarded, migrate, conflicted)
            - Appliance type and connection status from settings
            - Additional slurpit_data from mixin
        """
        appliance_type = ''
        connection_status = ''
        try:
            setting = SlurpitSetting.objects.get()
            server_url = setting.server_url
            api_key = setting.api_key
            appliance_type = setting.appliance_type
            connection_status = setting.connection_status
        except ObjectDoesNotExist:
            setting = None

        return {
            'to_onboard_count': self.to_onboard_queryset.count(),
            'onboarded_count': self.onboarded_queryset.count(),
            'migrate_count': self.migrate_queryset.count(),
            'conflicted_count': self.conflicted_queryset.count(),
            'appliance_type': appliance_type,
            'connection_status': connection_status,
            **self.slurpit_data
        }


@method_decorator(slurpit_plugin_registered, name='dispatch')
class SlurpitImportedDeviceOnboardView(SlurpitViewMixim, generic.BulkEditView):
    """Bulk edit view for onboarding Slurpit devices into NetBox.
    
    This view handles:
    - Bulk onboarding of devices with form-based configuration
    - Removing onboarded devices
    - Migrating existing devices (create, update_slurpit, update_netbox)
    - Resolving conflicts (create, update_slurpit, update_netbox)
    
    The view supports multiple operations based on query parameters:
    - 'remove': Remove selected devices
    - 'migrate': Migrate devices (create/update_slurpit/update_netbox)
    - 'conflicted': Resolve conflicts (create/update_slurpit/update_netbox)
    - '_apply': Apply form changes for onboarding
    """
    template_name = f"{base_name}/bulk_edit.html"
    queryset = models.SlurpitImportedDevice.objects.all()
    table = tables.SlurpitImportedDeviceTable
    model_form = forms.OnboardingForm
    form = forms.OnboardingForm
    filterset = SlurpitImportedDeviceFilterSet

    def get_form_kwargs(self):
        """Get keyword arguments for form initialization.
        
        Returns:
            Dictionary with form kwargs including models_queryset
        """
        kwargs = super().get_form_kwargs()
        kwargs['models_queryset'] = self.queryset
        return kwargs

    def post(self, request, **kwargs):
        """Handle POST requests for various onboarding operations.
        
        Supports multiple operations:
        - Remove: Delete selected devices and optionally clean up custom fields
        - Migrate: Migrate devices (create new, update from Slurpit, or update NetBox)
        - Conflicted: Resolve conflicts (create new, update from Slurpit, or update NetBox)
        - _apply: Apply form-based onboarding
        
        Args:
            request: HTTP request object
            **kwargs: Variable keyword arguments
        
        Returns:
            Redirect or JSON response based on operation result
        """
        model = self.queryset.model

        # Determine which devices to process
        if request.POST.get('_all') and self.filterset is not None:
            # Process all filtered devices
            pk_list = self.filterset(request.GET, self.queryset.values_list('pk', flat=True), request=request).qs
            self.queryset = models.SlurpitImportedDevice.objects.all()
        else:
            # Process only selected devices
            pk_list = request.POST.getlist('pk')
            self.queryset = models.SlurpitImportedDevice.objects.filter(pk__in=pk_list)

        # Remove operation: Delete selected devices
        if 'remove' in request.GET:
            if len(pk_list) == 0:
                messages.warning(request, "No {} were selected.".format(model._meta.verbose_name_plural))
                log_message = "Failed to remove since no devices were selected."
                
            else:
                # If removing onboarded devices, clean up custom fields from mapped device
                if 'onboarded' in request.GET:
                    for onboarded_item in self.queryset:
                        cf = onboarded_item.mapped_device.custom_field_data
                        # Remove all Slurpit custom fields
                        cf.pop('slurpit_hostname', None)
                        cf.pop('slurpit_fqdn', None)
                        cf.pop('slurpit_platform', None)
                        cf.pop('slurpit_manufacturer', None)
                        cf.pop('slurpit_devicetype', None)
                        cf.pop('slurpit_ipv4', None)
                        cf.pop('slurpit_site', None)
                        onboarded_item.mapped_device.custom_field_data = cf
                        onboarded_item.mapped_device.save()
                # Delete the imported device records
                self.queryset.delete()
                msg = f'Removed {len(pk_list)} {model._meta.verbose_name_plural}'
                messages.success(self.request, msg)
            return redirect(self.get_return_url(request))

        # Get distinct device types and sites from selected devices
        device_types = list(self.queryset.values_list('device_type').distinct())
        sites = list(self.queryset.values_list('site').distinct())

        # Create form with initial data
        form = create_form(self.form, request.POST, models.SlurpitImportedDevice, {'pk': pk_list, 'device_types': device_types, 'sites': sites})
        restrict_form_fields(form, request.user)

        # Apply form changes for onboarding
        if '_apply' in request.POST:
            if form.is_valid():
                try:
                    with transaction.atomic():
                        updated_objects, status, error, obj_name = self._update_objects(form, request)
                        
                        if status == "fail":
                            msg = f'{error[0:-1]} at {obj_name}.'
                            messages.error(self.request, msg)
                            return redirect(self.get_return_url(request))
                        if updated_objects:
                            msg = f'Onboarded {len(updated_objects)} {model._meta.verbose_name_plural}'
                            messages.success(self.request, msg)

                    return redirect(self.get_return_url(request))

                except ValidationError as e:
                    messages.error(self.request, ", ".join(e.messages))
                    # clear_webhooks.send(sender=self)
                    return JsonResponse({"status": "error", "error": str(e)})
                except Exception as e:
                    messages.error(self.request, str(e))
                    form.add_error(None, str(e))
                    # clear_webhooks.send(sender=self)
                    return JsonResponse({"status": "Error", "error": str(e)})
            return JsonResponse({"status": "error", "error": "validation error"})  
        
        # Migration operations: Update devices based on Slurpit data
        elif 'migrate' in request.GET:
            migrate = request.GET.get('migrate')
            
            # Create: Remove mapping and delete NetBox device (start fresh)
            if migrate == 'create':                
                for obj in self.queryset:
                    device = obj.mapped_device
                    obj.mapped_device = None
                    obj.save()
                    # Delete device last to prevent cascade delete of imported device
                    device.delete()
            
            # Update Slurpit: Update NetBox device with current Slurpit data
            elif migrate == 'update_slurpit':
                for obj in self.queryset:
                    device = obj.mapped_device
                    device.name = obj.hostname
                    
                    if obj.serial:
                        device.serial = obj.serial
                    
                    # Update custom fields and site from Slurpit data
                    set_device_custom_fields(device, get_device_custom_fields_dict(obj))
                    update_device_site(device, obj.site)
                    device.save()

                msg = f'Migration is done successfully.'
                messages.success(self.request, msg)

                return redirect(self.get_return_url(request))
            
            # Update NetBox: Update NetBox device with all Slurpit data including device type and platform
            else:
                for obj in self.queryset:
                    device = obj.mapped_device
                    device.name = obj.hostname
                    
                    if obj.serial:
                        device.serial = obj.serial
                    
                    # Update all device attributes from Slurpit
                    set_device_custom_fields(device, get_device_custom_fields_dict(obj))
                    update_device_site(device, obj.site)
                    device.device_type = get_create_dcim_objects(obj)
                    assign_device_platform(device, obj.device_os)
                    device.save()
                    obj.save()
                    # Assign primary IP address
                    assign_primary_ip_address(device, get_management_ip(obj), get_ip_device)

                    
                msg = f'Migration is done successfully.'
                messages.success(self.request, msg)

                return redirect(self.get_return_url(request))

        # Conflict resolution operations: Resolve conflicts between Slurpit and NetBox devices
        elif 'conflicted' in request.GET:
            conflic = request.GET.get('conflicted')
            
            # Create: Delete conflicting NetBox devices (by name or IP) to make room for Slurpit devices
            if conflic == 'create':
                # Delete devices with matching hostnames (case-insensitive)
                Device.objects.filter(name__lower__in=self.queryset.values('hostname__lower')).delete()

                # Delete devices with matching IP addresses
                for ipv4 in self.queryset.values_list('ipv4', flat=True).distinct():
                    if ipv4 is None:
                        continue
                    Device.objects.filter(primary_ip4__address__net_host=ipv4).delete()
                
                # Delete devices with matching serial numbers (ignoring blanks)
                for serial in self.queryset.values_list('serial', flat=True).distinct():
                    if not serial:
                        continue
                    Device.objects.filter(serial=serial).delete()

            # Update Slurpit: Update existing NetBox device with Slurpit data
            elif conflic == 'update_slurpit':
                for obj in self.queryset:
                    # Find device by hostname first
                    device = Device.objects.filter(name__iexact=obj.hostname).first()
                    
                    # If not found by name, try finding by management IP
                    if device is None:
                        _mgmt_ip = get_management_ip(obj)
                        if _mgmt_ip:
                            device = Device.objects.filter(primary_ip4__address__net_host=_mgmt_ip).first()
                    
                    # If not found by name or IP, try finding by serial number (if not blank)
                    if device is None and obj.serial:
                        device = Device.objects.filter(serial=obj.serial).first()

                    if obj.serial:
                        device.serial = obj.serial
                    
                    # Update device with Slurpit data
                    set_device_custom_fields(device, get_device_custom_fields_dict(obj))
                    
                    # Remove any other imported device mapping to this device
                    other_imported_device = SlurpitImportedDevice.objects.filter(mapped_device=device).first()
                    if other_imported_device:
                        other_imported_device.delete()

                    # Map this imported device to the NetBox device
                    obj.mapped_device = device
                    update_device_site(device, obj.site)
                    device.save()
                    obj.save()

                    
                msg = f'Conflicts successfully resolved.'
                messages.success(self.request, msg)

                return redirect(self.get_return_url(request))
            
            # Update NetBox: Update NetBox device with all Slurpit data (full sync)
            else:
                for obj in self.queryset:
                    device = None
                    
                    # Try to find device by management IP first
                    _mgmt_ip = get_management_ip(obj)
                    if _mgmt_ip:
                        device = Device.objects.filter(primary_ip4__address__net_host=_mgmt_ip).first()

                    # If not found by IP, try finding by hostname
                    if device is None:
                        device = Device.objects.filter(name__iexact=obj.hostname).first()
                    
                    # If not found by IP or hostname, try finding by serial number (if not blank)
                    if device is None and obj.serial:
                        device = Device.objects.filter(serial=obj.serial).first()
                    
                    if device:
                        # If found by IP but hostname differs, update hostname and remove conflicting device
                        if device.name != obj.hostname:
                            other_device = Device.objects.filter(name__iexact=obj.hostname).first()
                            if other_device:
                                other_device.delete()
                            
                            device.name = obj.hostname
                    
                    if obj.serial:
                        device.serial = obj.serial
                    
                    # Update all device attributes from Slurpit
                    set_device_custom_fields(device, get_device_custom_fields_dict(obj))
                      
                    # Remove any other imported device mapping to this device
                    other_imported_device = SlurpitImportedDevice.objects.filter(mapped_device=device).first()
                    if other_imported_device:
                        other_imported_device.delete()

                    # Map this imported device to the NetBox device
                    obj.mapped_device = device    
                    device.device_type = get_create_dcim_objects(obj)
                    assign_device_platform(device, obj.device_os)
                    update_device_site(device, obj.site)
                    device.save()
                    obj.save()
                    # Assign primary IP address
                    assign_primary_ip_address(device, _mgmt_ip, get_ip_device)

                    
                msg = f'Conflicts successfully resolved.'
                messages.success(self.request, msg)

                return redirect(self.get_return_url(request))
        
        # Prepare initial form data with defaults
        initial_data = {'pk': pk_list, 'device_types': device_types, 'sites': sites}
        
        # Add default objects (device role, site, etc.) to initial data
        for k, v in get_default_objects().items():
            initial_data.setdefault(k, str(v.id))
        initial_data.setdefault('status', status_active())

        if request.POST.get('_all'):
            initial_data['_all'] = 'on'

        # Set device type in initial data
        # If multiple types, allow user to choose
        if len(device_types) > 1:
            initial_data['device_type'] = 'keep_original'
        # If single type, try to find matching DeviceType in NetBox
        elif len(device_types) == 1 and (dt := DeviceType.objects.filter(model__iexact=device_types[0][0]).first()):
            initial_data['device_type'] = dt.id
        
        # Set site in initial data
        # If multiple sites, allow user to choose
        if len(sites) > 1:
            initial_data['site'] = 'keep_original'
        # If single site, try to find matching Site in NetBox
        elif len(sites) == 1 and (site := Site.objects.filter(name=sites[0][0]).first()):
            initial_data['site'] = site.id

        # Create form with initial data
        form = create_form(self.form, None, models.SlurpitImportedDevice, initial_data)
        restrict_form_fields(form, request.user)
                
        # Retrieve objects being edited and create table
        table = self.table(self.queryset.filter(mapped_device_id__isnull=True), orderable=False)
        if not table.rows:
            messages.warning(request, "No {} were selected.".format(model._meta.verbose_name_plural))
            return redirect(self.get_return_url(request))

        return render(request, self.template_name, {
            'model': model,
            'form': form,
            'table': table,
            'obj_type_plural': self.queryset.model._meta.verbose_name_plural,
            'return_url': self.get_return_url(request),
            **self.slurpit_data
        })

    def _update_objects(self, form, request):
        """Update devices based on form data.
        
        Creates or updates NetBox devices from Slurpit imported devices using form data.
        Handles device type and site assignment, custom fields, tags, and error handling.
        
        Args:
            form: Validated form instance with device configuration
            request: HTTP request object
        
        Returns:
            Tuple containing:
            - updated_objects: List of successfully updated SlurpitImportedDevice objects
            - status: "success" or "fail"
            - error: Error message if status is "fail"
            - obj_name: Name of object that failed (if applicable)
        """
        # Get device type from form if specified
        device_type = None
        if form.cleaned_data['device_type'] != 'keep_original':
            device_type = DeviceType.objects.filter(id=form.cleaned_data['device_type']).first()

        # Get site from form if specified
        site = None
        if form.cleaned_data['site'] != 'keep_original':
            site = Site.objects.filter(id=form.cleaned_data['site']).first()

        updated_objects = []
        # Extract device data from form (status, role, etc.)
        data = get_form_device_data(form)

        # Get objects to process
        objs = self.queryset.filter(pk__in=form.cleaned_data['pk'])
        
        for obj in objs:
            # Skip if already mapped
            if obj.mapped_device_id is not None:
                continue

            # Determine device type to use
            dt = device_type
            if not device_type:
                # Use mapped device type if available, otherwise will be created
                dt = obj.mapped_devicetype
            
            # Determine site to use
            item_site = site
            if not item_site:
                if obj.site is None or obj.site == "":
                    # Use default site if no site specified
                    defaults = get_default_objects()
                    item_site = defaults['site']
                else:
                    # Get site by name from Slurpit data
                    item_site = Site.objects.get(name=obj.site)
            
            try:
                # Create or get NetBox device
                device = get_dcim_device(obj, device_type=dt, site=item_site, **data)
                obj.mapped_device = device
                obj.save()
                updated_objects.append(obj)
            except Exception as e:
                # Return error information
                return [], "fail", str(e), obj.hostname
            
            # Take a snapshot for change logging if supported
            if hasattr(device, 'snapshot'):
                device.snapshot()
            
            # Apply tag additions if specified
            if form.cleaned_data.get('add_tags', None):
                device.tags.add(*form.cleaned_data['add_tags'])
            
            # Apply tag removals if specified
            if form.cleaned_data.get('remove_tags', None):
                device.tags.remove(*form.cleaned_data['remove_tags'])

        return updated_objects, "success", "", ""


@method_decorator(slurpit_plugin_registered, name='dispatch')
class ImportDevices(View):
    """View for importing devices from Slurpit server.
    
    This view handles the asynchronous import process:
    1. Syncs sites from Slurpit
    2. Fetches devices in batches using offset pagination
    3. Imports devices into the database
    4. Processes the import queue
    
    The import is done in batches to handle large numbers of devices efficiently.
    """
    
    def get(self, request, *args, **kwargs):
        """Handle GET requests for device import.
        
        Supports two modes:
        - With offset: Import devices in batches (pagination)
        - Without offset: Process the import queue
        
        Args:
            request: HTTP request object with optional 'offset' query parameter
            *args: Variable positional arguments
            **kwargs: Variable keyword arguments
        
        Returns:
            JSON response with action status and offset (if applicable)
        """
        offset = request.GET.get("offset", None)
        try:
            if offset is not None:
                # Batch import mode: fetch and import devices in chunks
                offset = int(offset)
                
                # On first batch (offset=0), sync sites and start import
                if offset == 0:
                    sync_sites()
                    start_device_import()
                
                # Fetch devices from Slurpit API
                devices, log_message = get_devices(offset)
                
                # If devices were fetched, import them and update offset
                if devices is not None and len(devices) > 0:
                    import_devices(devices)
                    offset += len(devices)
                
                # If devices is None, there was an API error
                if devices is None:
                    messages.error(request, "Please confirm the Slurp'it server is running and reachable.")
                    return JsonResponse({"action": "error", "error": "ERROR"})
                
                # Return updated offset for next batch
                return JsonResponse({"action": "import", "offset": offset})
            
            # No offset: Process the import queue (final step)
            process_import()
            messages.info(request, "Synced the devices from Slurp'it.")
            return JsonResponse({"action": "process"})
            
        except requests.exceptions.RequestException as e:
            # Handle network/API errors
            messages.error(request, "An error occured during querying Slurp'it!")
            
        return JsonResponse({"action": "", "error": "ERROR"})
    

