import json

from django.apps import apps as django_apps
from django.contrib.auth.mixins import UserPassesTestMixin
from django.http import JsonResponse
from django.views import View
from django.shortcuts import render, redirect
from django.urls import reverse

from .inspector import get_app_models, get_diagram_data
from .models import DiagramPreferences, _default_excluded_apps


class _StaffRequired(UserPassesTestMixin):
    def test_func(self):
        return self.request.user.is_active and self.request.user.is_staff


def _get_prefs(user):
    try:
        p = user.lumen_preferences
        return {'selected_apps': p.selected_apps, 'excluded_apps': p.excluded_apps, 'exclude_models': p.exclude_models, 'routing_style': p.routing_style, 'show_only_managed': p.show_only_managed, 'show_inheritance': p.show_inheritance}
    except DiagramPreferences.DoesNotExist:
        return {'selected_apps': [], 'excluded_apps': _default_excluded_apps(), 'exclude_models': [], 'routing_style': 'curved', 'show_only_managed': False, 'show_inheritance': False}


class DiagramView(_StaffRequired, View):
    def get(self, request):
        prefs = _get_prefs(request.user)
        models = get_app_models(excluded=prefs['excluded_apps'], exclude_model_patterns=prefs['exclude_models'])
        data = get_diagram_data(models)
        return render(request, 'django_lumen/diagram.html', {
            'diagram_data': json.dumps(data),
            'prefs': json.dumps(prefs),
        })


class SavePreferencesView(_StaffRequired, View):
    _allowed = {'selected_apps', 'excluded_apps', 'exclude_models', 'routing_style', 'show_only_managed', 'show_inheritance'}

    def post(self, request):
        data = json.loads(request.body)
        defaults = {k: v for k, v in data.items() if k in self._allowed}
        DiagramPreferences.objects.update_or_create(user=request.user, defaults=defaults)
        return JsonResponse({'ok': True})


class SettingsView(_StaffRequired, View):
    def _all_app_labels(self):
        return sorted({m._meta.app_label for m in django_apps.get_models()})

    def get(self, request):
        prefs = _get_prefs(request.user)
        return render(request, 'django_lumen/settings.html', {
            'all_apps': self._all_app_labels(),
            'excluded_apps': prefs['excluded_apps'],
            'exclude_models': json.dumps(prefs['exclude_models']),
            'show_only_managed': prefs['show_only_managed'],
            'show_inheritance': prefs['show_inheritance'],
        })

    def post(self, request):
        valid = set(self._all_app_labels())
        excluded_apps = [a for a in request.POST.getlist('excluded_apps') if a in valid]
        try:
            patterns = json.loads(request.POST.get('exclude_models', '[]'))
            if not isinstance(patterns, list):
                patterns = []
            patterns = [str(p) for p in patterns if p]
        except (ValueError, TypeError):
            patterns = []
        DiagramPreferences.objects.update_or_create(
            user=request.user,
            defaults={
                'excluded_apps': excluded_apps,
                'exclude_models': patterns,
                'show_only_managed': request.POST.get('show_only_managed') == 'on',
                'show_inheritance': request.POST.get('show_inheritance') == 'on',
            },
        )
        return redirect(reverse('django-lumen-diagram'))


class ExcludedAppsView(_StaffRequired, View):
    """Legacy view — redirects to the unified Settings page."""
    def get(self, request):
        return redirect(f"{reverse('django-lumen-settings')}?tab=apps")

    def post(self, request):
        return redirect(f"{reverse('django-lumen-settings')}?tab=apps")


class HelpView(_StaffRequired, View):
    def get(self, request):
        return render(request, 'django_lumen/help.html')
