from django.contrib.contenttypes.models import ContentType
from django.db.models import Prefetch
from django.db.models.expressions import RawSQL
from django.shortcuts import get_object_or_404, redirect, render
from django.urls import reverse
from django.utils.translation import gettext_lazy as _

from circuits.models import Provider
from dcim.filtersets import InterfaceFilterSet
from dcim.forms import InterfaceFilterForm
from dcim.models import Device, Interface, Site
from ipam.tables import VLANTranslationRuleTable
from netbox.object_actions import AddObject, BulkDelete, BulkEdit, BulkExport, BulkImport
from netbox.views import generic
from utilities.query import count_related
from utilities.tables import get_table_ordering
from utilities.views import GetRelatedModelsMixin, ViewTab, register_model_view
from virtualization.filtersets import VMInterfaceFilterSet
from virtualization.forms import VMInterfaceFilterForm
from virtualization.models import VirtualMachine, VMInterface

from . import filtersets, forms, tables
from .choices import PrefixStatusChoices
from .constants import *
from .models import *
from .utils import add_available_vlans, add_requested_prefixes, annotate_ip_space

#
# VRFs
#


@register_model_view(VRF, 'list', path='', detail=False)
class VRFListView(generic.ObjectListView):
    queryset = VRF.objects.all()
    filterset = filtersets.VRFFilterSet
    filterset_form = forms.VRFFilterForm
    table = tables.VRFTable


@register_model_view(VRF)
class VRFView(GetRelatedModelsMixin, generic.ObjectView):
    queryset = VRF.objects.all()

    def get_extra_context(self, request, instance):
        import_targets_table = tables.RouteTargetTable(
            instance.import_targets.all(),
            orderable=False
        )
        import_targets_table.configure(request)

        export_targets_table = tables.RouteTargetTable(
            instance.export_targets.all(),
            orderable=False
        )
        export_targets_table.configure(request)

        related_models = self.get_related_models(
            request,
            instance,
            omit=(Interface, VMInterface),
            extra=(
                (
                    Interface.objects.restrict(request.user, 'view').filter(vrf=instance),
                    'vrf_id',
                    _('Device Interfaces')
                ),
                (
                    VMInterface.objects.restrict(request.user, 'view').filter(vrf=instance),
                    'vrf_id',
                    _('VM Interfaces')
                ),
            ),
        )

        return {
            'related_models': related_models,
            'import_targets_table': import_targets_table,
            'export_targets_table': export_targets_table,
        }


@register_model_view(VRF, 'add', detail=False)
@register_model_view(VRF, 'edit')
class VRFEditView(generic.ObjectEditView):
    queryset = VRF.objects.all()
    form = forms.VRFForm


@register_model_view(VRF, 'delete')
class VRFDeleteView(generic.ObjectDeleteView):
    queryset = VRF.objects.all()


@register_model_view(VRF, 'bulk_import', path='import', detail=False)
class VRFBulkImportView(generic.BulkImportView):
    queryset = VRF.objects.all()
    model_form = forms.VRFImportForm


@register_model_view(VRF, 'bulk_edit', path='edit', detail=False)
class VRFBulkEditView(generic.BulkEditView):
    queryset = VRF.objects.all()
    filterset = filtersets.VRFFilterSet
    table = tables.VRFTable
    form = forms.VRFBulkEditForm


@register_model_view(VRF, 'bulk_rename', path='rename', detail=False)
class VRFBulkRenameView(generic.BulkRenameView):
    queryset = VRF.objects.all()
    filterset = filtersets.VRFFilterSet


@register_model_view(VRF, 'bulk_delete', path='delete', detail=False)
class VRFBulkDeleteView(generic.BulkDeleteView):
    queryset = VRF.objects.all()
    filterset = filtersets.VRFFilterSet
    table = tables.VRFTable


#
# Route targets
#

@register_model_view(RouteTarget, 'list', path='', detail=False)
class RouteTargetListView(generic.ObjectListView):
    queryset = RouteTarget.objects.all()
    filterset = filtersets.RouteTargetFilterSet
    filterset_form = forms.RouteTargetFilterForm
    table = tables.RouteTargetTable


@register_model_view(RouteTarget)
class RouteTargetView(generic.ObjectView):
    queryset = RouteTarget.objects.all()


@register_model_view(RouteTarget, 'add', detail=False)
@register_model_view(RouteTarget, 'edit')
class RouteTargetEditView(generic.ObjectEditView):
    queryset = RouteTarget.objects.all()
    form = forms.RouteTargetForm


@register_model_view(RouteTarget, 'delete')
class RouteTargetDeleteView(generic.ObjectDeleteView):
    queryset = RouteTarget.objects.all()


@register_model_view(RouteTarget, 'bulk_import', path='import', detail=False)
class RouteTargetBulkImportView(generic.BulkImportView):
    queryset = RouteTarget.objects.all()
    model_form = forms.RouteTargetImportForm


@register_model_view(RouteTarget, 'bulk_edit', path='edit', detail=False)
class RouteTargetBulkEditView(generic.BulkEditView):
    queryset = RouteTarget.objects.all()
    filterset = filtersets.RouteTargetFilterSet
    table = tables.RouteTargetTable
    form = forms.RouteTargetBulkEditForm


@register_model_view(RouteTarget, 'bulk_rename', path='rename', detail=False)
class RouteTargetBulkRenameView(generic.BulkRenameView):
    queryset = RouteTarget.objects.all()
    filterset = filtersets.RouteTargetFilterSet


@register_model_view(RouteTarget, 'bulk_delete', path='delete', detail=False)
class RouteTargetBulkDeleteView(generic.BulkDeleteView):
    queryset = RouteTarget.objects.all()
    filterset = filtersets.RouteTargetFilterSet
    table = tables.RouteTargetTable


#
# RIRs
#

@register_model_view(RIR, 'list', path='', detail=False)
class RIRListView(generic.ObjectListView):
    queryset = RIR.objects.annotate(
        aggregate_count=count_related(Aggregate, 'rir')
    )
    filterset = filtersets.RIRFilterSet
    filterset_form = forms.RIRFilterForm
    table = tables.RIRTable


@register_model_view(RIR)
class RIRView(GetRelatedModelsMixin, generic.ObjectView):
    queryset = RIR.objects.all()

    def get_extra_context(self, request, instance):
        return {
            'related_models': self.get_related_models(request, instance),
        }


@register_model_view(RIR, 'add', detail=False)
@register_model_view(RIR, 'edit')
class RIREditView(generic.ObjectEditView):
    queryset = RIR.objects.all()
    form = forms.RIRForm


@register_model_view(RIR, 'delete')
class RIRDeleteView(generic.ObjectDeleteView):
    queryset = RIR.objects.all()


@register_model_view(RIR, 'bulk_import', path='import', detail=False)
class RIRBulkImportView(generic.BulkImportView):
    queryset = RIR.objects.all()
    model_form = forms.RIRImportForm


