# webapp/api/fileman/views.py
# ============================================================
#                       DJANGO & DRF IMPORTS
# ============================================================
from django.http import HttpResponse
from django.core.paginator import Paginator
from django.shortcuts import get_object_or_404
from django.db.models import Case, When

from rest_framework import viewsets, status, filters
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 drf_spectacular.utils import (
    extend_schema,
    extend_schema_view,
    OpenApiExample
)

# ============================================================
#                       PROJECT IMPORTS
# ============================================================
from .pagination import FilemanPagination
from .serializers import (
    ContentSerializer,
    ContentCreateSerializer,
    PdfToDocxSerializer,
)
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

# ============================================================
#               PDF → DOCX + PDF → IMAGE IMPORTS
# ============================================================
from pdf2docx import Converter
from pdf2image import convert_from_path
from docx2pdf import convert  # pip install docx2pdf

import tempfile
import requests
import zipfile
import io
import os
import json 
from django.db import connection

# ===============================
# API: Konversi DOCX → PDF
# ===============================
from rest_framework.decorators import action
from rest_framework.parsers import MultiPartParser, FormParser
from rest_framework.response import Response
from rest_framework import status
from django.http import HttpResponse
import tempfile, os, subprocess, platform

from docx import Document
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import A4
from io import BytesIO

import os
import uuid
import tempfile
from django.conf import settings
from rest_framework.decorators import action
from rest_framework.response import Response
from slugify import slugify
from pdf2image import convert_from_path
from drf_spectacular.utils import extend_schema, OpenApiResponse
from .serializers import DocxToImageSerializer
from webapp.views.fileman.main import safe_docx2pdf
from django.db.models import Q, Value, F, CharField, TextField, IntegerField
from webapp.ml.e5large import embed_model

# 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 "{}"
        
def semantic_search_content_ids(query_text, allowed_types=None, top_k=100):

    if not query_text:
        return []

    if allowed_types is None:
        allowed_types = [
            "application/pdf",
            "image/jpeg",
            "image/png",
            "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
        ]

    q_emb = embed_model.encode([query_text], convert_to_numpy=True)[0]
    q_vec = "[" + ",".join(str(x) for x in q_emb.tolist()) + "]"

    ilike_clause = "ac.chunk_content ILIKE %s" 
    search_pattern = f"%{query_text}%"

    sql = f"""
        SELECT
            atch.pk_contents,
            ac.pk_attachment,
            (ae.embedding <-> %s::vector) AS semantic_dist,
            COALESCE(ts_rank_cd(ac.tsv, plainto_tsquery(%s)), 0) AS keyword_score,

            (
                0.6 * (ae.embedding <-> %s::vector)
                +
                0.4 * (1.0 - COALESCE(ts_rank_cd(ac.tsv, plainto_tsquery(%s)), 0))
            ) AS hybrid_score
        FROM attachments_chunks ac
        JOIN attachments_embeddings ae ON ae.pk_chunk = ac.pk
        JOIN attachments atch ON atch.pk = ac.pk_attachment
        WHERE atch.type = ANY(%s)
            AND (
                {ilike_clause}
                OR plainto_tsquery('indonesian', %s) @@ ac.tsv
                OR TRUE    -- supaya semantic tetap jalan
            )
        ORDER BY hybrid_score ASC
        LIMIT %s;
    """

    params = [q_vec, query_text,q_vec, query_text, allowed_types, search_pattern]

    # Add ILIKE '%keyword%' params
    # for w in keywords:
    #     params.append(f"%{w}%")

    params.append(query_text)  # for tsquery
    params.append(top_k)

    with connection.cursor() as cur:
        cur.execute(sql, params)
        rows = cur.fetchall()

    matched_map = {}
    
    for r in rows:
        c_pk = r[0] # Content PK
        a_pk = r[1] # Attachment PK
        
        if c_pk not in matched_map:
            matched_map[c_pk] = []
        
        # Masukkan attachment ID jika belum ada (hindari duplikat dari chunk berbeda di file sama)
        if a_pk not in matched_map[c_pk]:
            matched_map[c_pk].append(a_pk)

    # Return dictionary ini, bukan list biasa
    return matched_map


