# webapp/api/fileman/views.py
from rest_framework import viewsets, status
from rest_framework.response import Response
from rest_framework.decorators import action
from rest_framework.parsers import MultiPartParser, FormParser
from rest_framework.permissions import IsAuthenticated
from django.core.paginator import Paginator
from django.shortcuts import get_object_or_404
import json

from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiExample

from webapp.views.fileman import main, upload
from webapp.models.koleksi import (
    Contents, Categories, Attachments, ContentVersion, ContentVersionAttachment,
    ContentTag, Tag
)
from webapp.models import master as m_master
from .serializers import ContentSerializer, ContentCreateSerializer

# Helper
def get_val(obj, field, default=None):
    if obj is None:
        return default
    if isinstance(obj, dict):
        return obj.get(field, default)
    return getattr(obj, field, default)

def fix_json_attributes(attr_raw):
    if not attr_raw:
        return "{}"
    if isinstance(attr_raw, dict):
        return json.dumps(attr_raw)
    try:
        json.loads(attr_raw)
        return attr_raw
    except json.JSONDecodeError:
        attr_fixed = str(attr_raw).replace("'", '"')
        try:
            json.loads(attr_fixed)
            return attr_fixed
        except json.JSONDecodeError:
            return "{}"


@extend_schema_view(
    list=extend_schema(
        tags=["Content Management"],
        summary="Daftar konten",
        description="Ambil daftar konten dengan pagination, bisa filter kategori, tahun, SKPD, tag.",
        responses={200: ContentSerializer(many=True)},
    ),
    retrieve=extend_schema(
        tags=["Content Management"],
        summary="Detail konten",
        description="Ambil detail konten termasuk tags, attachments, dan versi.",
        responses={200: ContentSerializer},
    ),
    create=extend_schema(
        tags=["Content Management"],
        summary="Buat konten baru",
        description="Buat konten lengkap dengan tags, attachments, versi pertama.",
        request=ContentCreateSerializer,
        responses={201: OpenApiExample(
            "Contoh sukses",
            value={
                "id": 1,
                "judul": "Judul Dokumen",
                "deskripsi": "Deskripsi dokumen",
                "status_konten": "public",
                "pk_categories": 3,
                "category": "SP2D",
                "attributes": {},
                "tags": ["tag1","tag2"],
                "attachments": [],
                "created": "2025-09-11T00:00:00Z",
                "updated": "2025-09-11T00:00:00Z",
            },
            response_only=True
        )},
    ),
    update=extend_schema(
        tags=["Content Management"],
        summary="Update konten",
        description="Update konten + generate versi baru, termasuk tags dan file baru.",
        request=ContentCreateSerializer,
        responses={200: ContentSerializer},
    ),
    destroy=extend_schema(
        tags=["Content Management"],
        summary="Soft delete konten",
        description="Backup versi aktif, soft delete attachments, tandai konten sebagai '[DELETED]'.",
        responses={204: OpenApiExample(
            "Soft delete sukses",
            value={"detail": "Konten berhasil dihapus."},
            response_only=True
        )},
    ),
    permanent_delete=extend_schema(
        tags=["Content Management"],
        summary="Hard delete konten",
        description="Hapus permanen konten beserta semua attachment dari disk & DB.",
        responses={204: OpenApiExample(
            "Hard delete sukses",
            value={"detail": "Konten dan attachment berhasil dihapus."},
            response_only=True
        )},
    ),
    attachments=extend_schema(
        tags=["Content Management"],
        summary="Upload attachment",
        description="Upload satu atau lebih file ke konten.",
        request={"multipart/form-data": {"type": "object", "properties": {"attachments": {"type": "array", "items": {"type": "string", "format": "binary"}}}}},
        responses={201: ContentSerializer},
    ),
    soft_delete_attachment=extend_schema(
        tags=["Content Management"],
        summary="Soft delete attachment",
        description="Soft delete attachment berdasarkan ID.",
        responses={204: OpenApiExample(
            "Soft delete attachment sukses",
            value={"detail": "Attachment berhasil dihapus."},
            response_only=True
        )},
    ),
    hard_delete_attachment=extend_schema(
        tags=["Content Management"],
        summary="Hard delete attachment",
        description="Hapus attachment permanen dari disk & DB.",
        responses={204: OpenApiExample(
            "Hard delete attachment sukses",
            value={"detail": "Attachment berhasil dihapus."},
            response_only=True
        )},
    ),
    rollback=extend_schema(
        tags=["Content Management"],
        summary="Rollback ke versi lama",
        description="Kembalikan konten ke versi sebelumnya, backup versi aktif dulu.",
        responses={200: ContentSerializer},
    ),
)
class ContentViewSet(viewsets.ModelViewSet):
    queryset = Contents.objects.all()
    parser_classes = [MultiPartParser, FormParser]
    serializer_class = ContentSerializer
    permission_classes = [IsAuthenticated]

    # ---------- LIST ----------
    def list(self, request):
        query = main._get_queryset(request)
        page = int(request.GET.get("page", 1))
        page_size = int(request.GET.get("page_size", 8))
        paginator = Paginator(query, page_size)
        paging = paginator.get_page(page)

        serializer = ContentSerializer(paging, many=True, context={"request": request})
        next_page = paging.next_page_number() if paging.has_next() else None
        prev_page = paging.previous_page_number() if paging.has_previous() else None
        return Response({
            "count": paginator.count,
            "page": page,
            "next": f"{request.build_absolute_uri('?page=')}{next_page}" if next_page else None,
            "previous": f"{request.build_absolute_uri('?page=')}{prev_page}" if prev_page else None,
            "page_size": page_size,
            "results": serializer.data
        })

    # ---------- RETRIEVE ----------
    def retrieve(self, request, pk=None):
        content = get_object_or_404(Contents, pk=pk)
        serializer = ContentSerializer(content, context={"request": request})
        return Response(serializer.data)

    # ---------- CREATE ----------
    def create(self, request):
        serializer = ContentCreateSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        values = serializer.validated_data
        try:
            files = request.FILES.getlist('attachments')
            values['tahun'] = m_master.Ta._read_one(asis=1, tahun=values['tahun'])
            values['pk_categories'] = Categories._read_one(asis=1, pk=values['pk_categories'])
            values['pk_skpd_dg'] = m_master.Skpddg._read_one(asis=1, pk=values['pk_skpd_dg'])
            values['user_input'] = getattr(request.user, 'pk', None) or request.session.get('pk')
            values['status_konten'] = values.get('status_konten') or 'public'
            values['attributes'] = fix_json_attributes(values.get('attributes', '{}'))

            contents = Contents._create(**values)
            pk = contents.pk

            # tags
            tags_raw = values.get('tags', '') or ''
            tags = [t.strip().lower() for t in tags_raw.split(',') if t.strip()]
            for tag_name in tags:
                tag, _ = Tag.objects.get_or_create(name=tag_name)
                ContentTag.objects.get_or_create(content=contents, tag=tag)

            # attachments
            if files:
                upload.deploy(request, pk, files)
            else:
                Attachments.objects.create(pk_contents=contents, name="", path="", _type="", size=0, is_deleted=False)

            # content version
            tag_objs = ContentTag.objects.filter(content_id=pk).select_related('tag')
            tag_names = [t.tag.name for t in tag_objs if t.tag.name] or []

            version = ContentVersion.objects.create(
                pk_contents=pk,
                judul=values.get('judul'),
                deskripsi=values.get('deskripsi'),
                attributes=values['attributes'],
                tahun=values['tahun'].tahun,
                pk_categories=values['pk_categories'].pk,
                pk_skpd_dg=values['pk_skpd_dg'].pk,
                status_konten=values['status_konten'],
                user_input=values['user_input'],
                tags=tag_names,
            )

            attachments_qs = Attachments.objects.filter(pk_contents=pk, is_deleted=False)
            for a in attachments_qs:
                ContentVersionAttachment.objects.create(
                    pk_version=version,
                    name=a.name or "",
                    path=a.path or "",
                    content_type=a._type or "",
                    size=a.size or 0
                )

            output = ContentSerializer(contents, context={"request": request})
            return Response(output.data, status=status.HTTP_201_CREATED)

        except Exception as e:
            return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)

    # ---------- UPDATE ----------
    def update(self, request, pk=None):
        try:
            tags_raw = request.data.get('tags', '') or ''
            tags = [t.strip().lower() for t in tags_raw.split(',') if t.strip()]
            tag_names = tags.copy()

            old = Contents._read_one(pk=pk)
            old_attr_fixed = fix_json_attributes(old.get('attributes', '{}'))

            # backup versi lama
            version = ContentVersion.objects.create(
                pk_contents=pk,
                judul=old.get('judul'),
                deskripsi=old.get('deskripsi'),
                attributes=old_attr_fixed,
                tahun=old.get('tahun'),
                pk_categories=old.get('pk_categories'),
                pk_skpd_dg=old.get('pk_skpd_dg'),
                status_konten=old.get('status_konten'),
                user_input=getattr(request.user, 'pk', None) or request.session.get('pk'),
                tags=tag_names,
            )

            old_files = Attachments.objects.filter(pk_contents=pk)
            for a in old_files:
                if not getattr(a, 'is_deleted', False):
                    ContentVersionAttachment.objects.create(
                        pk_version=version,
                        name=get_val(a, 'name'),
                        path=get_val(a, 'path'),
                        content_type=get_val(a, '_type'),
                        size=get_val(a, 'size')
                    )

            pending_ids = request.data.get('pending_delete_ids', '') or ''
            for att_id in str(pending_ids).split(','):
                if att_id.strip().isdigit():
                    Attachments.objects.filter(pk=int(att_id)).update(is_deleted=True)

            values = {k: v for k, v in request.data.items() if k not in ['csrfmiddlewaretoken', 'pending_delete_ids', 'tags']}
            values['tahun'] = m_master.Ta._read_one(asis=1, tahun=values.get('tahun'))
            values['pk_categories'] = Categories._read_one(asis=1, pk=values.get('pk_categories'))
            values['pk_skpd_dg'] = m_master.Skpddg._read_one(asis=1, pk=values.get('pk_skpd_dg'))
            values['status_konten'] = request.data.get('status_konten') or values.get('status_konten')
            values['attributes'] = fix_json_attributes(values.get('attributes', '{}'))

            Contents._update(values, {'pk': pk})

            ContentTag.objects.filter(content_id=pk).delete()
            for tag_name in tags:
                tag, _ = Tag.objects.get_or_create(name=tag_name)
                ContentTag.objects.get_or_create(content_id=pk, tag=tag)

            attachments_files = request.FILES.getlist('attachments')
            if attachments_files:
                upload.deploy(request, pk, attachments_files)

            content = Contents.objects.get(pk=pk)
            serializer = ContentSerializer(content, context={"request": request})
            return Response(serializer.data, status=status.HTTP_200_OK)

        except Exception as e:
            return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)

    # ---------- SOFT DELETE ----------
    def destroy(self, request, pk=None):
        try:
            content = Contents.objects.get(pk=pk)

            raw_attr = content.attributes or "{}"
            attributes_fixed = fix_json_attributes(raw_attr)
            tag_qs = ContentTag.objects.filter(content_id=pk).select_related('tag')
            tag_names = [t.tag.name for t in tag_qs if t.tag.name]

            backup_version = ContentVersion.objects.create(
                pk_contents=pk,
                judul=content.judul,
                deskripsi=content.deskripsi,
                attributes=attributes_fixed,
                tahun=get_val(content, 'tahun'),
                pk_categories=get_val(content, 'pk_categories'),
                pk_skpd_dg=get_val(content, 'pk_skpd_dg'),
                status_konten=content.status_konten,
                user_input=request.session.get("pk") or getattr(request.user, "pk", None),
                tags=tag_names,
            )

            active_files = Attachments.objects.filter(pk_contents=pk, is_deleted=False)
            for a in active_files:
                ContentVersionAttachment.objects.create(
                    pk_version=backup_version,
                    name=a.name,
                    path=a.path,
                    content_type=a._type,
                    size=a.size
                )

            Attachments.objects.filter(pk_contents=pk, is_deleted=False).update(is_deleted=True)
            content.judul = f"[DELETED] {content.judul}"
            content.deskripsi = "[Content deleted]"
            content.save(update_fields=["judul", "deskripsi"])
            return Response(status=status.HTTP_204_NO_CONTENT)
        except Contents.DoesNotExist:
            return Response({"error": "Content not found"}, status=status.HTTP_404_NOT_FOUND)
        except Exception as e:
            return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)

    # ---------- HARD DELETE ----------
    @action(detail=True, methods=["delete"], url_path="permanent")
    def permanent_delete(self, request, pk=None):
        try:
            content = get_object_or_404(Contents, pk=pk)
            attachments = Attachments.objects.filter(pk_contents=pk)
            for att in attachments:
                upload.destroy_one_permanent(att.pk)
            content.delete()
            return Response(status=status.HTTP_204_NO_CONTENT)
        except Exception as e:
            return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)

    # ---------- ATTACHMENTS ----------
    @action(detail=True, methods=["post"])
    def attachments(self, request, pk=None):
        try:
            result = upload.deploy(request, pk)
            return Response(result, status=status.HTTP_201_CREATED)
        except Exception as e:
            return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)

    @action(detail=True, methods=["delete"], url_path="attachments/(?P<att_id>[^/.]+)")
    def soft_delete_attachment(self, request, pk=None, att_id=None):
        Attachments.objects.filter(pk=int(att_id), pk_contents=pk).update(is_deleted=True)
        return Response(status=status.HTTP_204_NO_CONTENT)

    @action(detail=True, methods=["delete"], url_path="attachments/(?P<att_id>[^/.]+)/permanent")
    def hard_delete_attachment(self, request, pk=None, att_id=None):
        try:
            upload.destroy_one_permanent(pk=int(att_id))
            return Response(status=status.HTTP_204_NO_CONTENT)
        except Exception as e:
            return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)

    # ---------- ROLLBACK ----------
    @action(detail=True, methods=["post"], url_path="rollback/(?P<version_id>[^/.]+)")
    def rollback(self, request, pk=None, version_id=None):
        try:
            version = ContentVersion.objects.get(pk=version_id, pk_contents=pk)

            current = Contents._read_one(pk=pk)
            raw_attr = current.get('attributes', '{}')
            attributes_fixed = fix_json_attributes(raw_attr)
            tag_qs = ContentTag.objects.filter(content_id=pk).select_related('tag')
            tag_names = [t.tag.name for t in tag_qs if t.tag.name]

            backup_version = ContentVersion.objects.create(
                pk_contents=pk,
                judul=current.get('judul'),
                deskripsi=current.get('deskripsi'),
                attributes=attributes_fixed,
                tahun=current.get('tahun'),
                pk_categories=current.get('pk_categories'),
                pk_skpd_dg=current.get('pk_skpd_dg'),
                status_konten=current.get('status_konten'),
                user_input=request.session.get("pk") or getattr(request.user, "pk", None),
                tags=tag_names,
            )

            Attachments.objects.filter(pk_contents=pk, is_deleted=False).update(is_deleted=True)

            values = {
                "judul": version.judul,
                "deskripsi": version.deskripsi,
                "attributes": fix_json_attributes(version.attributes),
                "tahun": m_master.Ta._read_one(asis=1, tahun=version.tahun),
                "pk_categories": Categories._read_one(asis=1, pk=version.pk_categories),
                "pk_skpd_dg": m_master.Skpddg._read_one(asis=1, pk=version.pk_skpd_dg),
                "status_konten": version.status_konten,
                "user_input": request.session.get("pk") or getattr(request.user, "pk", None),
            }
            Contents._update(values, {"pk": pk})

            ContentTag.objects.filter(content_id=pk).delete()
            for tag_name in version.tags or []:
                tag, _ = Tag.objects.get_or_create(name=tag_name)
                ContentTag.objects.get_or_create(content_id=pk, tag=tag)

            old_attachments = ContentVersionAttachment.objects.filter(pk_version=version_id)
            for va in old_attachments:
                Attachments.objects.create(
                    pk_contents=pk,
                    name=va.name,
                    path=va.path,
                    _type=va.content_type,
                    size=va.size,
                    is_deleted=False
                )

            content = Contents.objects.get(pk=pk)
            serializer = ContentSerializer(content, context={"request": request})
            return Response(serializer.data, status=status.HTTP_200_OK)
        except Exception as e:
            return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)