@register_model_view(RIR, 'bulk_edit', path='edit', detail=False)
class RIRBulkEditView(generic.BulkEditView):
    queryset = RIR.objects.annotate(
        aggregate_count=count_related(Aggregate, 'rir')
    )
    filterset = filtersets.RIRFilterSet
    table = tables.RIRTable
    form = forms.RIRBulkEditForm


@register_model_view(RIR, 'bulk_rename', path='rename', detail=False)
class RIRBulkRenameView(generic.BulkRenameView):
    queryset = RIR.objects.all()
    filterset = filtersets.RIRFilterSet


@register_model_view(RIR, 'bulk_delete', path='delete', detail=False)
class RIRBulkDeleteView(generic.BulkDeleteView):
    queryset = RIR.objects.annotate(
        aggregate_count=count_related(Aggregate, 'rir')
    )
    filterset = filtersets.RIRFilterSet
    table = tables.RIRTable


#
# ASN ranges
#

@register_model_view(ASNRange, 'list', path='', detail=False)
class ASNRangeListView(generic.ObjectListView):
    queryset = ASNRange.objects.annotate_asn_counts()
    filterset = filtersets.ASNRangeFilterSet
    filterset_form = forms.ASNRangeFilterForm
    table = tables.ASNRangeTable


@register_model_view(ASNRange)
class ASNRangeView(generic.ObjectView):
    queryset = ASNRange.objects.all()


@register_model_view(ASNRange, 'asns')
class ASNRangeASNsView(generic.ObjectChildrenView):
    queryset = ASNRange.objects.all()
    child_model = ASN
    table = tables.ASNTable
    filterset = filtersets.ASNFilterSet
    filterset_form = forms.ASNFilterForm
    tab = ViewTab(
        label=_('ASNs'),
        badge=lambda x: x.get_child_asns().count(),
        permission='ipam.view_asn',
        weight=500
    )

    def get_children(self, request, parent):
        return parent.get_child_asns().restrict(request.user, 'view').annotate(
            site_count=count_related(Site, 'asns'),
            provider_count=count_related(Provider, 'asns')
        )


@register_model_view(ASNRange, 'add', detail=False)
@register_model_view(ASNRange, 'edit')
class ASNRangeEditView(generic.ObjectEditView):
    queryset = ASNRange.objects.all()
    form = forms.ASNRangeForm


@register_model_view(ASNRange, 'delete')
class ASNRangeDeleteView(generic.ObjectDeleteView):
    queryset = ASNRange.objects.all()


@register_model_view(ASNRange, 'bulk_import', path='import', detail=False)
class ASNRangeBulkImportView(generic.BulkImportView):
    queryset = ASNRange.objects.all()
    model_form = forms.ASNRangeImportForm


@register_model_view(ASNRange, 'bulk_edit', path='edit', detail=False)
class ASNRangeBulkEditView(generic.BulkEditView):
    queryset = ASNRange.objects.annotate_asn_counts()
    filterset = filtersets.ASNRangeFilterSet
    table = tables.ASNRangeTable
    form = forms.ASNRangeBulkEditForm


@register_model_view(ASNRange, 'bulk_rename', path='rename', detail=False)
class ASNRangeBulkRenameView(generic.BulkRenameView):
    queryset = ASNRange.objects.all()
    filterset = filtersets.ASNRangeFilterSet


@register_model_view(ASNRange, 'bulk_delete', path='delete', detail=False)
class ASNRangeBulkDeleteView(generic.BulkDeleteView):
    queryset = ASNRange.objects.annotate_asn_counts()
    filterset = filtersets.ASNRangeFilterSet
    table = tables.ASNRangeTable


#
# ASNs
#

@register_model_view(ASN, 'list', path='', detail=False)
class ASNListView(generic.ObjectListView):
    queryset = ASN.objects.annotate(
        site_count=count_related(Site, 'asns'),
        provider_count=count_related(Provider, 'asns')
    )
    filterset = filtersets.ASNFilterSet
    filterset_form = forms.ASNFilterForm
    table = tables.ASNTable


@register_model_view(ASN)
class ASNView(GetRelatedModelsMixin, generic.ObjectView):
    queryset = ASN.objects.all()

    def get_extra_context(self, request, instance):
        return {
            'related_models': self.get_related_models(
                request,
                instance,
                extra=(
                    (Site.objects.restrict(request.user, 'view').filter(asns__in=[instance]), 'asn_id'),
                    (Provider.objects.restrict(request.user, 'view').filter(asns__in=[instance]), 'asn_id'),
                ),
            ),
        }


@register_model_view(ASN, 'add', detail=False)
@register_model_view(ASN, 'edit')
class ASNEditView(generic.ObjectEditView):
    queryset = ASN.objects.all()
    form = forms.ASNForm


@register_model_view(ASN, 'delete')
class ASNDeleteView(generic.ObjectDeleteView):
    queryset = ASN.objects.all()


@register_model_view(ASN, 'bulk_import', path='import', detail=False)
class ASNBulkImportView(generic.BulkImportView):
    queryset = ASN.objects.all()
    model_form = forms.ASNImportForm


@register_model_view(ASN, 'bulk_edit', path='edit', detail=False)
class ASNBulkEditView(generic.BulkEditView):
    queryset = ASN.objects.annotate(
        site_count=count_related(Site, 'asns')
    )
    filterset = filtersets.ASNFilterSet
    table = tables.ASNTable
    form = forms.ASNBulkEditForm


@register_model_view(ASN, 'bulk_rename', path='rename', detail=False)
class ASNBulkRenameView(generic.BulkRenameView):
    queryset = ASN.objects.all()
    filterset = filtersets.ASNFilterSet


@register_model_view(ASN, 'bulk_delete', path='delete', detail=False)
class ASNBulkDeleteView(generic.BulkDeleteView):
    queryset = ASN.objects.annotate(
        site_count=count_related(Site, 'asns')
    )
    filterset = filtersets.ASNFilterSet
    table = tables.ASNTable


#
# Aggregates
#

@register_model_view(Aggregate, 'list', path='', detail=False)
class AggregateListView(generic.ObjectListView):
    queryset = Aggregate.objects.annotate(
        child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ())
    )
    filterset = filtersets.AggregateFilterSet
    filterset_form = forms.AggregateFilterForm
    table = tables.AggregateTable
    actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete)


@register_model_view(Aggregate)
class AggregateView(generic.ObjectView):
    queryset = Aggregate.objects.all()