@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()
    serializer_class = ContentSerializer
    permission_classes = [IsAuthenticated]
    parser_classes = [MultiPartParser, FormParser]

    pagination_class = FilemanPagination

    # DRF Filters
    filter_backends = [filters.SearchFilter, filters.OrderingFilter]

    # 🔍 Search: judul, deskripsi, tag
    search_fields = [
        'judul',
        'deskripsi',
        'contenttag__tag__name',
    ]

    ordering_fields = ['created', 'updated', 'tahun']

    # ---------- LIST ----------
    def list(self, request, *args, **kwargs):
        queryset = main._get_queryset(request)

        kategori = request.GET.get('kategori')
        if kategori and kategori.isdigit():
            queryset = queryset.filter(pk_categories=int(kategori))

        skpd = request.GET.get('skpd')
        if skpd and skpd.isdigit():
            queryset = queryset.filter(pk_skpd_dg=int(skpd))

        tahun = request.GET.get('tahun')
        if tahun and tahun.isdigit():
            queryset = queryset.filter(tahun=int(tahun))

        queryset = self.filter_queryset(queryset)

        # ==========================
        # 🔥 SEMANTIC SEARCH FILTER
        # ==========================
        # f = Q()
        file_type = request.GET.get("type", "all")
        if file_type == "pdf":
            allowed_types = ["application/pdf"]
        elif file_type == "image":
            allowed_types = ["image/jpeg", "image/png"]
        else:
            allowed_types = [
                "application/pdf",
                "image/jpeg",
                "image/png",
                "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
                ]
            
        search_mode = request.GET.get("searchMode")
        search_text = request.GET.get("searchTerm")

        semantic_map = [] # Simpan variable ini
        is_semantic = False

        if search_mode == "semantic" and search_text:
            print("SEMANTIC API:", search_text)
            
            # Panggil fungsi semantic yang sudah direvisi (return dictionary)
            semantic_map = semantic_search_content_ids(search_text, allowed_types=allowed_types)
            content_ids = list(semantic_map.keys())

            print("DEBUG content_ids:", content_ids)
            if content_ids:
                # f &= Q(pk__in=content_ids)
                queryset = queryset.filter(pk__in=content_ids)
                # 2. 🔥 PRESERVE ORDER (Penting agar ranking AI tidak hancur)
                preserved = Case(*[When(pk=pk, then=pos) for pos, pk in enumerate(content_ids)])
                queryset = queryset.order_by(preserved)
            else:
                # hasil kosong
                # f &= Q(pk__in=[])
                queryset = queryset.none()
        else:
            # Jika bukan semantic, gunakan filter/order bawaan DRF
            queryset = self.filter_queryset(queryset)
        
        # ==================================================
        # 💉 INJECT CONTEXT KE SERIALIZER
        # ==================================================
        # Kita perlu kirim semantic_map ke serializer agar dia tahu file mana yang harus ditampilkan
        serializer_context = {
            "request": request,
            "is_semantic": is_semantic,
            "semantic_map": semantic_map
        }

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = ContentSerializer(page, many=True, context=serializer_context)
            return self.get_paginated_response(serializer.data)

        serializer = ContentSerializer(queryset, many=True, context=serializer_context)
        return Response(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_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)

            if files:
                upload.deploy(request, pk, files)
            else:
                Attachments.objects.create(pk_contents=contents, name="", path="", _type="", size=0, is_deleted=False)

            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', '{}'))

            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:
            files = request.FILES.getlist("attachments")
            if not files:
                return Response({"error": "Tidak ada file."}, status=400)
                        # 2. Upload file ke folder + DB
            uploaded = upload.deploy(request, pk, files)
            first = uploaded[0]
            attachment_id = first.pk

            # 3. QUICK EXTRACT (fast OCR)
            from webapp.ml_loader_svc import quick_extract_text
            extracted_text = quick_extract_text(first.path) or ""

            # 4. AI Category Prediction
            from webapp.ml_loader_svc import predict_category, suggest_tags
            category_id = predict_category(extracted_text)

            # Ambil nama kategori (sesuai tabel Categories)
            category_name = None
            if category_id:
                cat = Categories.objects.filter(pk=category_id).first()
                category_name = cat.text if cat else None

            # 5. Suggest Tags
            suggested_tags = suggest_tags(extracted_text) or []

            # 6. Response ke Android
            response_data = {
                "attachment_id": attachment_id,
                "quick_extract": extracted_text[:5000],  # batasi agar aman
                "predicted_category_id": category_id,
                "predicted_category_name": category_name,
                "suggested_tags": suggested_tags,
                "message": "Upload berhasil. Quick extract dan AI prediction tersedia."
            }

            # 7. Background job (OCR penuh + update DB)
            from webapp.tasks import process_attachment_fulltext
            process_attachment_fulltext.delay(attachment_id)

            return Response(response_data, status=status.HTTP_200_OK)
        except Exception as e:
            return Response({"error": str(e)}, status=400)

    @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)


    # ======================================================================
    #                       🚀 PDF → DOCX (NEW API)
    # ======================================================================
    @extend_schema(
        tags=["File Conversion"],
        summary="Konversi PDF ke DOCX",
        description="Kirim URL PDF dan dapatkan file DOCX hasil konversi.",
        request={
            "multipart/form-data": {
                "type": "object",
                "properties": {
                    "url": {
                        "type": "string",
                        "format": "uri"
                    }
                },
                "required": ["url"]
            }
        },
        responses={200: "File DOCX sebagai download"},
    )
    @action(detail=False, methods=["post"], url_path="convert/pdf-to-docx")
    def convert_pdf_to_docx(self, request):
        serializer = PdfToDocxSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        data = serializer.validated_data

        try:
            with tempfile.TemporaryDirectory() as tmpdir:

                # Jika input berupa URL
                if data.get("url"):
                    pdf_url = data["url"]
                    r = requests.get(pdf_url)
                    if r.status_code != 200:
                        return Response({"error": "Gagal mengunduh PDF dari URL"}, status=400)

                    pdf_path = os.path.join(tmpdir, "Dokumen Baru.pdf")
                    with open(pdf_path, "wb") as f:
                        f.write(r.content)

                # Jika input berupa File
                else:
                    file = data["file"]
                    pdf_path = os.path.join(tmpdir, file.name)
                    with open(pdf_path, "wb") as f:
                        for chunk in file.chunks():
                            f.write(chunk)

                # Konversi PDF → DOCX
                docx_path = pdf_path.replace(".pdf", ".docx")
                cv = Converter(pdf_path)
                cv.convert(docx_path)
                cv.close()

                # Return file DOCX
                with open(docx_path, "rb") as output:
                    response = HttpResponse(
                        output.read(),
                        content_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document"
                    )
                    response["Content-Disposition"] = (
                        f'attachment; filename="{os.path.basename(docx_path)}"'
                    )
                    return response

        except Exception as e:
            return Response({"error": str(e)}, status=500)


    # ======================================================================
    #                       🚀 PDF → IMAGE (NEW API)
    # ======================================================================
    @extend_schema(
        tags=["File Conversion"],
        summary="Konversi PDF ke Image",
        description="Kirim URL PDF dan dapatkan semua halaman PDF sebagai gambar JPG dalam satu ZIP.",
        request={
            "multipart/form-data": {
                "type": "object",
                "properties": {
                    "url": {"type": "string", "format": "uri"}
                },
                "required": ["url"]
            }
        },
        responses={200: "ZIP berisi JPG"},
    )
    @action(detail=False, methods=["post"], url_path="convert/pdf-to-image")
    def convert_pdf_to_image(self, request):
        from rest_framework.response import Response
        import requests, tempfile, os, io, zipfile
        from pdf2image import convert_from_path

        url = request.data.get("url")
        if not url:
            return Response({"error": "Harus mengirim URL PDF"}, status=400)

        try:
            with tempfile.TemporaryDirectory() as tmpdir:

                # Unduh PDF dari URL
                r = requests.get(url)
                if r.status_code != 200:
                    return Response({"error": "Gagal mengunduh PDF dari URL"}, status=400)

                pdf_path = os.path.join(tmpdir, "Dokumen_Baru.pdf")
                with open(pdf_path, "wb") as f:
                    f.write(r.content)

                # Konversi PDF → Image (JPG)
                pages = convert_from_path(pdf_path, dpi=200)
                image_files = []

                for i, page in enumerate(pages, start=1):
                    img_path = os.path.join(tmpdir, f"page_{i}.jpg")
                    page.save(img_path, "JPEG")
                    image_files.append(img_path)

                # Buat ZIP
                zip_buffer = io.BytesIO()
                with zipfile.ZipFile(zip_buffer, "w") as zip_file:
                    for img_path in image_files:
                        zip_file.write(img_path, os.path.basename(img_path))
                zip_buffer.seek(0)

                response = HttpResponse(zip_buffer, content_type="application/zip")
                response["Content-Disposition"] = 'attachment; filename="PDF_images.zip"'
                return response

        except Exception as e:
            return Response({"error": str(e)}, status=500)
        
    # ======================================================================
    #                       🚀 IMAGE → PDF (NEW API)
    # ======================================================================
    @extend_schema(
        tags=["File Conversion"],
        summary="Konversi Image ke PDF",
        description="Kirim satu atau beberapa file gambar dan gabungkan menjadi PDF.",
        request={
            "multipart/form-data": {
                "type": "object",
                "properties": {
                    "images": {
                        "type": "array",
                        "items": {"type": "string", "format": "binary"}
                    }
                },
                "required": ["images"]
            }
        },
        responses={200: "File PDF sebagai download"},
    )
    @action(detail=False, methods=["post"], url_path="convert/images-to-pdf")
    def convert_images_to_pdf(self, request):
        from .serializers import ImagesToPdfSerializer
        from reportlab.pdfgen import canvas
        from reportlab.lib.pagesizes import letter
        from PIL import Image
        import io, tempfile, os
        from django.http import HttpResponse

        serializer = ImagesToPdfSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        images = serializer.validated_data.get("images")

        try:
            with tempfile.TemporaryDirectory() as tmpdir:
                pdf_path = os.path.join(tmpdir, "Hasil.pdf")

                # Konversi gambar ke PDF
                pil_images = [Image.open(img) for img in images]
                pil_images_rgb = []

                for img in pil_images:
                    # Pastikan semua gambar dalam mode RGB
                    if img.mode != 'RGB':
                        img = img.convert('RGB')
                    pil_images_rgb.append(img)

                # Simpan semua gambar menjadi satu PDF
                pil_images_rgb[0].save(
                    pdf_path,
                    save_all=True,
                    append_images=pil_images_rgb[1:],
                    format='PDF'
                )

                # Return file PDF
                with open(pdf_path, "rb") as f:
                    response = HttpResponse(
                        f.read(),
                        content_type="application/pdf"
                    )
                    response["Content-Disposition"] = 'attachment; filename="Hasil.pdf"'
                    return response

        except Exception as e:
            return Response({"error": str(e)}, status=500)
        
    # ======================================================================
    #                       🚀 IMAGE → DOCX (NEW API)
    # ======================================================================
    @extend_schema(
        tags=["File Conversion"],
        summary="Konversi Image ke DOCX",
        description="Kirim satu atau beberapa gambar lalu akan dimasukkan ke dalam sebuah file DOCX.",
        request={
            "multipart/form-data": {
                "type": "object",
                "properties": {
                    "images": {
                        "type": "array",
                        "items": {"type": "string", "format": "binary"}
                    }
                },
                "required": ["images"]
            }
        },
        responses={200: "File DOCX sebagai download"},
    )
    @action(detail=False, methods=["post"], url_path="convert/images-to-docx")
    def convert_images_to_docx(self, request):
        from .serializers import ImagesToDocxSerializer
        from docx import Document
        import tempfile, os
        from django.http import HttpResponse

        serializer = ImagesToDocxSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        images = serializer.validated_data.get("images")

        try:
            with tempfile.TemporaryDirectory() as tmpdir:
                # Buat dokumen Word baru
                doc = Document()

                # Tambahkan setiap gambar
                for img in images:
                    img_path = os.path.join(tmpdir, img.name)
                    with open(img_path, "wb") as f:
                        for chunk in img.chunks():
                            f.write(chunk)

                    doc.add_picture(img_path)
                    doc.add_page_break()  # pisahkan halaman setiap gambar

                docx_path = os.path.join(tmpdir, "Hasil_Gambar.docx")
                doc.save(docx_path)

                # Return DOCX
                with open(docx_path, "rb") as f:
                    response = HttpResponse(
                        f.read(),
                        content_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document"
                    )
                    response["Content-Disposition"] = 'attachment; filename="Hasil_Gambar.docx"'
                    return response

        except Exception as e:
            return Response({"error": str(e)}, status=500)
        
    # ======================================================================
    #                       🚀 DOCX → PDF (NEW API)
    # ======================================================================
    @extend_schema(
        tags=["File Conversion"],
        summary="Konversi DOCX ke PDF",
        description="Kirim file DOCX dan dapatkan file PDF hasil konversi.",
        request={
            "multipart/form-data": {
                "type": "object",
                "properties": {
                    "docx_file": {
                        "type": "string",
                        "format": "binary"
                    }
                },
                "required": ["docx_file"]
            }
        },
        responses={200: "File PDF sebagai download"},
    )
    @action(detail=False, methods=["post"], url_path="convert/docx-to-pdf")
    def convert_docx_to_pdf(self, request):
        docx_file = request.FILES.get("docx_file")
        if not docx_file:
            return Response({"error": "Harus mengirim file DOCX"}, status=400)

        try:
            # Baca DOCX
            doc = Document(docx_file)
            buffer = BytesIO()
            c = canvas.Canvas(buffer, pagesize=A4)
            width, height = A4
            y = height - 50

            for para in doc.paragraphs:
                text = para.text
                if text:
                    c.drawString(50, y, text)
                    y -= 15
                    if y < 50:
                        c.showPage()
                        y = height - 50

            c.save()
            buffer.seek(0)

            response = HttpResponse(
                buffer.read(),
                content_type="application/pdf"
            )
            response["Content-Disposition"] = f'attachment; filename="{os.path.splitext(docx_file.name)[0]}.pdf"'
            return response

        except Exception as e:
            return Response({"error": str(e)}, status=500)


    # ======================================================================
    #                       🚀 DOCX → IMAGE (ZIP)
    # ======================================================================
    @extend_schema(
        tags=["File Conversion"],
        summary="Konversi DOCX ke Image",
        description="Upload file DOCX dan hasilkan ZIP berisi gambar JPG per halaman.",
        request={
            "multipart/form-data": {
                "type": "object",
                "properties": {
                    "file": {"type": "string", "format": "binary"},
                },
                "required": ["file"],
            }
        },
        responses={200: "ZIP berisi JPG"},
    )
    @action(detail=False, methods=["post"], url_path="convert/docx-to-image")
    def convert_docx_to_image(self, request):

        serializer = DocxToImageSerializer(data=request.FILES)
        serializer.is_valid(raise_exception=True)

        docx_file = serializer.validated_data["file"]

        try:
            with tempfile.TemporaryDirectory() as tmpdir:

                # --- nama unik
                base = slugify(os.path.splitext(docx_file.name)[0])
                uniq = uuid.uuid4().hex[:8]

                docx_path = os.path.join(tmpdir, f"{base}-{uniq}.docx")
                pdf_path = docx_path.replace(".docx", ".pdf")

                # --- simpan DOCX
                with open(docx_path, "wb") as f:
                    for chunk in docx_file.chunks():
                        f.write(chunk)

                # --- convert DOCX → PDF (fungsi aman server)
                safe_docx2pdf(docx_path, pdf_path)

                if not os.path.exists(pdf_path):
                    return Response({"error": "Gagal membuat PDF"}, status=500)

                # --- convert PDF → IMG
                images = convert_from_path(pdf_path, dpi=200)

                # --- buat ZIP buffer
                zip_buffer = io.BytesIO()
                with zipfile.ZipFile(zip_buffer, "w") as zip_file:
                    for i, img in enumerate(images, start=1):
                        img_filename = f"page_{i}.jpg"
                        img_path = os.path.join(tmpdir, img_filename)
                        img.save(img_path, "JPEG")
                        zip_file.write(img_path, img_filename)

                zip_buffer.seek(0)

                # --- return ZIP
                response = HttpResponse(zip_buffer, content_type="application/zip")
                response["Content-Disposition"] = (
                    f'attachment; filename="{base}_{uniq}_images.zip"'
                )

                return response

        except Exception as e:
            return Response({"error": str(e)}, status=500)
        
