"""
Router: OCR Tester
Deskripsi : Endpoint debugging khusus untuk menguji pipeline YOLO (deteksi kotak FAT)
            dan PaddleOCR (pengenalan teks) secara transparan.
            Endpoint ini BUKAN untuk produksi — semua detail intermediate ditampilkan.
"""

import os
import io
import time
import torch
import numpy as np
from datetime import datetime
from PIL import Image, ImageDraw, ImageFont
from fastapi import APIRouter, File, UploadFile, HTTPException
from fastapi.responses import JSONResponse

router = APIRouter(prefix="/dev", tags=["[DEV] OCR Tester"])


def _draw_boxes_on_image(image: Image.Image, boxes_info: list[dict]) -> Image.Image:
    """Menggambar bounding box hasil deteksi YOLO pada gambar asli."""
    draw = ImageDraw.Draw(image)
    
    # Coba muat font Arial berukuran besar (40), fallback ke font default jika gagal
    try:
        font = ImageFont.truetype("arial.ttf", 40)
    except IOError:
        font = ImageFont.load_default()
        
    for idx, box in enumerate(boxes_info):
        x1, y1, x2, y2 = box["x_min"], box["y_min"], box["x_max"], box["y_max"]
        color = "red" if idx == 0 else "orange"  # Kotak terbaik = merah
        
        # Garis kotak SUPER tebal
        draw.rectangle([(x1, y1), (x2, y2)], outline=color, width=12)
        
        # Label dengan background
        label = f"#{idx+1} Conf:{box['confidence']:.2f}"
        
        # Kalkulasi lebar & tinggi teks agar kotak background pas
        try:
            left, top, right, bottom = draw.textbbox((x1, 0), label, font=font)
            text_w = right - left
            text_h = bottom - top
        except AttributeError:
            # Fallback untuk versi Pillow lama
            text_w = len(label) * 20
            text_h = 40
            
        text_y = max(0, y1 - text_h - 10)
        
        # Gambar background kotak untuk label
        draw.rectangle([(x1, text_y), (x1 + text_w + 10, text_y + text_h + 10)], fill=color)
        draw.text((x1 + 5, text_y + 5), label, fill="white", font=font)
        
    return image