@register_model_view(Aggregate, 'prefixes')
class AggregatePrefixesView(generic.ObjectChildrenView):
    queryset = Aggregate.objects.all()
    child_model = Prefix
    table = tables.PrefixTable
    filterset = filtersets.PrefixFilterSet
    filterset_form = forms.PrefixFilterForm
    template_name = 'ipam/aggregate/prefixes.html'
    tab = ViewTab(
        label=_('Prefixes'),
        badge=lambda x: x.get_child_prefixes().count(),
        permission='ipam.view_prefix',
        weight=500
    )

    def get_children(self, request, parent):
        return Prefix.objects.restrict(request.user, 'view').filter(
            prefix__net_contained_or_equal=str(parent.prefix)
        ).prefetch_related('scope', 'role', 'tenant', 'tenant__group', 'vlan')

    def prep_table_data(self, request, queryset, parent):
        # Determine whether to show assigned prefixes, available prefixes, or both
        show_available = bool(request.GET.get('show_available', 'true') == 'true')
        show_assigned = bool(request.GET.get('show_assigned', 'true') == 'true')

        return add_requested_prefixes(parent.prefix, queryset, show_available, show_assigned)

    def get_extra_context(self, request, instance):
        return {
            'bulk_querystring': f'within={instance.prefix}',
            'first_available_prefix': instance.get_first_available_prefix(),
            'show_available': bool(request.GET.get('show_available', 'true') == 'true'),
            'show_assigned': bool(request.GET.get('show_assigned', 'true') == 'true'),
        }


@register_model_view(Aggregate, 'add', detail=False)
@register_model_view(Aggregate, 'edit')
class AggregateEditView(generic.ObjectEditView):
    queryset = Aggregate.objects.all()
    form = forms.AggregateForm


@register_model_view(Aggregate, 'delete')
class AggregateDeleteView(generic.ObjectDeleteView):
    queryset = Aggregate.objects.all()


@register_model_view(Aggregate, 'bulk_import', path='import', detail=False)
class AggregateBulkImportView(generic.BulkImportView):
    queryset = Aggregate.objects.all()
    model_form = forms.AggregateImportForm


@register_model_view(Aggregate, 'bulk_edit', path='edit', detail=False)
class AggregateBulkEditView(generic.BulkEditView):
    queryset = Aggregate.objects.annotate(
        child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ())
    )
    filterset = filtersets.AggregateFilterSet
    table = tables.AggregateTable
    form = forms.AggregateBulkEditForm


@register_model_view(Aggregate, 'bulk_delete', path='delete', detail=False)
class AggregateBulkDeleteView(generic.BulkDeleteView):
    queryset = Aggregate.objects.annotate(
        child_count=RawSQL('SELECT COUNT(*) FROM ipam_prefix WHERE ipam_prefix.prefix <<= ipam_aggregate.prefix', ())
    )
    filterset = filtersets.AggregateFilterSet
    table = tables.AggregateTable


#
# Prefix/VLAN roles
#

@register_model_view(Role, 'list', path='', detail=False)
class RoleListView(generic.ObjectListView):
    queryset = Role.objects.annotate(
        prefix_count=count_related(Prefix, 'role'),
        iprange_count=count_related(IPRange, 'role'),
        vlan_count=count_related(VLAN, 'role'),
        asn_count=count_related(ASN, 'role')
    )
    filterset = filtersets.RoleFilterSet
    filterset_form = forms.RoleFilterForm
    table = tables.RoleTable


@register_model_view(Role)
class RoleView(GetRelatedModelsMixin, generic.ObjectView):
    queryset = Role.objects.all()

    def get_extra_context(self, request, instance):
        return {
            'related_models': self.get_related_models(request, instance),
        }


@register_model_view(Role, 'add', detail=False)
@register_model_view(Role, 'edit')
class RoleEditView(generic.ObjectEditView):
    queryset = Role.objects.all()
    form = forms.RoleForm


@register_model_view(Role, 'delete')
class RoleDeleteView(generic.ObjectDeleteView):
    queryset = Role.objects.all()


@register_model_view(Role, 'bulk_import', path='import', detail=False)
class RoleBulkImportView(generic.BulkImportView):
    queryset = Role.objects.all()
    model_form = forms.RoleImportForm


@register_model_view(Role, 'bulk_edit', path='edit', detail=False)
class RoleBulkEditView(generic.BulkEditView):
    queryset = Role.objects.all()
    filterset = filtersets.RoleFilterSet
    table = tables.RoleTable
    form = forms.RoleBulkEditForm


@register_model_view(Role, 'bulk_rename', path='rename', detail=False)
class RoleBulkRenameView(generic.BulkRenameView):
    queryset = Role.objects.all()
    filterset = filtersets.RoleFilterSet


@register_model_view(Role, 'bulk_delete', path='delete', detail=False)
class RoleBulkDeleteView(generic.BulkDeleteView):
    queryset = Role.objects.all()
    filterset = filtersets.RoleFilterSet
    table = tables.RoleTable


#
# Prefixes
#

@register_model_view(Prefix, 'list', path='', detail=False)
class PrefixListView(generic.ObjectListView):
    queryset = Prefix.objects.all()
    filterset = filtersets.PrefixFilterSet
    filterset_form = forms.PrefixFilterForm
    table = tables.PrefixTable
    template_name = 'ipam/prefix_list.html'
    actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete)


@register_model_view(Prefix)
class PrefixView(generic.ObjectView):
    queryset = Prefix.objects.all()

    def get_extra_context(self, request, instance):
        try:
            aggregate = Aggregate.objects.restrict(request.user, 'view').get(
                prefix__net_contains_or_equals=str(instance.prefix)
            )
        except Aggregate.DoesNotExist:
            aggregate = None

        # Parent prefixes table
        parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
            Q(vrf=instance.vrf) | Q(vrf__isnull=True, status=PrefixStatusChoices.STATUS_CONTAINER)
        ).filter(
            prefix__net_contains=str(instance.prefix)
        ).prefetch_related(
            'scope', 'role', 'tenant', 'vlan',
        )
        parent_prefix_table = tables.PrefixTable(
            list(parent_prefixes),
            exclude=('vrf', 'utilization'),
            orderable=False
        )
        parent_prefix_table.configure(request)

        # Duplicate prefixes table
        duplicate_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
            vrf=instance.vrf, prefix=str(instance.prefix)
        ).exclude(
            pk=instance.pk
        ).prefetch_related(
            'scope', 'role', 'tenant', 'vlan',
        )
        duplicate_prefix_table = tables.PrefixTable(
            list(duplicate_prefixes),
            exclude=('vrf', 'utilization'),
            orderable=False
        )
        duplicate_prefix_table.configure(request)

        return {
            'aggregate': aggregate,
            'parent_prefix_table': parent_prefix_table,
            'duplicate_prefix_table': duplicate_prefix_table,
        }


@register_model_view(Prefix, 'prefixes')
class PrefixPrefixesView(generic.ObjectChildrenView):
    queryset = Prefix.objects.all()
    child_model = Prefix
    table = tables.PrefixTable
    filterset = filtersets.PrefixFilterSet
    filterset_form = forms.PrefixFilterForm
    template_name = 'ipam/prefix/prefixes.html'
    tab = ViewTab(
        label=_('Child Prefixes'),
        badge=lambda x: x.get_child_prefixes().count(),
        permission='ipam.view_prefix',
        weight=500
    )

    def get_children(self, request, parent):
        return parent.get_child_prefixes().restrict(request.user, 'view').prefetch_related(
            'scope', 'vrf', 'vlan', 'role', 'tenant', 'tenant__group'
        )

    def prep_table_data(self, request, queryset, parent):
        # Determine whether to show assigned prefixes, available prefixes, or both
        show_available = bool(request.GET.get('show_available', 'true') == 'true')
        show_assigned = bool(request.GET.get('show_assigned', 'true') == 'true')

        return add_requested_prefixes(parent.prefix, queryset, show_available, show_assigned)

    def get_extra_context(self, request, instance):
        return {
            'bulk_querystring': f"vrf_id={instance.vrf.pk if instance.vrf else '0'}&within={instance.prefix}",
            'first_available_prefix': instance.get_first_available_prefix(),
            'show_available': bool(request.GET.get('show_available', 'true') == 'true'),
            'show_assigned': bool(request.GET.get('show_assigned', 'true') == 'true'),
        }


