"""Lab 07 - Content Understanding: one service, four modalities.

Objective 2.4 - Extract information from documents, images, audio and video;
                build a lightweight app with information extraction.

Run:  python 07_content_understanding.py
"""
import json
import os
import time

import requests

from _common import banner, require

ENDPOINT = os.getenv("CONTENT_UNDERSTANDING_ENDPOINT", "").rstrip("/")
API_VERSION = "2025-11-01"          # GA. 2026-06-01-preview for preview features

banner("Lab 07 - Azure AI Content Understanding")

if not ENDPOINT:
    print("  CONTENT_UNDERSTANDING_ENDPOINT not set - running in explain-only mode.\n")

CUSTOM_ANALYZER_SCHEMA = {
    "description": "Contoso supplier invoices",
    "baseAnalyzerId": "prebuilt-documentAnalyzer",
    "config": {
        "returnDetails": True,
        "estimateFieldSourceAndConfidence": True,   # turns ON confidence + grounding
        "enableSegment": False,                     # split into logical sections
    },
    "fieldSchema": {
        "fields": {
            # extract = verbatim from the content. DOCUMENTS ONLY.
            "InvoiceNumber": {"type": "string", "method": "extract"},
            "InvoiceDate":   {"type": "date",   "method": "extract"},
            "TotalAmount":   {"type": "number", "method": "extract"},

            # classify = pick from a predefined set. All modalities.
            "Department": {
                "type": "string", "method": "classify",
                "enum": ["Facilities", "IT", "Marketing", "Operations"],
            },

            # generate = create a new value. All modalities.
            "Summary": {
                "type": "string", "method": "generate",
                "description": "One sentence describing what was purchased",
            },

            "LineItems": {
                "type": "array",
                "items": {"type": "object", "properties": {
                    "Description": {"type": "string", "method": "extract"},
                    "Amount":      {"type": "number", "method": "extract"},
                }},
            },
        }
    },
}

print("A custom analyzer schema:\n")
print(json.dumps(CUSTOM_ANALYZER_SCHEMA, indent=2))

print("""
The three field-extraction methods, and the exam's favourite trap:

    extract   verbatim value        DOCUMENTS ONLY
    classify  predefined category   all modalities
    generate  newly created value   all modalities

  So to pull information out of a VIDEO you must use classify or generate.
  There is no 'extract' for video, audio, or images.
""")

banner("The call pattern - analysis is ASYNCHRONOUS")

print("""
    1. POST  .../analyzers/{id}:analyze     -> 202 + Operation-Location header
    2. GET   {Operation-Location}           -> poll until Succeeded / Failed
    3. read  result.contents[].fields       -> value + confidence + grounding

  Any exam answer implying a single synchronous call is WRONG.
""")


def analyze(analyzer_id: str, source_url: str) -> dict:
    """Submit content and poll until the operation completes."""
    from azure.identity import DefaultAzureCredential

    token = DefaultAzureCredential().get_token(
        "https://cognitiveservices.azure.com/.default"
    ).token
    headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}

    submitted = requests.post(
        f"{ENDPOINT}/contentunderstanding/analyzers/{analyzer_id}:analyze"
        f"?api-version={API_VERSION}",
        headers=headers,
        json={"url": source_url},
        timeout=30,
    )
    submitted.raise_for_status()
    operation_url = submitted.headers["Operation-Location"]

    for _ in range(60):
        payload = requests.get(operation_url, headers=headers, timeout=30).json()
        if payload.get("status") in ("Succeeded", "Failed"):
            return payload
        time.sleep(2)
    raise TimeoutError("Analysis did not complete within 120 seconds.")


CONFIDENCE_THRESHOLD = 0.85


def route(fields: dict) -> str:
    """The commercial point of confidence scores: straight-through processing."""
    weakest = min((f.get("confidence", 0.0) for f in fields.values()), default=0.0)
    return "auto-process" if weakest >= CONFIDENCE_THRESHOLD else "human-review"


if ENDPOINT:
    banner("Live run - prebuilt invoice analyzer")

    SAMPLE_INVOICE = ("https://raw.githubusercontent.com/Azure-Samples/"
                      "cognitive-services-REST-api-samples/master/curl/"
                      "form-recognizer/sample-invoice.pdf")
    try:
        outcome = analyze("prebuilt-invoice", SAMPLE_INVOICE)
        contents = outcome.get("result", {}).get("contents", [])
        if contents:
            fields = contents[0].get("fields", {})
            for name, field in fields.items():
                value = (field.get("valueString") or field.get("valueNumber")
                         or field.get("valueDate") or field.get("valueArray"))
                print(f"  {name:20} {str(value)[:48]:50} "
                      f"conf={field.get('confidence', 0):.2f}")
            print(f"\n  Routing decision: {route(fields)}")
        else:
            print("  No contents returned.")
    except Exception as exc:                                # noqa: BLE001
        print(f"  Live call failed: {exc}")
        print("  Check the endpoint format and your RBAC role on the resource.")
else:
    banner("Skipped live run")
    print("  Set CONTENT_UNDERSTANDING_ENDPOINT in .env to run it.")
    print("  Format: https://<resource-name>.services.ai.azure.com")

print("""
Exam anchors:
  - ONE service, FOUR modalities: documents, images, audio, video
  - Analyzer = reusable config (content extraction + field schema + models)
  - extract is DOCUMENTS ONLY; classify and generate work everywhere
  - Analysis is ASYNCHRONOUS: submit, get Operation-Location, poll
  - Confidence scores (0-1) enable straight-through processing
  - Grounding = WHERE in the source the value came from
  - enableSegment splits documents/video into logical sections
  - GA API version: 2025-11-01
  - Output: Markdown (for RAG) or JSON (for automation)
  - vs Document Intelligence: that is documents-only; CU is the multimodal choice
""")
