"""Admin interfaces for visitor analytics and IP blocking."""

from __future__ import annotations

from django.contrib import admin, messages
from django.db.models import Q
from django.http import HttpResponseRedirect, JsonResponse
from django.template.response import TemplateResponse
from django.urls import path, reverse
from django.utils import timezone
from django.utils.html import format_html
from unfold.admin import ModelAdmin

from cms.models import BlockedIP, VisitorPageView, VisitorSession
from cms.visitor_services import ANALYTICS, BLOCKLIST


class BlockSelectedIPsMixin:
    """Admin behaviour shared by models that can block visitor IPs."""

    @admin.action(description="Block selected IP addresses")
    def block_selected_ips(self, request, queryset):
        count = 0
        for obj in queryset:
            ip = getattr(obj, 'ip_address', None)
            if not ip:
                continue
            reason = getattr(self, 'block_reason', None) or self.block_reason_for(obj)
            BLOCKLIST.add_block(
                ip,
                reason=reason,
                source='manual',
                notes=f'Blocked from admin by {request.user}',
                created_by=request.user,
            )
            count += 1
        self.message_user(request, f'Blocked {count} IP address(es).', messages.SUCCESS)

    def block_reason_for(self, obj):
        return ''


class VisitorSessionAdmin(BlockSelectedIPsMixin, ModelAdmin):
    """Browse what each visitor did, where they came from and where they went."""

    list_display = (
        'ip_address', 'location', 'country_code', 'city', 'device_type',
        'browser', 'os', 'page_views', 'viewed', 'is_bot_badge', 'last_seen',
    )
    list_filter = ('is_bot', 'is_blocked', 'device_type', 'browser', 'os', 'country_code', 'last_seen')
    search_fields = ('ip_address', 'city', 'region', 'country_name', 'isp', 'user_agent', 'session_key')
    readonly_fields = [f.name for f in VisitorSession._meta.fields]
    date_hierarchy = 'last_seen'

    fieldsets = (
        ('Identity', {
            'fields': ('session_key', 'ip_address', 'ip_version', 'is_bot', 'is_blocked'),
            'classes': ('tab',),
        }),
        ('Network Intelligence', {
            'fields': (
                'country_code', 'country_name', 'region', 'city', 'postal_code',
                'latitude', 'longitude', 'timezone_name', 'isp', 'org', 'asn',
                'is_proxy', 'is_hosting',
            ),
            'classes': ('tab',),
        }),
        ('Device', {
            'fields': ('user_agent', 'device_type', 'browser', 'os'),
            'classes': ('tab',),
        }),
        ('Navigation', {
            'fields': ('referer', 'landing_path', 'last_path', 'region_code', 'page_views'),
            'classes': ('tab',),
        }),
        ('Timestamps', {
            'fields': ('first_seen', 'last_seen'),
            'classes': ('collapse', 'tab'),
        }),
    )

    @admin.display(description='Location')
    def location(self, obj):
        return obj.location_display

    @admin.display(description='Last page')
    def viewed(self, obj):
        return (obj.last_path or '')[:40]

    @admin.display(description='Bot', ordering='is_bot')
    def is_bot_badge(self, obj):
        if obj.is_bot:
            return format_html('<span class="unfold-badge unfold-badge--danger">{}</span>', 'BOT')
        return format_html('<span class="unfold-badge unfold-badge--success">{}</span>', 'Human')

    def has_add_permission(self, request):
        return False

    def has_change_permission(self, request, obj=None):
        return request.user.is_superuser


class VisitorPageViewAdmin(ModelAdmin):
    list_display = ('path', 'session', 'ip_address', 'status_code', 'method', 'viewed_at')
    list_filter = ('method', 'status_code', 'viewed_at')
    search_fields = ('path', 'ip_address', 'session__session_key')
    readonly_fields = [f.name for f in VisitorPageView._meta.fields]
    date_hierarchy = 'viewed_at'

    def has_add_permission(self, request):
        return False

    def has_change_permission(self, request, obj=None):
        return request.user.is_superuser