@register_model_view(Prefix, 'ipranges', path='ip-ranges')
class PrefixIPRangesView(generic.ObjectChildrenView):
    queryset = Prefix.objects.all()
    child_model = IPRange
    table = tables.IPRangeTable
    filterset = filtersets.IPRangeFilterSet
    filterset_form = forms.IPRangeFilterForm
    template_name = 'ipam/prefix/ip_ranges.html'
    tab = ViewTab(
        label=_('Child Ranges'),
        badge=lambda x: x.get_child_ranges().count(),
        permission='ipam.view_iprange',
        weight=600
    )

    def get_children(self, request, parent):
        return parent.get_child_ranges().restrict(request.user, 'view').prefetch_related(
            'tenant__group',
        )

    def get_extra_context(self, request, instance):
        return {
            'bulk_querystring': f"vrf_id={instance.vrf.pk if instance.vrf else '0'}&parent={instance.prefix}",
            'first_available_ip': instance.get_first_available_ip(),
        }


@register_model_view(Prefix, 'ipaddresses', path='ip-addresses')
class PrefixIPAddressesView(generic.ObjectChildrenView):
    queryset = Prefix.objects.all()
    child_model = IPAddress
    table = tables.AnnotatedIPAddressTable
    filterset = filtersets.IPAddressFilterSet
    filterset_form = forms.IPAddressFilterForm
    template_name = 'ipam/prefix/ip_addresses.html'
    tab = ViewTab(
        label=_('IP Addresses'),
        badge=lambda x: x.get_child_ips().count(),
        permission='ipam.view_ipaddress',
        weight=700
    )

    def get_children(self, request, parent):
        return parent.get_child_ips().restrict(request.user, 'view').prefetch_related('vrf', 'tenant', 'tenant__group')

    def prep_table_data(self, request, queryset, parent):
        if not request.GET.get('q') and not get_table_ordering(request, self.table):
            return annotate_ip_space(parent)
        return queryset

    def get_extra_context(self, request, instance):
        return {
            'bulk_querystring': f"vrf_id={instance.vrf.pk if instance.vrf else '0'}&parent={instance.prefix}",
            'first_available_ip': instance.get_first_available_ip(),
        }


@register_model_view(Prefix, 'add', detail=False)
@register_model_view(Prefix, 'edit')
class PrefixEditView(generic.ObjectEditView):
    queryset = Prefix.objects.all()
    form = forms.PrefixForm
    template_name = 'ipam/prefix_edit.html'


@register_model_view(Prefix, 'delete')
class PrefixDeleteView(generic.ObjectDeleteView):
    queryset = Prefix.objects.all()


@register_model_view(Prefix, 'bulk_add', path='bulk-add', detail=False)
class PrefixBulkCreateView(generic.BulkCreateView):
    queryset = Prefix.objects.all()
    form = forms.IPNetworkBulkCreateForm
    model_form = forms.PrefixBulkAddForm
    pattern_target = 'prefix'
    template_name = 'ipam/prefix_bulk_add.html'


@register_model_view(Prefix, 'bulk_import', path='import', detail=False)
class PrefixBulkImportView(generic.BulkImportView):
    queryset = Prefix.objects.all()
    model_form = forms.PrefixImportForm


@register_model_view(Prefix, 'bulk_edit', path='edit', detail=False)
class PrefixBulkEditView(generic.BulkEditView):
    queryset = Prefix.objects.prefetch_related('vrf__tenant')
    filterset = filtersets.PrefixFilterSet
    table = tables.PrefixTable
    form = forms.PrefixBulkEditForm


@register_model_view(Prefix, 'bulk_delete', path='delete', detail=False)
class PrefixBulkDeleteView(generic.BulkDeleteView):
    queryset = Prefix.objects.prefetch_related('vrf__tenant')
    filterset = filtersets.PrefixFilterSet
    table = tables.PrefixTable


#
# IP Ranges
#

@register_model_view(IPRange, 'list', path='', detail=False)
class IPRangeListView(generic.ObjectListView):
    queryset = IPRange.objects.all()
    filterset = filtersets.IPRangeFilterSet
    filterset_form = forms.IPRangeFilterForm
    table = tables.IPRangeTable


@register_model_view(IPRange)
class IPRangeView(generic.ObjectView):
    queryset = IPRange.objects.all()

    def get_extra_context(self, request, instance):

        # Parent prefixes table
        parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
            Q(prefix__net_contains_or_equals=str(instance.start_address.ip)),
            Q(prefix__net_contains_or_equals=str(instance.end_address.ip)),
            vrf=instance.vrf
        ).prefetch_related(
            'scope', 'role', 'tenant', 'vlan', 'role'
        )
        parent_prefixes_table = tables.PrefixTable(
            list(parent_prefixes),
            exclude=('vrf', 'utilization'),
            orderable=False
        )
        parent_prefixes_table.configure(request)

        return {
            'parent_prefixes_table': parent_prefixes_table,
        }


@register_model_view(IPRange, 'ipaddresses', path='ip-addresses')
class IPRangeIPAddressesView(generic.ObjectChildrenView):
    queryset = IPRange.objects.all()
    child_model = IPAddress
    table = tables.IPAddressTable
    filterset = filtersets.IPAddressFilterSet
    filterset_form = forms.IPRangeFilterForm
    template_name = 'ipam/iprange/ip_addresses.html'
    tab = ViewTab(
        label=_('IP Addresses'),
        badge=lambda x: x.get_child_ips().count(),
        permission='ipam.view_ipaddress',
        weight=500
    )

    def get_children(self, request, parent):
        return parent.get_child_ips().restrict(request.user, 'view')


@register_model_view(IPRange, 'add', detail=False)
@register_model_view(IPRange, 'edit')
class IPRangeEditView(generic.ObjectEditView):
    queryset = IPRange.objects.all()
    form = forms.IPRangeForm


@register_model_view(IPRange, 'delete')
class IPRangeDeleteView(generic.ObjectDeleteView):
    queryset = IPRange.objects.all()


@register_model_view(IPRange, 'bulk_import', path='import', detail=False)
class IPRangeBulkImportView(generic.BulkImportView):
    queryset = IPRange.objects.all()
    model_form = forms.IPRangeImportForm


