from typing import TYPE_CHECKING, Annotated

import strawberry
import strawberry_django
from django.db.models import Q, QuerySet
from strawberry.scalars import ID
from strawberry_django import BaseFilterLookup, DatetimeFilterLookup, FilterLookup, StrFilterLookup

from extras import models
from extras.graphql.filter_mixins import CustomFieldsFilterMixin, TagsFilterMixin
from netbox.graphql.filter_mixins import SyncedDataFilterMixin
from netbox.graphql.filters import BaseModelFilter, ChangeLoggedModelFilter, PrimaryModelFilter

if TYPE_CHECKING:
    from core.graphql.filters import ContentTypeFilter
    from dcim.graphql.filters import (
        DeviceRoleFilter,
        DeviceTypeFilter,
        LocationFilter,
        PlatformFilter,
        RegionFilter,
        SiteFilter,
        SiteGroupFilter,
    )
    from extras.graphql.filter_lookups import ExtraChoicesLookup
    from netbox.graphql.enums import ColorEnum
    from netbox.graphql.filter_lookups import (
        BigIntegerLookup,
        FloatLookup,
        IntegerLookup,
        JSONFilter,
        StringArrayLookup,
        TreeNodeFilter,
    )
    from tenancy.graphql.filters import TenantFilter, TenantGroupFilter
    from users.graphql.filters import GroupFilter, UserFilter
    from virtualization.graphql.filters import ClusterFilter, ClusterGroupFilter, ClusterTypeFilter

    from .enums import *

__all__ = (
    'ConfigContextFilter',
    'ConfigContextProfileFilter',
    'ConfigTemplateFilter',
    'CustomFieldChoiceSetFilter',
    'CustomFieldFilter',
    'CustomLinkFilter',
    'EventRuleFilter',
    'ExportTemplateFilter',
    'ImageAttachmentFilter',
    'JournalEntryFilter',
    'NotificationFilter',
    'NotificationGroupFilter',
    'SavedFilterFilter',
    'SubscriptionFilter',
    'TableConfigFilter',
    'TagFilter',
    'WebhookFilter',
)


@strawberry_django.filter_type(models.ConfigContext, lookups=True)
class ConfigContextFilter(SyncedDataFilterMixin, ChangeLoggedModelFilter):
    name: StrFilterLookup | None = strawberry_django.filter_field()
    weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
        strawberry_django.filter_field()
    )
    description: StrFilterLookup | None = strawberry_django.filter_field()
    is_active: FilterLookup[bool] | None = strawberry_django.filter_field()
    regions: Annotated['RegionFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
        strawberry_django.filter_field()
    )
    region_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
        strawberry_django.filter_field()
    )
    site_groups: Annotated['SiteGroupFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
        strawberry_django.filter_field()
    )
    site_group_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
        strawberry_django.filter_field()
    )
    sites: Annotated['SiteFilter', strawberry.lazy('dcim.graphql.filters')] | None = strawberry_django.filter_field()
    locations: Annotated['LocationFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
        strawberry_django.filter_field()
    )
    device_types: Annotated['DeviceTypeFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
        strawberry_django.filter_field()
    )
    roles: Annotated['DeviceRoleFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
        strawberry_django.filter_field()
    )
    platforms: Annotated['PlatformFilter', strawberry.lazy('dcim.graphql.filters')] | None = (
        strawberry_django.filter_field()
    )
    cluster_types: Annotated['ClusterTypeFilter', strawberry.lazy('virtualization.graphql.filters')] | None = (
        strawberry_django.filter_field()
    )
    cluster_groups: Annotated['ClusterGroupFilter', strawberry.lazy('virtualization.graphql.filters')] | None = (
        strawberry_django.filter_field()
    )
    clusters: Annotated['ClusterFilter', strawberry.lazy('virtualization.graphql.filters')] | None = (
        strawberry_django.filter_field()
    )
    tenant_groups: Annotated['TenantGroupFilter', strawberry.lazy('tenancy.graphql.filters')] | None = (
        strawberry_django.filter_field()
    )
    tenant_group_id: Annotated['TreeNodeFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
        strawberry_django.filter_field()
    )
    tenants: Annotated['TenantFilter', strawberry.lazy('tenancy.graphql.filters')] | None = (
        strawberry_django.filter_field()
    )
    tags: Annotated['TagFilter', strawberry.lazy('extras.graphql.filters')] | None = strawberry_django.filter_field()
    data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
        strawberry_django.filter_field()
    )


