"""
Examples for using the Docling Serve SDK v2.0.

This module contains practical examples demonstrating document conversion,
chunking, async task management, structured error handling, and extensible event hooks.

Usage:
    # Run all local examples (no network downloads):
    python -m docling_serve_sdk.examples

    # Ensure Docling Serve is running first:
    docling-serve dev
"""

from __future__ import annotations

import asyncio
import sys
from pathlib import Path
from typing import Any, Dict, Optional

from docling_serve_sdk import (
    DoclingClient,
    DoclingAPIError,
    DoclingTimeoutError,
    DoclingConnectionError,
    DoclingValidationError,
    ConvertDocumentResponse,
    ConvertDocumentsRequest,
    ConvertDocumentsRequestOptions,
    HttpSourceRequest,
    HierarchicalChunkerOptions,
    HierarchicalChunkerOptionsDocumentsRequest,
    HybridChunkerOptions,
    InputFormat,
    OutputFormat,
    ImageRefMode,
    ProcessingPipeline,
)

# Resolve a lightweight file that is guaranteed to exist in the repo
_REPO_ROOT = Path(__file__).resolve().parent.parent
_SAMPLE_FILE = _REPO_ROOT / "README.md"


def basic_conversion_example() -> None:
    """Basic document conversion using a local file and the Context Manager."""
    print("=== Basic Document Conversion ===")

    with DoclingClient("http://localhost:5001") as client:
        # Health check
        health = client.health_check()
        print(f"  Service status: {health.status}")

        # Version info
        version = client.version_info()
        print(f"  Server version: {version}")

        # Convert the README
        result = client.convert_file(str(_SAMPLE_FILE))
        if isinstance(result, ConvertDocumentResponse):
            print(f"  Conversion status: {result.status}")
            print(f"  Processing time: {result.processing_time:.2f}s")
            if result.document and result.document.md_content:
                snippet = result.document.md_content[:120].replace("\n", " ")
                print(f"  Content snippet: {snippet}...")

    print()


def custom_options_example() -> None:
    """Example with custom conversion options."""
    print("=== Custom Options Example ===")

    options = ConvertDocumentsRequestOptions(
        from_formats=[InputFormat.MD],
        to_formats=[OutputFormat.MD, OutputFormat.HTML],
        image_export_mode=ImageRefMode.EMBEDDED,
        do_ocr=False,  # Not needed for markdown
        pipeline=ProcessingPipeline.STANDARD,
        include_images=True,
        images_scale=2.0,
    )

    with DoclingClient("http://localhost:5001") as client:
        result = client.convert_file(str(_SAMPLE_FILE), options=options)
        if isinstance(result, ConvertDocumentResponse):
            print(f"  Status: {result.status}")
            print(f"  Processing time: {result.processing_time:.2f}s")

    print()


def url_conversion_example() -> None:
    """Convert a document from a URL (requires internet access)."""
    print("=== URL Source Conversion ===")

    request = ConvertDocumentsRequest(
        sources=[
            HttpSourceRequest(url="https://arxiv.org/pdf/2206.01062"),
        ],
        options=ConvertDocumentsRequestOptions(
            to_formats=[OutputFormat.MD],
        ),
    )

    with DoclingClient("http://localhost:5001") as client:
        result = client.convert_source(request)
        print(f"  Result type: {type(result).__name__}")
        if isinstance(result, ConvertDocumentResponse):
            print(f"  Status: {result.status}")

    print()


def async_task_example() -> None:
    """Submit a conversion as an async task and poll for results."""
    print("=== Async Task Example ===")

    with DoclingClient("http://localhost:5001") as client:
        # Submit async task
        task = client.convert_file_async_task(str(_SAMPLE_FILE))
        print(f"  Task submitted: {task.task_id}")
        print(f"  Initial status: {task.task_status}")

        # Wait for completion with automatic polling
        result = client.wait_for_task(task.task_id, poll_interval=2.0, timeout=300.0)
        print(f"  Task completed! Result keys: {list(result.keys())}")

    print()


def chunking_example() -> None:
    """Chunk a document using HybridChunker."""
    print("=== Hybrid Chunking Example ===")

    chunking_opts = HybridChunkerOptions(
        use_markdown_tables=True,
        max_tokens=512,
        tokenizer="sentence-transformers/all-MiniLM-L6-v2",
    )

    with DoclingClient("http://localhost:5001") as client:
        result = client.chunk_hybrid_file(
            str(_SAMPLE_FILE),
            chunking_options=chunking_opts,
            include_converted_doc=True,
        )
        print(f"  Chunks generated: {len(result.chunks)}")
        if result.chunks:
            first = result.chunks[0]
            snippet = first.text[:100].replace("\n", " ")
            print(f"  First chunk: {snippet}...")

    print()


def chunking_source_example() -> None:
    """Chunk a document from a URL source using HierarchicalChunker (requires internet)."""
    print("=== Hierarchical Chunking from URL ===")

    request = HierarchicalChunkerOptionsDocumentsRequest(
        sources=[
            HttpSourceRequest(url="https://arxiv.org/pdf/2206.01062"),
        ],
        chunking_options=HierarchicalChunkerOptions(
            use_markdown_tables=True,
            include_raw_text=True,
        ),
    )

    with DoclingClient("http://localhost:5001") as client:
        result = client.chunk_hierarchical_source(request)
        print(f"  Chunks generated: {len(result.chunks)}")

    print()