@register_model_view(IPRange, 'bulk_edit', path='edit', detail=False)
class IPRangeBulkEditView(generic.BulkEditView):
    queryset = IPRange.objects.all()
    filterset = filtersets.IPRangeFilterSet
    table = tables.IPRangeTable
    form = forms.IPRangeBulkEditForm


@register_model_view(IPRange, 'bulk_rename', path='rename', detail=False)
class IPRangeBulkRenameView(generic.BulkRenameView):
    queryset = IPRange.objects.all()
    filterset = filtersets.IPRangeFilterSet


@register_model_view(IPRange, 'bulk_delete', path='delete', detail=False)
class IPRangeBulkDeleteView(generic.BulkDeleteView):
    queryset = IPRange.objects.all()
    filterset = filtersets.IPRangeFilterSet
    table = tables.IPRangeTable


#
# IP addresses
#

@register_model_view(IPAddress, 'list', path='', detail=False)
class IPAddressListView(generic.ObjectListView):
    queryset = IPAddress.objects.all()
    filterset = filtersets.IPAddressFilterSet
    filterset_form = forms.IPAddressFilterForm
    table = tables.IPAddressTable
    actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete)


@register_model_view(IPAddress)
class IPAddressView(generic.ObjectView):
    queryset = IPAddress.objects.prefetch_related('vrf__tenant', 'tenant')

    def get_extra_context(self, request, instance):
        # Parent prefixes table
        parent_prefixes = Prefix.objects.restrict(request.user, 'view').filter(
            vrf=instance.vrf,
            prefix__net_contains_or_equals=str(instance.address.ip)
        ).prefetch_related(
            'scope', 'role'
        )
        parent_prefixes_table = tables.PrefixTable(
            list(parent_prefixes),
            exclude=('vrf', 'utilization'),
            orderable=False
        )
        parent_prefixes_table.configure(request)

        # Duplicate IPs table
        duplicate_ips = IPAddress.objects.restrict(request.user, 'view').filter(
            vrf=instance.vrf,
            address=str(instance.address)
        ).exclude(
            pk=instance.pk
        ).prefetch_related(
            'nat_inside'
        )
        # Exclude anycast IPs if this IP is anycast
        if instance.role == IPAddressRoleChoices.ROLE_ANYCAST:
            duplicate_ips = duplicate_ips.exclude(role=IPAddressRoleChoices.ROLE_ANYCAST)
        # Limit to a maximum of 10 duplicates displayed here
        duplicate_ips_table = tables.IPAddressTable(duplicate_ips[:10], orderable=False)
        duplicate_ips_table.configure(request)

        return {
            'parent_prefixes_table': parent_prefixes_table,
            'duplicate_ips_table': duplicate_ips_table,
        }


@register_model_view(IPAddress, 'add', detail=False)
@register_model_view(IPAddress, 'edit')
class IPAddressEditView(generic.ObjectEditView):
    queryset = IPAddress.objects.all()
    form = forms.IPAddressForm
    template_name = 'ipam/ipaddress_edit.html'

    def alter_object(self, obj, request, url_args, url_kwargs):

        if 'interface' in request.GET:
            try:
                obj.assigned_object = Interface.objects.get(pk=request.GET['interface'])
            except (ValueError, Interface.DoesNotExist):
                pass

        elif 'vminterface' in request.GET:
            try:
                obj.assigned_object = VMInterface.objects.get(pk=request.GET['vminterface'])
            except (ValueError, VMInterface.DoesNotExist):
                pass

        elif 'fhrpgroup' in request.GET:
            try:
                obj.assigned_object = FHRPGroup.objects.get(pk=request.GET['fhrpgroup'])
            except (ValueError, FHRPGroup.DoesNotExist):
                pass

        return obj

    def get_extra_addanother_params(self, request):
        if 'interface' in request.GET:
            return {'interface': request.GET['interface']}
        if 'vminterface' in request.GET:
            return {'vminterface': request.GET['vminterface']}
        return {}


# TODO: Standardize or remove this view
@register_model_view(IPAddress, 'assign', path='assign', detail=False)
class IPAddressAssignView(generic.ObjectView):
    """
    Search for IPAddresses to be assigned to an Interface.
    """
    queryset = IPAddress.objects.all()

    def dispatch(self, request, *args, **kwargs):

        # Redirect user if an interface has not been provided
        if 'interface' not in request.GET and 'vminterface' not in request.GET:
            return redirect('ipam:ipaddress_add')

        return super().dispatch(request, *args, **kwargs)

    def get(self, request):
        form = forms.IPAddressAssignForm()

        return render(request, 'ipam/ipaddress_assign.html', {
            'form': form,
            'return_url': request.GET.get('return_url', ''),
        })

    def post(self, request):
        form = forms.IPAddressAssignForm(request.POST)
        table = None

        if form.is_valid():
            addresses = self.queryset.prefetch_related('vrf', 'tenant')
            # Limit to 100 results
            addresses = filtersets.IPAddressFilterSet(request.POST, addresses).qs[:100]
            table = tables.IPAddressAssignTable(addresses)
            table.configure(request)

        return render(request, 'ipam/ipaddress_assign.html', {
            'form': form,
            'table': table,
            'return_url': request.GET.get('return_url'),
        })


@register_model_view(IPAddress, 'delete')
class IPAddressDeleteView(generic.ObjectDeleteView):
    queryset = IPAddress.objects.all()


@register_model_view(IPAddress, 'bulk_add', path='bulk-add', detail=False)
class IPAddressBulkCreateView(generic.BulkCreateView):
    queryset = IPAddress.objects.all()
    form = forms.IPNetworkBulkCreateForm
    model_form = forms.IPAddressBulkAddForm
    pattern_target = 'address'
    template_name = 'ipam/ipaddress_bulk_add.html'


@register_model_view(IPAddress, 'bulk_import', path='import', detail=False)
class IPAddressBulkImportView(generic.BulkImportView):
    queryset = IPAddress.objects.all()
    model_form = forms.IPAddressImportForm


@register_model_view(IPAddress, 'bulk_edit', path='edit', detail=False)
class IPAddressBulkEditView(generic.BulkEditView):
    queryset = IPAddress.objects.prefetch_related('vrf__tenant')
    filterset = filtersets.IPAddressFilterSet
    table = tables.IPAddressTable
    form = forms.IPAddressBulkEditForm


@register_model_view(IPAddress, 'bulk_delete', path='delete', detail=False)
class IPAddressBulkDeleteView(generic.BulkDeleteView):
    queryset = IPAddress.objects.prefetch_related('vrf__tenant')
    filterset = filtersets.IPAddressFilterSet
    table = tables.IPAddressTable


@register_model_view(IPAddress, 'related_ips', path='related-ip-addresses')
class IPAddressRelatedIPsView(generic.ObjectChildrenView):
    queryset = IPAddress.objects.prefetch_related('vrf__tenant', 'tenant')
    child_model = IPAddress
    table = tables.IPAddressTable
    filterset = filtersets.IPAddressFilterSet
    filterset_form = forms.IPAddressFilterForm
    tab = ViewTab(
        label=_('Related IPs'),
        badge=lambda x: x.get_related_ips().count(),
        weight=500,
        hide_if_empty=True,
    )

    def get_children(self, request, parent):
        return parent.get_related_ips().restrict(request.user, 'view')