class BlockedIPAdmin(ModelAdmin):
    list_display = (
        'ip_address', 'block_type', 'reason', 'source', 'is_active_badge',
        'hit_count', 'last_hit', 'expires_at', 'created_at', 'unblock_button',
    )
    list_filter = ('block_type', 'source', 'is_active', 'created_at')
    search_fields = ('ip_address', 'reason', 'notes')
    readonly_fields = ('hit_count', 'last_hit', 'created_at', 'updated_at')
    date_hierarchy = 'created_at'
    actions = ['unblock_selected', 'activate_selected', 'deactivate_selected', 'delete_selected']

    fieldsets = (
        ('Block', {
            'fields': ('ip_address', 'block_type', 'is_active', 'expires_at'),
            'classes': ('tab',),
        }),
        ('Details', {
            'fields': ('reason', 'source', 'notes', 'created_by'),
            'classes': ('tab',),
        }),
        ('Statistics', {
            'fields': ('hit_count', 'last_hit', 'created_at', 'updated_at'),
            'classes': ('collapse', 'tab'),
        }),
    )

    def save_model(self, request, obj, form, change):
        super().save_model(request, obj, form, change)
        BLOCKLIST.invalidate()

    def delete_model(self, request, obj):
        super().delete_model(request, obj)
        BLOCKLIST.invalidate()

    def delete_queryset(self, request, queryset):
        super().delete_queryset(request, queryset)
        BLOCKLIST.invalidate()

    def get_urls(self):
        urls = super().get_urls()
        custom_urls = [
            path(
                '<int:object_id>/unblock/',
                self.admin_site.admin_view(self.unblock_view),
                name='cms_blockedip_unblock',
            ),
        ]
        return custom_urls + urls

    def unblock_view(self, request, object_id, *args, **kwargs):
        """Deactivate one block and return to the changelist."""
        block = self.get_object(request, object_id)
        if block is not None:
            block.is_active = False
            block.expires_at = None
            block.save(update_fields=['is_active', 'expires_at', 'updated_at'])
            BLOCKLIST.invalidate()
            self.message_user(
                request,
                f'Unblocked {block.ip_address}. It can visit the site again immediately.',
                messages.SUCCESS,
            )
        return HttpResponseRedirect(reverse('admin:cms_blockedip_changelist'))

    @admin.display(description='Unblock')
    def unblock_button(self, obj):
        """One-click row action: unblock without opening the form."""
        if not obj.is_effective():
            return format_html('<span style="color:#94a3b8;">&mdash;</span>')
        return format_html(
            '<a href="{}" onclick="return confirm(\'Unblock {}?\');" '
            'style="color:#dc2626;font-weight:600;text-decoration:none;">Unblock</a>',
            reverse('admin:cms_blockedip_unblock', args=[obj.pk]),
            obj.ip_address,
        )

    @admin.display(description='Active', ordering='is_active')
    def is_active_badge(self, obj):
        if not obj.is_effective():
            return format_html('<span class="unfold-badge unfold-badge--danger">{}</span>', 'Inactive')
        return format_html('<span class="unfold-badge unfold-badge--success">{}</span>', 'Active')

    @admin.action(description="Unblock selected IPs")
    def unblock_selected(self, request, queryset):
        queryset.update(is_active=False, expires_at=None)
        BLOCKLIST.invalidate()
        self.message_user(
            request,
            f'Unblocked {queryset.count()} IP address(es).',
            messages.SUCCESS,
        )

    @admin.action(description="Activate selected blocks")
    def activate_selected(self, request, queryset):
        queryset.update(is_active=True, expires_at=None)
        BLOCKLIST.invalidate()
        self.message_user(request, 'Selected IP blocks activated.', messages.SUCCESS)

    @admin.action(description="Deactivate selected blocks")
    def deactivate_selected(self, request, queryset):
        queryset.update(is_active=False)
        BLOCKLIST.invalidate()
        self.message_user(request, 'Selected IP blocks deactivated.', messages.SUCCESS)


def security_dashboard_view(request):
    """Render live visitor + security analytics for the admin site."""
    summary = ANALYTICS.summary()
    now = timezone.now()

    online_10m = ANALYTICS.online_now(10)
    countries = ANALYTICS.top_countries(24, 10)
    pages = ANALYTICS.top_pages(24, 10)
    referers = ANALYTICS.top_referers(24, 6)

    active_blocks = list(
        BlockedIP.objects.filter(is_active=True)
        .filter(Q(expires_at__isnull=True) | Q(expires_at__gt=now))
        .order_by('-created_at')[:10]
    )

    recent_visitors = list(
        VisitorSession.objects.filter(is_bot=False)
        .order_by('-last_seen')[:12]
    )

    context = {
        'title': 'Live Visitors & Security',
        'summary': summary,
        'online_10m': online_10m,
        'countries': countries,
        'pages': pages,
        'referers': referers,
        'active_blocks': active_blocks,
        'recent_visitors': recent_visitors,
        'now': now,
    }
    return TemplateResponse(request, 'admin/security_dashboard.html', context)


def visitor_live_json_view(request):
    """JSON snapshot for the dashboard's auto-refresh."""
    if not (request.user.is_staff or request.user.is_superuser):
        return JsonResponse({'error': 'Unauthorized'}, status=403)
    return JsonResponse(ANALYTICS.summary())


def violation_activity_view(request):
    """Recent visitor sessions with high page/view velocity (potential floods)."""
    query = (
        VisitorSession.objects.filter(is_bot=False, page_views__gte=3)
        .order_by('-last_seen')[:25]
    )
    return JsonResponse(
        {'sessions': list(query.values('ip_address', 'city', 'country_code', 'page_views', 'last_seen'))},
    )