async def async_client_example() -> None:
    """Example of fully async client usage with Context Manager."""
    print("=== Async Client Example ===")

    async with DoclingClient("http://localhost:5001") as client:
        health = await client.health_check_async()
        print(f"  Async health: {health.status}")

        result = await client.convert_file_async(str(_SAMPLE_FILE))
        if isinstance(result, ConvertDocumentResponse):
            print(f"  Async status: {result.status}")
            if result.document.md_content is not None:
                snippet = result.document.md_content[:120].replace("\n", " ")
                print(f"  Content snippet: {snippet}...")

    print()


def error_handling_example() -> None:
    """Demonstrate structured error handling with precise exception types."""
    print("=== Error Handling Example ===")

    with DoclingClient("http://localhost:5001") as client:
        # 1. API error — empty sources list
        try:
            client.convert_source(ConvertDocumentsRequest(sources=[]))
        except DoclingValidationError as e:
            print(f"  [422] Validation error: {e.detail}")
        except DoclingAPIError as e:
            print(f"  [{e.status_code}] API error (empty sources): {e}")

    # 2. Connection error — unreachable host (RFC 5737 TEST-NET)
    try:
        broken = DoclingClient("http://192.0.2.1:9999", max_retries=0, timeout=5.0)
        broken.health_check()
    except (DoclingConnectionError, DoclingTimeoutError) as e:
        print(f"  Expected error (unreachable host): {type(e).__name__}")

    print()


def event_hooks_example() -> None:
    """Demonstrate the new extensible event hooks for telemetry."""
    print("=== Event Hooks (Telemetry) Example ===")

    def my_request_hook(
        req_id: str, method: str, path: str, body: Optional[Dict[str, Any]],
    ) -> None:
        print(f"  [REQ {req_id[:8]}] -> {method} {path}")

    def my_response_hook(
        req_id: str, method: str, path: str, status: int, duration: float,
    ) -> None:
        print(f"  [RES {req_id[:8]}] <- {status} in {duration:.3f}s")

    with DoclingClient(
        base_url="http://localhost:5001",
        on_request=my_request_hook,
        on_response=my_response_hook,
    ) as client:
        client.health_check()

    print()


def api_key_example() -> None:
    """Example with API key authentication and repr redaction."""
    print("=== API Key Authentication ===")

    client = DoclingClient(
        base_url="http://localhost:5001",
        api_key="my-secret-key",
    )
    # The key is redacted in repr
    print(f"  Client repr: {client}")  # Shows api_key=***

    # API key is sent in X-Api-Key header
    health = client.health_check()
    print(f"  Status: {health.status}")

    print()


def management_example() -> None:
    """Example of management endpoints (requires server management access)."""
    print("=== Management Endpoints ===")

    with DoclingClient("http://localhost:5001") as client:
        try:
            stats = client.memory_stats()
            print(f"  Memory stats: {stats}")

            counts = client.memory_counts()
            print(f"  Memory counts: {counts}")

            clear = client.clear_converters()
            print(f"  Clear converters: {clear.status}")

            clear = client.clear_results(older_than=3600)
            print(f"  Clear results: {clear.status}")
        except DoclingAPIError as e:
            print(f"  [{e.status_code}] Management access restricted: {e}")

    print()


# ---------------------------------------------------------------------------
# Main — run each example independently with isolated error handling
# ---------------------------------------------------------------------------

_EXAMPLES = [
    ("basic_conversion", basic_conversion_example),
    ("custom_options", custom_options_example),
    ("async_task", async_task_example),
    ("chunking", chunking_example),
    ("error_handling", error_handling_example),
    ("event_hooks", event_hooks_example),
    ("api_key", api_key_example),
    ("management", management_example),
]

# These examples require downloading from the internet and may be slow
_NETWORK_EXAMPLES = [
    ("url_conversion", url_conversion_example),
    ("chunking_source", chunking_source_example),
]


def main() -> None:
    """Run all examples. Pass --network to include URL-based examples."""
    print("Docling Serve SDK v2.0 — Examples")
    print("=" * 50)

    if not _SAMPLE_FILE.exists():
        print(f"ERROR: Sample file not found: {_SAMPLE_FILE}")
        sys.exit(1)

    examples = list(_EXAMPLES)
    if "--network" in sys.argv:
        examples.extend(_NETWORK_EXAMPLES)

    passed = 0
    failed = 0

    for _name, func in examples:
        try:
            func()
            passed += 1
        except Exception as e:
            print(f"  FAILED: {type(e).__name__}: {e}\n")
            failed += 1

    # Async example
    try:
        asyncio.run(async_client_example())
        passed += 1
    except Exception as e:
        print(f"  FAILED: {type(e).__name__}: {e}\n")
        failed += 1

    print("=" * 50)
    print(f"Results: {passed} passed, {failed} failed")
    if failed:
        print("Some examples failed — check that Docling Serve is running on port 5001.")
        sys.exit(1)


if __name__ == "__main__":
    main()