#
# VLAN groups
#

@register_model_view(VLANGroup, 'list', path='', detail=False)
class VLANGroupListView(generic.ObjectListView):
    queryset = VLANGroup.objects.annotate_utilization()
    filterset = filtersets.VLANGroupFilterSet
    filterset_form = forms.VLANGroupFilterForm
    table = tables.VLANGroupTable


@register_model_view(VLANGroup)
class VLANGroupView(GetRelatedModelsMixin, generic.ObjectView):
    queryset = VLANGroup.objects.annotate_utilization()

    def get_extra_context(self, request, instance):
        return {
            'related_models': self.get_related_models(request, instance),
        }


@register_model_view(VLANGroup, 'add', detail=False)
@register_model_view(VLANGroup, 'edit')
class VLANGroupEditView(generic.ObjectEditView):
    queryset = VLANGroup.objects.all()
    form = forms.VLANGroupForm


@register_model_view(VLANGroup, 'delete')
class VLANGroupDeleteView(generic.ObjectDeleteView):
    queryset = VLANGroup.objects.all()


@register_model_view(VLANGroup, 'bulk_import', path='import', detail=False)
class VLANGroupBulkImportView(generic.BulkImportView):
    queryset = VLANGroup.objects.all()
    model_form = forms.VLANGroupImportForm


@register_model_view(VLANGroup, 'bulk_edit', path='edit', detail=False)
class VLANGroupBulkEditView(generic.BulkEditView):
    queryset = VLANGroup.objects.annotate_utilization().prefetch_related('tags')
    filterset = filtersets.VLANGroupFilterSet
    table = tables.VLANGroupTable
    form = forms.VLANGroupBulkEditForm


@register_model_view(VLANGroup, 'bulk_rename', path='rename', detail=False)
class VLANGroupBulkRenameView(generic.BulkRenameView):
    queryset = VLANGroup.objects.all()
    filterset = filtersets.VLANGroupFilterSet


@register_model_view(VLANGroup, 'bulk_delete', path='delete', detail=False)
class VLANGroupBulkDeleteView(generic.BulkDeleteView):
    queryset = VLANGroup.objects.annotate_utilization().prefetch_related('tags')
    filterset = filtersets.VLANGroupFilterSet
    table = tables.VLANGroupTable


@register_model_view(VLANGroup, 'vlans')
class VLANGroupVLANsView(generic.ObjectChildrenView):
    queryset = VLANGroup.objects.all()
    child_model = VLAN
    table = tables.VLANTable
    filterset = filtersets.VLANFilterSet
    filterset_form = forms.VLANFilterForm
    tab = ViewTab(
        label=_('VLANs'),
        badge=lambda x: x.get_child_vlans().count(),
        permission='ipam.view_vlan',
        weight=500
    )

    def get_children(self, request, parent):
        return parent.get_child_vlans().restrict(request.user, 'view').prefetch_related(
            Prefetch('prefixes', queryset=Prefix.objects.restrict(request.user)),
            'tenant', 'site', 'role',
        )

    def prep_table_data(self, request, queryset, parent):
        if not get_table_ordering(request, self.table):
            return add_available_vlans(queryset, parent)
        return queryset


#
# VLAN Translation Policies
#

@register_model_view(VLANTranslationPolicy, 'list', path='', detail=False)
class VLANTranslationPolicyListView(generic.ObjectListView):
    queryset = VLANTranslationPolicy.objects.annotate(
        rule_count=count_related(VLANTranslationRule, 'policy'),
    )
    filterset = filtersets.VLANTranslationPolicyFilterSet
    filterset_form = forms.VLANTranslationPolicyFilterForm
    table = tables.VLANTranslationPolicyTable


@register_model_view(VLANTranslationPolicy)
class VLANTranslationPolicyView(GetRelatedModelsMixin, generic.ObjectView):
    queryset = VLANTranslationPolicy.objects.all()

    def get_extra_context(self, request, instance):
        vlan_translation_table = VLANTranslationRuleTable(
            data=instance.rules.all(),
            orderable=False
        )
        vlan_translation_table.configure(request)

        return {
            'vlan_translation_table': vlan_translation_table,
        }


@register_model_view(VLANTranslationPolicy, 'add', detail=False)
@register_model_view(VLANTranslationPolicy, 'edit')
class VLANTranslationPolicyEditView(generic.ObjectEditView):
    queryset = VLANTranslationPolicy.objects.all()
    form = forms.VLANTranslationPolicyForm


@register_model_view(VLANTranslationPolicy, 'delete')
class VLANTranslationPolicyDeleteView(generic.ObjectDeleteView):
    queryset = VLANTranslationPolicy.objects.all()


@register_model_view(VLANTranslationPolicy, 'bulk_import', path='import', detail=False)
class VLANTranslationPolicyBulkImportView(generic.BulkImportView):
    queryset = VLANTranslationPolicy.objects.all()
    model_form = forms.VLANTranslationPolicyImportForm


@register_model_view(VLANTranslationPolicy, 'bulk_edit', path='edit', detail=False)
class VLANTranslationPolicyBulkEditView(generic.BulkEditView):
    queryset = VLANTranslationPolicy.objects.all()
    filterset = filtersets.VLANTranslationPolicyFilterSet
    table = tables.VLANTranslationPolicyTable
    form = forms.VLANTranslationPolicyBulkEditForm


@register_model_view(VLANTranslationPolicy, 'bulk_rename', path='rename', detail=False)
class VLANTranslationPolicyBulkRenameView(generic.BulkRenameView):
    queryset = VLANTranslationPolicy.objects.all()
    filterset = filtersets.VLANTranslationPolicyFilterSet


@register_model_view(VLANTranslationPolicy, 'bulk_delete', path='delete', detail=False)
class VLANTranslationPolicyBulkDeleteView(generic.BulkDeleteView):
    queryset = VLANTranslationPolicy.objects.all()
    filterset = filtersets.VLANTranslationPolicyFilterSet
    table = tables.VLANTranslationPolicyTable


#
# VLAN Translation Rules
#

@register_model_view(VLANTranslationRule, 'list', path='', detail=False)
class VLANTranslationRuleListView(generic.ObjectListView):
    queryset = VLANTranslationRule.objects.all()
    filterset = filtersets.VLANTranslationRuleFilterSet
    filterset_form = forms.VLANTranslationRuleFilterForm
    table = tables.VLANTranslationRuleTable
    actions = (AddObject, BulkImport, BulkExport, BulkEdit, BulkDelete)


@register_model_view(VLANTranslationRule)
class VLANTranslationRuleView(GetRelatedModelsMixin, generic.ObjectView):
    queryset = VLANTranslationRule.objects.all()

    def get_extra_context(self, request, instance):
        return {
            'related_models': self.get_related_models(request, instance),
        }