@strawberry_django.filter_type(models.ConfigContextProfile, lookups=True)
class ConfigContextProfileFilter(SyncedDataFilterMixin, PrimaryModelFilter):
    name: StrFilterLookup | None = strawberry_django.filter_field()
    description: StrFilterLookup | None = strawberry_django.filter_field()
    tags: Annotated['TagFilter', strawberry.lazy('extras.graphql.filters')] | None = strawberry_django.filter_field()


@strawberry_django.filter_type(models.ConfigTemplate, lookups=True)
class ConfigTemplateFilter(SyncedDataFilterMixin, ChangeLoggedModelFilter):
    name: StrFilterLookup | None = strawberry_django.filter_field()
    description: StrFilterLookup | None = strawberry_django.filter_field()
    template_code: StrFilterLookup | None = strawberry_django.filter_field()
    environment_params: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
        strawberry_django.filter_field()
    )
    mime_type: StrFilterLookup | None = strawberry_django.filter_field()
    file_name: StrFilterLookup | None = strawberry_django.filter_field()
    file_extension: StrFilterLookup | None = strawberry_django.filter_field()
    as_attachment: FilterLookup[bool] | None = strawberry_django.filter_field()


@strawberry_django.filter_type(models.CustomField, lookups=True)
class CustomFieldFilter(ChangeLoggedModelFilter):
    type: BaseFilterLookup[Annotated['CustomFieldTypeEnum', strawberry.lazy('extras.graphql.enums')]] | None = (
        strawberry_django.filter_field()
    )
    object_types: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = (
        strawberry_django.filter_field()
    )
    related_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = (
        strawberry_django.filter_field()
    )
    name: StrFilterLookup | None = strawberry_django.filter_field()
    label: StrFilterLookup | None = strawberry_django.filter_field()
    group_name: StrFilterLookup | None = strawberry_django.filter_field()
    description: StrFilterLookup | None = strawberry_django.filter_field()
    required: FilterLookup[bool] | None = strawberry_django.filter_field()
    unique: FilterLookup[bool] | None = strawberry_django.filter_field()
    search_weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
        strawberry_django.filter_field()
    )
    filter_logic: (
        BaseFilterLookup[Annotated['CustomFieldFilterLogicEnum', strawberry.lazy('extras.graphql.enums')]] | None
    ) = (
        strawberry_django.filter_field()
    )
    default: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
        strawberry_django.filter_field()
    )
    related_object_filter: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
        strawberry_django.filter_field()
    )
    weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
        strawberry_django.filter_field()
    )
    validation_minimum: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
        strawberry_django.filter_field()
    )
    validation_maximum: Annotated['FloatLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
        strawberry_django.filter_field()
    )
    validation_regex: StrFilterLookup | None = strawberry_django.filter_field()
    choice_set: Annotated['CustomFieldChoiceSetFilter', strawberry.lazy('extras.graphql.filters')] | None = (
        strawberry_django.filter_field()
    )
    choice_set_id: ID | None = strawberry_django.filter_field()
    ui_visible: (
        BaseFilterLookup[Annotated['CustomFieldUIVisibleEnum', strawberry.lazy('extras.graphql.enums')]] | None
    ) = (
        strawberry_django.filter_field()
    )
    ui_editable: (
        BaseFilterLookup[Annotated['CustomFieldUIEditableEnum', strawberry.lazy('extras.graphql.enums')]] | None
    ) = (
        strawberry_django.filter_field()
    )
    is_cloneable: FilterLookup[bool] | None = strawberry_django.filter_field()
    nulls_first: FilterLookup[bool] | None = strawberry_django.filter_field()
    comments: StrFilterLookup | None = strawberry_django.filter_field()


@strawberry_django.filter_type(models.CustomFieldChoiceSet, lookups=True)
class CustomFieldChoiceSetFilter(ChangeLoggedModelFilter):
    name: StrFilterLookup | None = strawberry_django.filter_field()
    description: StrFilterLookup | None = strawberry_django.filter_field()
    base_choices: (
        BaseFilterLookup[Annotated['CustomFieldChoiceSetBaseEnum', strawberry.lazy('extras.graphql.enums')]] | None
    ) = (
        strawberry_django.filter_field()
    )
    extra_choices: Annotated['ExtraChoicesLookup', strawberry.lazy('extras.graphql.filter_lookups')] | None = (
        strawberry_django.filter_field()
    )
    order_alphabetically: FilterLookup[bool] | None = strawberry_django.filter_field()

    @strawberry_django.filter_field(resolve_value=True)
    def choice_colors(
        self,
        queryset: QuerySet,
        value: list[Annotated['CustomFieldChoiceColorEnum', strawberry.lazy('extras.graphql.enums')]],
        prefix: str,
    ) -> tuple[QuerySet, Q]:
        if not value:
            return queryset, Q()

        field_name = f'{prefix}choice_colors'
        choice_values = set()

        for mapping in queryset.exclude(**{f'{field_name}__isnull': True}).values_list(field_name, flat=True):
            if isinstance(mapping, dict):
                choice_values.update(mapping.keys())

        if not choice_values:
            return queryset, Q(pk__in=[])

        params = Q()
        for color in value:
            for choice_value in choice_values:
                params |= Q(**{f'{field_name}__contains': {choice_value: color}})

        return queryset, params


