import json
import logging
from pathlib import Path

from cache_memoize import cache_memoize
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import ensure_csrf_cookie
from drf_spectacular.utils import extend_schema
from dynamic_preferences.api import viewsets as preferences_viewsets
from dynamic_preferences.api.serializers import GlobalPreferenceSerializer
from dynamic_preferences.registries import global_preferences_registry
from rest_framework import generics, views
from rest_framework.renderers import JSONRenderer
from rest_framework.response import Response

from funkwhale_api import __version__ as funkwhale_version
from funkwhale_api.common import preferences
from funkwhale_api.common.renderers import ActivityStreamRenderer
from funkwhale_api.federation.actors import get_service_actor
from funkwhale_api.federation.models import Domain
from funkwhale_api.moderation.models import REPORT_TYPES
from funkwhale_api.music.utils import SUPPORTED_EXTENSIONS
from funkwhale_api.users.oauth import permissions as oauth_permissions

from . import serializers, stats

NODEINFO_2_CONTENT_TYPE = "application/json; profile=http://nodeinfo.diaspora.software/ns/schema/2.0#; charset=utf-8"  # noqa


logger = logging.getLogger(__name__)


class AdminSettings(preferences_viewsets.GlobalPreferencesViewSet):
    pagination_class = None
    permission_classes = [oauth_permissions.ScopePermission]
    required_scope = "instance:settings"


class InstanceSettings(generics.GenericAPIView):
    permission_classes = []
    authentication_classes = []
    serializer_class = GlobalPreferenceSerializer

    def get_queryset(self):
        manager = global_preferences_registry.manager()
        manager.all()
        all_preferences = manager.model.objects.all().order_by("section", "name")
        api_preferences = [
            p for p in all_preferences if getattr(p.preference, "show_in_api", False)
        ]
        return api_preferences

    @extend_schema(operation_id="get_instance_settings")
    def get(self, request):
        queryset = self.get_queryset()
        data = GlobalPreferenceSerializer(queryset, many=True).data
        return Response(data, status=200)


@method_decorator(ensure_csrf_cookie, name="dispatch")
class NodeInfo20(views.APIView):
    permission_classes = []
    authentication_classes = []
    serializer_class = serializers.NodeInfo20Serializer
    renderer_classes = (JSONRenderer,)

    @extend_schema(
        responses=serializers.NodeInfo20Serializer, operation_id="getNodeInfo20"
    )
    def get(self, request):
        pref = preferences.all()
        if (
            pref["moderation__allow_list_public"]
            and pref["moderation__allow_list_enabled"]
        ):
            allowed_domains = list(
                Domain.objects.filter(allowed=True)
                .order_by("name")
                .values_list("name", flat=True)
            )
        else:
            allowed_domains = None

        data = {
            "software": {"version": funkwhale_version},
            "services": {"inbound": ["atom1.0"], "outbound": ["atom1.0"]},
            "preferences": pref,
            "stats": cache_memoize(600, prefix="memoize:instance:stats")(stats.get)()
            if pref["instance__nodeinfo_stats_enabled"]
            else None,
            "actorId": get_service_actor().fid,
            "supportedUploadExtensions": SUPPORTED_EXTENSIONS,
            "allowed_domains": allowed_domains,
            "report_types": [
                {
                    "type": t,
                    "label": l,
                    "anonymous": t
                    in pref.get("moderation__unauthenticated_report_types"),
                }
                for t, l in REPORT_TYPES
            ],
            "endpoints": {},
        }

        if not pref.get("common__api_authentication_required"):
            if pref.get("instance__nodeinfo_stats_enabled"):
                data["endpoints"]["knownNodes"] = reverse(
                    "api:v1:federation:domains-list"
                )
            if pref.get("federation__public_index"):
                data["endpoints"]["libraries"] = reverse(
                    "federation:index:index-libraries"
                )
                data["endpoints"]["channels"] = reverse(
                    "federation:index:index-channels"
                )
        serializer = self.serializer_class(data)
        return Response(
            serializer.data, status=200, content_type=NODEINFO_2_CONTENT_TYPE
        )


class NodeInfo21(NodeInfo20):
    serializer_class = serializers.NodeInfo21Serializer

    @extend_schema(
        responses=serializers.NodeInfo20Serializer, operation_id="getNodeInfo20"
    )
    def get(self, request):
        pref = preferences.all()
        if (
            pref["moderation__allow_list_public"]
            and pref["moderation__allow_list_enabled"]
        ):
            allowed_domains = list(
                Domain.objects.filter(allowed=True)
                .order_by("name")
                .values_list("name", flat=True)
            )
        else:
            allowed_domains = None

        data = {
            "software": {"version": funkwhale_version},
            "services": {"inbound": ["atom1.0"], "outbound": ["atom1.0"]},
            "preferences": pref,
            "stats": cache_memoize(600, prefix="memoize:instance:stats")(stats.get)()
            if pref["instance__nodeinfo_stats_enabled"]
            else None,
            "actorId": get_service_actor().fid,
            "supportedUploadExtensions": SUPPORTED_EXTENSIONS,
            "allowed_domains": allowed_domains,
            "languages": pref.get("moderation__languages"),
            "location": pref.get("instance__location"),
            "content": cache_memoize(600, prefix="memoize:instance:content")(
                stats.get_content
            )()
            if pref["instance__nodeinfo_stats_enabled"]
            else None,
            "features": [
                "channels",
                "podcasts",
            ],
        }

        if not pref.get("common__api_authentication_required"):
            data["features"].append("anonymousCanListen")

        if pref.get("federation__enabled"):
            data["features"].append("federation")

        if pref.get("music__only_allow_musicbrainz_tagged_files"):
            data["features"].append("onlyMbidTaggedContent")

        serializer = self.serializer_class(data)
        return Response(
            serializer.data, status=200, content_type=NODEINFO_2_CONTENT_TYPE
        )


PWA_MANIFEST_PATH = Path(__file__).parent / "pwa-manifest.json"
PWA_MANIFEST: dict = json.loads(PWA_MANIFEST_PATH.read_text(encoding="utf-8"))


class SpaManifest(generics.GenericAPIView):
    permission_classes = []
    authentication_classes = []
    serializer_class = serializers.SpaManifestSerializer
    renderer_classes = [ActivityStreamRenderer]

    @extend_schema(operation_id="get_spa_manifest")
    def get(self, request):
        manifest = PWA_MANIFEST.copy()
        instance_name = preferences.get("instance__name")
        if instance_name:
            manifest["short_name"] = instance_name
            manifest["name"] = instance_name
        instance_description = preferences.get("instance__short_description")
        if instance_description:
            manifest["description"] = instance_description
        serializer = self.get_serializer(manifest)
        return Response(
            serializer.data, status=200, content_type="application/manifest+json"
        )