@register_model_view(VLANTranslationRule, 'add', detail=False)
@register_model_view(VLANTranslationRule, 'edit')
class VLANTranslationRuleEditView(generic.ObjectEditView):
    queryset = VLANTranslationRule.objects.all()
    form = forms.VLANTranslationRuleForm


@register_model_view(VLANTranslationRule, 'delete')
class VLANTranslationRuleDeleteView(generic.ObjectDeleteView):
    queryset = VLANTranslationRule.objects.all()


@register_model_view(VLANTranslationRule, 'bulk_import', path='import', detail=False)
class VLANTranslationRuleBulkImportView(generic.BulkImportView):
    queryset = VLANTranslationRule.objects.all()
    model_form = forms.VLANTranslationRuleImportForm


@register_model_view(VLANTranslationRule, 'bulk_edit', path='edit', detail=False)
class VLANTranslationRuleBulkEditView(generic.BulkEditView):
    queryset = VLANTranslationRule.objects.all()
    filterset = filtersets.VLANTranslationRuleFilterSet
    table = tables.VLANTranslationRuleTable
    form = forms.VLANTranslationRuleBulkEditForm


@register_model_view(VLANTranslationRule, 'bulk_delete', path='delete', detail=False)
class VLANTranslationRuleBulkDeleteView(generic.BulkDeleteView):
    queryset = VLANTranslationRule.objects.all()
    filterset = filtersets.VLANTranslationRuleFilterSet
    table = tables.VLANTranslationRuleTable


#
# FHRP groups
#

@register_model_view(FHRPGroup, 'list', path='', detail=False)
class FHRPGroupListView(generic.ObjectListView):
    queryset = FHRPGroup.objects.annotate(
        member_count=count_related(FHRPGroupAssignment, 'group')
    )
    filterset = filtersets.FHRPGroupFilterSet
    filterset_form = forms.FHRPGroupFilterForm
    table = tables.FHRPGroupTable


@register_model_view(FHRPGroup)
class FHRPGroupView(GetRelatedModelsMixin, generic.ObjectView):
    queryset = FHRPGroup.objects.all()

    def get_extra_context(self, request, instance):
        # Get assigned interfaces
        members_table = tables.FHRPGroupAssignmentTable(
            data=FHRPGroupAssignment.objects.restrict(request.user, 'view').filter(group=instance),
            orderable=False
        )
        members_table.configure(request)
        members_table.columns.hide('group')

        return {
            'related_models': self.get_related_models(
                request, instance,
                extra=(
                    (
                        Service.objects.restrict(request.user, 'view').filter(
                            parent_object_type=ContentType.objects.get_for_model(FHRPGroup),
                            parent_object_id=instance.id,
                        ),
                        'fhrpgroup_id'
                    ),
                ),
            ),
            'members_table': members_table,
            'member_count': FHRPGroupAssignment.objects.filter(group=instance).count(),
        }


@register_model_view(FHRPGroup, 'add', detail=False)
@register_model_view(FHRPGroup, 'edit')
class FHRPGroupEditView(generic.ObjectEditView):
    queryset = FHRPGroup.objects.all()
    form = forms.FHRPGroupForm

    def get_return_url(self, request, obj=None):
        return_url = super().get_return_url(request, obj)

        # If we're redirecting the user to the FHRPGroupAssignment creation form,
        # initialize the group field with the FHRPGroup we just saved.
        if return_url.startswith(reverse('ipam:fhrpgroupassignment_add')):
            return_url += f'&group={obj.pk}'

        return return_url

    def alter_object(self, obj, request, url_args, url_kwargs):
        # Workaround to solve #10719. Capture the current user on the FHRPGroup instance so that
        # we can evaluate permissions during the creation of a new IPAddress within the form.
        obj._user = request.user
        return obj


@register_model_view(FHRPGroup, 'delete')
class FHRPGroupDeleteView(generic.ObjectDeleteView):
    queryset = FHRPGroup.objects.all()


@register_model_view(FHRPGroup, 'bulk_import', path='import', detail=False)
class FHRPGroupBulkImportView(generic.BulkImportView):
    queryset = FHRPGroup.objects.all()
    model_form = forms.FHRPGroupImportForm


@register_model_view(FHRPGroup, 'bulk_edit', path='edit', detail=False)
class FHRPGroupBulkEditView(generic.BulkEditView):
    queryset = FHRPGroup.objects.all()
    filterset = filtersets.FHRPGroupFilterSet
    table = tables.FHRPGroupTable
    form = forms.FHRPGroupBulkEditForm


@register_model_view(FHRPGroup, 'bulk_rename', path='rename', detail=False)
class FHRPGroupBulkRenameView(generic.BulkRenameView):
    queryset = FHRPGroup.objects.all()
    filterset = filtersets.FHRPGroupFilterSet


@register_model_view(FHRPGroup, 'bulk_delete', path='delete', detail=False)
class FHRPGroupBulkDeleteView(generic.BulkDeleteView):
    queryset = FHRPGroup.objects.all()
    filterset = filtersets.FHRPGroupFilterSet
    table = tables.FHRPGroupTable


#
# FHRP group assignments
#

@register_model_view(FHRPGroupAssignment, 'add', detail=False)
@register_model_view(FHRPGroupAssignment, 'edit')
class FHRPGroupAssignmentEditView(generic.ObjectEditView):
    queryset = FHRPGroupAssignment.objects.all()
    form = forms.FHRPGroupAssignmentForm

    def alter_object(self, instance, request, args, kwargs):
        if not instance.pk:
            # Assign the interface based on URL kwargs
            content_type = get_object_or_404(ContentType, pk=request.GET.get('interface_type'))
            instance.interface = get_object_or_404(content_type.model_class(), pk=request.GET.get('interface_id'))
        return instance

    def get_extra_addanother_params(self, request):
        return {
            'interface_type': request.GET.get('interface_type'),
            'interface_id': request.GET.get('interface_id'),
        }


@register_model_view(FHRPGroupAssignment, 'delete')
class FHRPGroupAssignmentDeleteView(generic.ObjectDeleteView):
    queryset = FHRPGroupAssignment.objects.all()


#
# VLANs
#

@register_model_view(VLAN, 'list', path='', detail=False)
class VLANListView(generic.ObjectListView):
    queryset = VLAN.objects.all()
    filterset = filtersets.VLANFilterSet
    filterset_form = forms.VLANFilterForm
    table = tables.VLANTable


@register_model_view(VLAN)
class VLANView(generic.ObjectView):
    queryset = VLAN.objects.all()

    def get_extra_context(self, request, instance):
        prefixes = Prefix.objects.restrict(request.user, 'view').filter(vlan=instance).prefetch_related(
            'vrf', 'scope', 'role', 'tenant'
        )
        prefix_table = tables.PrefixTable(list(prefixes), exclude=('vlan', 'utilization'), orderable=False)
        prefix_table.configure(request)

        return {
            'prefix_table': prefix_table,
        }