@strawberry_django.filter_type(models.CustomLink, lookups=True)
class CustomLinkFilter(ChangeLoggedModelFilter):
    name: StrFilterLookup | None = strawberry_django.filter_field()
    enabled: FilterLookup[bool] | None = strawberry_django.filter_field()
    link_text: StrFilterLookup | None = strawberry_django.filter_field()
    link_url: StrFilterLookup | None = strawberry_django.filter_field()
    weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
        strawberry_django.filter_field()
    )
    group_name: StrFilterLookup | None = strawberry_django.filter_field()
    button_class: (
        BaseFilterLookup[Annotated['CustomLinkButtonClassEnum', strawberry.lazy('extras.graphql.enums')]] | None
    ) = (
        strawberry_django.filter_field()
    )
    new_window: FilterLookup[bool] | None = strawberry_django.filter_field()


@strawberry_django.filter_type(models.ExportTemplate, lookups=True)
class ExportTemplateFilter(SyncedDataFilterMixin, ChangeLoggedModelFilter):
    name: StrFilterLookup | None = strawberry_django.filter_field()
    description: StrFilterLookup | None = strawberry_django.filter_field()
    template_code: StrFilterLookup | None = strawberry_django.filter_field()
    environment_params: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
        strawberry_django.filter_field()
    )
    mime_type: StrFilterLookup | None = strawberry_django.filter_field()
    file_name: StrFilterLookup | None = strawberry_django.filter_field()
    file_extension: StrFilterLookup | None = strawberry_django.filter_field()
    as_attachment: FilterLookup[bool] | None = strawberry_django.filter_field()


@strawberry_django.filter_type(models.ImageAttachment, lookups=True)
class ImageAttachmentFilter(ChangeLoggedModelFilter):
    object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = (
        strawberry_django.filter_field()
    )
    object_id: ID | None = strawberry_django.filter_field()
    image_height: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
        strawberry_django.filter_field()
    )
    image_width: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
        strawberry_django.filter_field()
    )
    image_size: Annotated['BigIntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
        strawberry_django.filter_field()
    )
    name: StrFilterLookup | None = strawberry_django.filter_field()


@strawberry_django.filter_type(models.JournalEntry, lookups=True)
class JournalEntryFilter(CustomFieldsFilterMixin, TagsFilterMixin, ChangeLoggedModelFilter):
    assigned_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = (
        strawberry_django.filter_field()
    )
    assigned_object_type_id: ID | None = strawberry_django.filter_field()
    assigned_object_id: ID | None = strawberry_django.filter_field()
    created_by: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = (
        strawberry_django.filter_field()
    )
    kind: BaseFilterLookup[Annotated['JournalEntryKindEnum', strawberry.lazy('extras.graphql.enums')]] | None = (
        strawberry_django.filter_field()
    )
    comments: StrFilterLookup | None = strawberry_django.filter_field()


@strawberry_django.filter_type(models.Notification, lookups=True)
class NotificationFilter(BaseModelFilter):
    created: DatetimeFilterLookup | None = strawberry_django.filter_field()
    read: DatetimeFilterLookup | None = strawberry_django.filter_field()
    user: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field()
    user_id: ID | None = strawberry_django.filter_field()
    object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = (
        strawberry_django.filter_field()
    )
    object_type_id: ID | None = strawberry_django.filter_field()
    object_id: ID | None = strawberry_django.filter_field()
    object_repr: StrFilterLookup | None = strawberry_django.filter_field()
    event_type: StrFilterLookup | None = strawberry_django.filter_field()


@strawberry_django.filter_type(models.NotificationGroup, lookups=True)
class NotificationGroupFilter(ChangeLoggedModelFilter):
    name: StrFilterLookup | None = strawberry_django.filter_field()
    description: StrFilterLookup | None = strawberry_django.filter_field()
    groups: Annotated['GroupFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field()
    users: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field()


@strawberry_django.filter_type(models.SavedFilter, lookups=True)
class SavedFilterFilter(ChangeLoggedModelFilter):
    name: StrFilterLookup | None = strawberry_django.filter_field()
    slug: StrFilterLookup | None = strawberry_django.filter_field()
    description: StrFilterLookup | None = strawberry_django.filter_field()
    user: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field()
    user_id: ID | None = strawberry_django.filter_field()
    weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
        strawberry_django.filter_field()
    )
    enabled: FilterLookup[bool] | None = strawberry_django.filter_field()
    shared: FilterLookup[bool] | None = strawberry_django.filter_field()
    parameters: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
        strawberry_django.filter_field()
    )


