"""Client for the exam extraction SERVICE (docling-exam-spike, P1).

The service runs the spike's FULL recognition pipeline (textlayer → question tree → OMR/figure/table
sidecars → structure fusion → analyse) and returns the ghost-region contract the app already consumes.
This module is a thin HTTP client: POST the paper, poll, return the `analyse` suggestions. The app's
auto-map merges them (coordinate-adapted, id-remapped) in routers/exam/templates.py.

Config: EXAM_EXTRACT_URL (e.g. http://192.168.0.203:8899). If unset, the app keeps its thin first-pass.
"""
from __future__ import annotations

import base64
import os
import time
from typing import Any, Dict, Optional

import requests

from modules.logger_tool import initialise_logger

logger = initialise_logger(__name__, os.getenv("LOG_LEVEL"), os.getenv("LOG_PATH"), "default", True)


class ExtractError(RuntimeError):
    pass


def service_url() -> Optional[str]:
    url = os.getenv("EXAM_EXTRACT_URL")
    return url.rstrip("/") if url else None


def is_enabled() -> bool:
    return bool(service_url())


def _get(base: str, slug: str, timeout: int = 30) -> Dict[str, Any]:
    r = requests.get(f"{base}/api/extract/{slug}", timeout=timeout)
    r.raise_for_status()
    return r.json()


def get_replica(slug: str, timeout: int = 30) -> Dict[str, Any]:
    """The digital-replica markdown for a paper (P4): {slug, title, n_questions, total_marks, markdown,
    questions:[{label, marks, markdown}]}. Raises ExtractError if the paper has no replica yet."""
    base = service_url()
    if not base:
        raise ExtractError("EXAM_EXTRACT_URL not configured")
    r = requests.get(f"{base}/api/replica/{slug}", timeout=timeout)
    if r.status_code == 404:
        raise ExtractError(f"no digital replica for {slug}")
    r.raise_for_status()
    return r.json()


def extract_suggestions(slug: str, pdf_bytes: bytes, *, force: bool = False,
                        poll_timeout: int = 1500, poll_interval: int = 5) -> Dict[str, Any]:
    """POST the paper to the service and poll until the analyse contract is ready.

    Returns the full analyse payload: {status, coordinate_space:'page_fraction', margins,
    suggestions:{questions[], response_areas[], boundaries[]}, meta}. Raises ExtractError on
    failure/timeout. A cold paper is ~15 min (Docling per masked page); cached papers return instantly.
    """
    base = service_url()
    if not base:
        raise ExtractError("EXAM_EXTRACT_URL not configured")
    payload = {"slug": slug, "pdf_b64": base64.b64encode(pdf_bytes).decode(), "force": force}
    r = requests.post(f"{base}/api/extract", json=payload, timeout=120)
    r.raise_for_status()
    started = r.json()
    if not started.get("ok", True):
        raise ExtractError(started.get("error") or "service rejected the request")
    # cached → fetch the contract straight away; otherwise poll the running job
    deadline = time.time() + poll_timeout
    while True:
        d = _get(base, slug)
        status = d.get("status")
        if status == "complete":
            if not (d.get("suggestions") or {}):
                raise ExtractError("service returned complete with no suggestions")
            return d
        if status == "error":
            raise ExtractError(f"extraction failed: {d.get('error')}")
        if time.time() >= deadline:
            raise ExtractError(f"extraction timed out after {poll_timeout}s (slug={slug})")
        time.sleep(poll_interval)