@register_model_view(VLAN, 'interfaces')
class VLANInterfacesView(generic.ObjectChildrenView):
    queryset = VLAN.objects.all()
    child_model = Interface
    table = tables.VLANDevicesTable
    filterset = InterfaceFilterSet
    filterset_form = InterfaceFilterForm
    tab = ViewTab(
        label=_('Device Interfaces'),
        badge=lambda x: x.get_interfaces().count(),
        permission='dcim.view_interface',
        weight=500
    )

    def get_children(self, request, parent):
        return parent.get_interfaces().restrict(request.user, 'view')


@register_model_view(VLAN, 'vminterfaces', path='vm-interfaces')
class VLANVMInterfacesView(generic.ObjectChildrenView):
    queryset = VLAN.objects.all()
    child_model = VMInterface
    table = tables.VLANVirtualMachinesTable
    filterset = VMInterfaceFilterSet
    filterset_form = VMInterfaceFilterForm
    tab = ViewTab(
        label=_('VM Interfaces'),
        badge=lambda x: x.get_vminterfaces().count(),
        permission='virtualization.view_vminterface',
        weight=510
    )

    def get_children(self, request, parent):
        return parent.get_vminterfaces().restrict(request.user, 'view')


@register_model_view(VLAN, 'add', detail=False)
@register_model_view(VLAN, 'edit')
class VLANEditView(generic.ObjectEditView):
    queryset = VLAN.objects.all()
    form = forms.VLANForm
    template_name = 'ipam/vlan_edit.html'


@register_model_view(VLAN, 'delete')
class VLANDeleteView(generic.ObjectDeleteView):
    queryset = VLAN.objects.all()


@register_model_view(VLAN, 'bulk_import', path='import', detail=False)
class VLANBulkImportView(generic.BulkImportView):
    queryset = VLAN.objects.all()
    model_form = forms.VLANImportForm


@register_model_view(VLAN, 'bulk_edit', path='edit', detail=False)
class VLANBulkEditView(generic.BulkEditView):
    queryset = VLAN.objects.all()
    filterset = filtersets.VLANFilterSet
    table = tables.VLANTable
    form = forms.VLANBulkEditForm


@register_model_view(VLAN, 'bulk_rename', path='rename', detail=False)
class VLANBulkRenameView(generic.BulkRenameView):
    queryset = VLAN.objects.all()
    filterset = filtersets.VLANFilterSet


@register_model_view(VLAN, 'bulk_delete', path='delete', detail=False)
class VLANBulkDeleteView(generic.BulkDeleteView):
    queryset = VLAN.objects.all()
    filterset = filtersets.VLANFilterSet
    table = tables.VLANTable


#
# Service templates
#

@register_model_view(ServiceTemplate, 'list', path='', detail=False)
class ServiceTemplateListView(generic.ObjectListView):
    queryset = ServiceTemplate.objects.all()
    filterset = filtersets.ServiceTemplateFilterSet
    filterset_form = forms.ServiceTemplateFilterForm
    table = tables.ServiceTemplateTable


@register_model_view(ServiceTemplate)
class ServiceTemplateView(generic.ObjectView):
    queryset = ServiceTemplate.objects.all()


@register_model_view(ServiceTemplate, 'add', detail=False)
@register_model_view(ServiceTemplate, 'edit')
class ServiceTemplateEditView(generic.ObjectEditView):
    queryset = ServiceTemplate.objects.all()
    form = forms.ServiceTemplateForm


@register_model_view(ServiceTemplate, 'delete')
class ServiceTemplateDeleteView(generic.ObjectDeleteView):
    queryset = ServiceTemplate.objects.all()


@register_model_view(ServiceTemplate, 'bulk_import', path='import', detail=False)
class ServiceTemplateBulkImportView(generic.BulkImportView):
    queryset = ServiceTemplate.objects.all()
    model_form = forms.ServiceTemplateImportForm


@register_model_view(ServiceTemplate, 'bulk_edit', path='edit', detail=False)
class ServiceTemplateBulkEditView(generic.BulkEditView):
    queryset = ServiceTemplate.objects.all()
    filterset = filtersets.ServiceTemplateFilterSet
    table = tables.ServiceTemplateTable
    form = forms.ServiceTemplateBulkEditForm


@register_model_view(ServiceTemplate, 'bulk_rename', path='rename', detail=False)
class ServiceTemplateBulkRenameView(generic.BulkRenameView):
    queryset = ServiceTemplate.objects.all()
    filterset = filtersets.ServiceTemplateFilterSet


@register_model_view(ServiceTemplate, 'bulk_delete', path='delete', detail=False)
class ServiceTemplateBulkDeleteView(generic.BulkDeleteView):
    queryset = ServiceTemplate.objects.all()
    filterset = filtersets.ServiceTemplateFilterSet
    table = tables.ServiceTemplateTable


#
# Services
#

@register_model_view(Service, 'list', path='', detail=False)
class ServiceListView(generic.ObjectListView):
    queryset = Service.objects.prefetch_related('parent')
    filterset = filtersets.ServiceFilterSet
    filterset_form = forms.ServiceFilterForm
    table = tables.ServiceTable


@register_model_view(Service)
class ServiceView(generic.ObjectView):
    queryset = Service.objects.all()

    def get_extra_context(self, request, instance):
        context = {}
        match instance.parent:
            case Device():
                context['breadcrumb_queryparam'] = 'device_id'
            case VirtualMachine():
                context['breadcrumb_queryparam'] = 'virtual_machine_id'
            case FHRPGroup():
                context['breadcrumb_queryparam'] = 'fhrpgroup_id'

        return context


@register_model_view(Service, 'add', detail=False)
class ServiceCreateView(generic.ObjectEditView):
    queryset = Service.objects.all()
    form = forms.ServiceCreateForm


@register_model_view(Service, 'edit')
class ServiceEditView(generic.ObjectEditView):
    queryset = Service.objects.all()
    form = forms.ServiceForm


@register_model_view(Service, 'delete')
class ServiceDeleteView(generic.ObjectDeleteView):
    queryset = Service.objects.all()


@register_model_view(Service, 'bulk_import', path='import', detail=False)
class ServiceBulkImportView(generic.BulkImportView):
    queryset = Service.objects.all()
    model_form = forms.ServiceImportForm


@register_model_view(Service, 'bulk_edit', path='edit', detail=False)
class ServiceBulkEditView(generic.BulkEditView):
    queryset = Service.objects.prefetch_related('parent')
    filterset = filtersets.ServiceFilterSet
    table = tables.ServiceTable
    form = forms.ServiceBulkEditForm


@register_model_view(Service, 'bulk_rename', path='rename', detail=False)
class ServiceBulkRenameView(generic.BulkRenameView):
    queryset = Service.objects.all()
    filterset = filtersets.ServiceFilterSet


@register_model_view(Service, 'bulk_delete', path='delete', detail=False)
class ServiceBulkDeleteView(generic.BulkDeleteView):
    queryset = Service.objects.prefetch_related('parent')
    filterset = filtersets.ServiceFilterSet
    table = tables.ServiceTable