@strawberry_django.filter_type(models.Subscription, lookups=True)
class SubscriptionFilter(BaseModelFilter):
    created: DatetimeFilterLookup | None = strawberry_django.filter_field()
    user: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field()
    user_id: ID | None = strawberry_django.filter_field()
    object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = (
        strawberry_django.filter_field()
    )
    object_type_id: ID | None = strawberry_django.filter_field()
    object_id: ID | None = strawberry_django.filter_field()


@strawberry_django.filter_type(models.TableConfig, lookups=True)
class TableConfigFilter(ChangeLoggedModelFilter):
    name: StrFilterLookup | None = strawberry_django.filter_field()
    description: StrFilterLookup | None = strawberry_django.filter_field()
    user: Annotated['UserFilter', strawberry.lazy('users.graphql.filters')] | None = strawberry_django.filter_field()
    user_id: ID | None = strawberry_django.filter_field()
    weight: Annotated['IntegerLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
        strawberry_django.filter_field()
    )
    enabled: FilterLookup[bool] | None = strawberry_django.filter_field()
    shared: FilterLookup[bool] | None = strawberry_django.filter_field()


@strawberry_django.filter_type(models.Tag, lookups=True)
class TagFilter(ChangeLoggedModelFilter):
    name: StrFilterLookup | None = strawberry_django.filter_field()
    slug: StrFilterLookup | None = strawberry_django.filter_field()
    color: BaseFilterLookup[Annotated['ColorEnum', strawberry.lazy('netbox.graphql.enums')]] | None = (
        strawberry_django.filter_field()
    )
    description: StrFilterLookup | None = strawberry_django.filter_field()


@strawberry_django.filter_type(models.Webhook, lookups=True)
class WebhookFilter(CustomFieldsFilterMixin, TagsFilterMixin, ChangeLoggedModelFilter):
    name: StrFilterLookup | None = strawberry_django.filter_field()
    description: StrFilterLookup | None = strawberry_django.filter_field()
    payload_url: StrFilterLookup | None = strawberry_django.filter_field()
    http_method: (
        BaseFilterLookup[Annotated['WebhookHttpMethodEnum', strawberry.lazy('extras.graphql.enums')]] | None
    ) = (
        strawberry_django.filter_field()
    )
    http_content_type: StrFilterLookup | None = strawberry_django.filter_field()
    additional_headers: StrFilterLookup | None = strawberry_django.filter_field()
    body_template: StrFilterLookup | None = strawberry_django.filter_field()
    secret: StrFilterLookup | None = strawberry_django.filter_field()
    ssl_verification: FilterLookup[bool] | None = strawberry_django.filter_field()
    ca_file_path: StrFilterLookup | None = strawberry_django.filter_field()
    events: Annotated['EventRuleFilter', strawberry.lazy('extras.graphql.filters')] | None = (
        strawberry_django.filter_field()
    )


@strawberry_django.filter_type(models.EventRule, lookups=True)
class EventRuleFilter(CustomFieldsFilterMixin, TagsFilterMixin, ChangeLoggedModelFilter):
    name: StrFilterLookup | None = strawberry_django.filter_field()
    description: StrFilterLookup | None = strawberry_django.filter_field()
    event_types: Annotated['StringArrayLookup', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
        strawberry_django.filter_field()
    )
    enabled: FilterLookup[bool] | None = strawberry_django.filter_field()
    conditions: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
        strawberry_django.filter_field()
    )
    action_type: BaseFilterLookup[Annotated['EventRuleActionEnum', strawberry.lazy('extras.graphql.enums')]] | None = (
        strawberry_django.filter_field()
    )
    action_object_type: Annotated['ContentTypeFilter', strawberry.lazy('core.graphql.filters')] | None = (
        strawberry_django.filter_field()
    )
    action_object_type_id: ID | None = strawberry_django.filter_field()
    action_object_id: ID | None = strawberry_django.filter_field()
    action_data: Annotated['JSONFilter', strawberry.lazy('netbox.graphql.filter_lookups')] | None = (
        strawberry_django.filter_field()
    )
    comments: StrFilterLookup | None = strawberry_django.filter_field()