@router.post(
    "/test-ocr",
    summary="[TESTER] Uji YOLO + PaddleOCR pada gambar",
    description="""
**Endpoint debugging** untuk menguji pipeline deteksi dan pembacaan teks pada foto kotak FAT.

### Alur kerja:
1. Upload foto → YOLO mendeteksi semua kotak FAT di gambar
2. Kotak terbaik (confidence tertinggi) di-crop dan di-zoom 2x
3. PaddleOCR membaca teks dari area crop tersebut
4. Semua hasil intermediate dikembalikan sebagai JSON (verbose mode)

### Output meliputi:
- Metadata gambar (ukuran, format)
- Semua bounding box yang ditemukan YOLO beserta confidence-nya
- Teks OCR per baris (dengan confidence per kata)
- Waktu inferensi YOLO dan OCR secara terpisah
- Status tiap tahap pipeline
    """,
    response_class=JSONResponse,
)
async def test_ocr_pipeline(
    foto: UploadFile = File(..., description="Foto kotak FAT yang ingin diuji")
):
    # ── Lazy import agar model hanya di-load sekali (sudah cached di inference.py) ──
    from app.utils.inference import model_fat_yolo, ocr_engine, device

    # ── 0. Baca & validasi file upload ───────────────────────────────────────────
    image_bytes = await foto.read()
    if not image_bytes:
        raise HTTPException(status_code=400, detail="File gambar kosong / tidak terbaca.")

    try:
        image = Image.open(io.BytesIO(image_bytes)).convert("RGB")
    except Exception:
        raise HTTPException(status_code=400, detail="File bukan gambar yang valid (pastikan JPG/PNG/WEBP).")

    img_width, img_height = image.size
    result: dict = {
        "timestamp": datetime.now().isoformat(),
        "input": {
            "filename": foto.filename,
            "content_type": foto.content_type,
            "file_size_kb": round(len(image_bytes) / 1024, 2),
            "image_width_px": img_width,
            "image_height_px": img_height,
        },
        "yolo": {},
        "ocr": {},
        "pipeline_summary": {}
    }

    # ── 1. YOLO Detection ─────────────────────────────────────────────────────────
    temp_path = f"uploads/dev_temp_{datetime.now().strftime('%H%M%S%f')}.jpg"
    os.makedirs("uploads", exist_ok=True)
    image.save(temp_path, "JPEG", quality=100)

    try:
        yolo_start = time.time()
        with torch.no_grad():
            yolo_results = model_fat_yolo(temp_path, device=device)
        yolo_time_ms = round((time.time() - yolo_start) * 1000, 2)
    except Exception as e:
        if os.path.exists(temp_path):
            os.remove(temp_path)
        raise HTTPException(status_code=500, detail=f"YOLO inference error: {str(e)}")
    finally:
        if os.path.exists(temp_path):
            os.remove(temp_path)

    # Parse semua bounding box dari YOLO
    all_boxes = []
    if yolo_results and yolo_results[0].boxes:
        raw_boxes = yolo_results[0].boxes
        for i, box in enumerate(raw_boxes):
            x1, y1, x2, y2 = map(int, box.xyxy[0])
            conf = float(box.conf[0])
            cls_id = int(box.cls[0])
            cls_name = yolo_results[0].names.get(cls_id, f"class_{cls_id}")
            area_px = (x2 - x1) * (y2 - y1)
            all_boxes.append({
                "box_index": i + 1,
                "class_id": cls_id,
                "class_name": cls_name,
                "confidence": round(conf, 4),
                "x_min": x1,
                "y_min": y1,
                "x_max": x2,
                "y_max": y2,
                "width_px": x2 - x1,
                "height_px": y2 - y1,
                "area_px2": area_px,
            })

    # Urutkan berdasarkan confidence (tertinggi di depan)
    all_boxes.sort(key=lambda b: b["confidence"], reverse=True)

    # Buat folder debug tester
    debug_dir = "uploads/debug_tester"
    os.makedirs(debug_dir, exist_ok=True)
    timestamp_str = datetime.now().strftime('%Y%m%d_%H%M%S%f')

    # Gambar kotak YOLO di atas gambar asli dan simpan
    yolo_debug_path = None
    if all_boxes:
        img_with_boxes = _draw_boxes_on_image(image.copy(), all_boxes)
        yolo_debug_path = f"{debug_dir}/yolo_boxes_{timestamp_str}.jpg"
        img_with_boxes.save(yolo_debug_path, "JPEG", quality=90)

    result["yolo"] = {
        "inference_time_ms": yolo_time_ms,
        "total_boxes_detected": len(all_boxes),
        "confidence_threshold_used": 0.4,
        "boxes_above_threshold": [b for b in all_boxes if b["confidence"] >= 0.4],
        "boxes_below_threshold": [b for b in all_boxes if b["confidence"] < 0.4],
        "all_boxes_raw": all_boxes,
        "debug_image_path": yolo_debug_path,
    }

    # ── 2. Pilih kotak terbaik & lakukan crop ─────────────────────────────────────
    best_box = next((b for b in all_boxes if b["confidence"] >= 0.4), None)

    if not best_box:
        result["ocr"] = {
            "skipped": True,
            "reason": "Tidak ada kotak FAT yang terdeteksi dengan confidence ≥ 0.4. OCR tidak dijalankan."
        }
        result["pipeline_summary"] = {
            "box_detected": False,
            "ocr_executed": False,
            "final_text": None,
            "total_time_ms": yolo_time_ms,
        }
        return JSONResponse(content=result)

    # Crop area terbaik + zoom 2x (sama persis seperti di inference.py)
    x1, y1, x2, y2 = best_box["x_min"], best_box["y_min"], best_box["x_max"], best_box["y_max"]
    cropped_img = image.crop((x1, y1, x2, y2))
    zoomed_img = cropped_img.resize(
        (cropped_img.width * 2, cropped_img.height * 2),
        Image.Resampling.LANCZOS
    )

    img_array = np.array(zoomed_img)

    result["yolo"]["best_box_selected"] = {
        **best_box,
        "crop_region": {"x1": x1, "y1": y1, "x2": x2, "y2": y2},
        "crop_size_original": {"w": x2 - x1, "h": y2 - y1},
        "crop_size_after_zoom": {"w": zoomed_img.width, "h": zoomed_img.height},
    }

    # ── 3. OCR pada crop ──────────────────────────────────────────────────────────
    try:
        ocr_start = time.time()
        ocr_result = ocr_engine.ocr(img_array, cls=False)
        ocr_time_ms = round((time.time() - ocr_start) * 1000, 2)
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"PaddleOCR inference error: {str(e)}")

    # Parse tiap baris teks — PaddleOCR 2.9.x format: [[polygon, [text, conf]], ...]
    ocr_lines = []
    all_texts = []
    ocr_debug_path = None

    if ocr_result and ocr_result[0]:
        # Siapkan gambar crop untuk digambar poligon OCR-nya
        img_ocr_debug = zoomed_img.copy()
        draw_ocr = ImageDraw.Draw(img_ocr_debug)

        for line_idx, line in enumerate(ocr_result[0]):
            polygon = line[0]           # Koordinat polygon deteksi teks
            text_val = line[1][0]       # Teks yang terbaca
            text_conf = line[1][1]      # Confidence teks tersebut

            text_clean = text_val.strip().upper()
            all_texts.append(text_clean)

            ocr_lines.append({
                "line_index": line_idx + 1,
                "raw_text": text_val,
                "text_upper": text_clean,
                "confidence": round(float(text_conf), 4),
                "polygon_coords": [[round(p[0], 1), round(p[1], 1)] for p in polygon],
            })
            
            # Gambar poligon OCR pada gambar crop (SANGAT tebal)
            flat_polygon = []
            for p in polygon:
                flat_polygon.extend([p[0], p[1]])
            draw_ocr.polygon(flat_polygon, outline="blue", width=10)
            
            # Tuliskan teks hasil baca + confidence di atas poligon
            label_ocr = f"{text_clean} ({float(text_conf):.2f})"
            
            # Load font besar (36)
            try:
                font_ocr = ImageFont.truetype("arial.ttf", 36)
            except IOError:
                font_ocr = ImageFont.load_default()
                
            text_x = polygon[0][0]
            
            # Kalkulasi ukuran box text
            try:
                left, top, right, bottom = draw_ocr.textbbox((text_x, 0), label_ocr, font=font_ocr)
                text_w = right - left
                text_h = bottom - top
            except AttributeError:
                text_w = len(label_ocr) * 18
                text_h = 36
                
            text_y = max(0, polygon[0][1] - text_h - 10)
            
            # Gambar background & text
            draw_ocr.rectangle([(text_x, text_y), (text_x + text_w + 10, text_y + text_h + 10)], fill="blue")
            draw_ocr.text((text_x + 5, text_y + 5), label_ocr, fill="white", font=font_ocr)

        # Simpan gambar debug OCR
        ocr_debug_path = f"{debug_dir}/ocr_crop_{timestamp_str}.jpg"
        img_ocr_debug.save(ocr_debug_path, "JPEG", quality=90)

    combined_text = " ".join(all_texts) if all_texts else None

    result["ocr"] = {
        "skipped": False,
        "inference_time_ms": ocr_time_ms,
        "total_lines_detected": len(ocr_lines),
        "lines": ocr_lines,
        "combined_text": combined_text,
        "combined_text_words": all_texts,
        "debug_image_path": ocr_debug_path,
    }

    # ── 4. Ringkasan pipeline ─────────────────────────────────────────────────────
    total_time = round(yolo_time_ms + ocr_time_ms, 2)
    result["pipeline_summary"] = {
        "box_detected": True,
        "ocr_executed": True,
        "best_box_confidence": best_box["confidence"],
        "final_text": combined_text,
        "total_lines_read": len(ocr_lines),
        "yolo_time_ms": yolo_time_ms,
        "ocr_time_ms": ocr_time_ms,
        "total_pipeline_time_ms": total_time,
        "device_used": str(device),
        "recommendation": (
            "✅ Teks terbaca dengan baik."
            if combined_text else
            "⚠️ Kotak FAT terdeteksi YOLO, namun OCR tidak menemukan teks. "
            "Coba foto dengan pencahayaan lebih baik atau resolusi lebih tinggi."
        ),
    }

    return JSONResponse(content=result)
