#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.11"
# dependencies = ["httpx>=0.28"]
# ///
"""
Parallel.ai CLI - Web search and content extraction

Requires PARALLEL_API_KEY environment variable for API operations.
"""

import json
import os
import sys
from typing import Any, NoReturn

import httpx

BASE_URL = "https://api.parallel.ai/v1beta"
BETA_HEADER = "search-extract-2025-10-10"


def get_api_key() -> str | None:
    """Get API key from environment."""
    return os.environ.get("PARALLEL_API_KEY", "").strip() or None


def error(message: str, hint: str | None = None) -> NoReturn:
    """Print error message to stderr and exit."""
    print(f"Error: {message}", file=sys.stderr)
    if hint:
        print(hint, file=sys.stderr)
    sys.exit(1)


def call_api(endpoint: str, payload: dict[str, Any]) -> dict[str, Any]:
    """Make API request with proper error handling."""
    api_key = get_api_key()
    if not api_key:
        error("PARALLEL_API_KEY not set", "Get your key from: https://platform.parallel.ai")

    headers = {
        "Content-Type": "application/json",
        "x-api-key": api_key,
        "parallel-beta": BETA_HEADER,
    }

    try:
        response = httpx.post(
            f"{BASE_URL}{endpoint}",
            json=payload,
            headers=headers,
            timeout=60.0,
        )
        response.raise_for_status()
        return response.json()
    except httpx.HTTPStatusError as e:
        # Try to extract error message from response
        try:
            body = e.response.json()
            msg = body.get("error", {}).get("message") or body.get("error") or str(body)
        except Exception:
            msg = e.response.text or str(e)
        error(f"API returned HTTP {e.response.status_code}", msg)
    except httpx.RequestError as e:
        error("Failed to reach Parallel.ai API", str(e))
    except json.JSONDecodeError:
        error("API returned invalid JSON", f"Response: {response.text[:200]}")


def format_search_results(data: dict[str, Any] | None) -> str:
    """Format search results as markdown."""
    if data is None:
        return "No results found"

    if "error" in data:
        error_msg = data["error"]
        if isinstance(error_msg, dict):
            return f"Error: {error_msg.get('message', error_msg)}"
        return f"Error: {error_msg}"

    results = data.get("results", [])
    if not results:
        return "No results found"

    output = []
    for r in results:
        title = r.get("title", "Untitled")
        url = r.get("url", "N/A")
        domain = r.get("domain") or "unknown"
        excerpt = r.get("excerpt") or r.get("snippet") or "No excerpt available"

        output.append(f"## {title}")
        output.append(f"**URL:** {url}")
        output.append(f"**Domain:** {domain}")
        output.append("")
        output.append(excerpt)
        output.append("")
        output.append("---")
        output.append("")

    return "\n".join(output)


def format_extract_results(data: dict[str, Any] | None) -> str:
    """Format extraction results as markdown."""
    if data is None:
        return "No content extracted"

    if "error" in data:
        error_msg = data["error"]
        if isinstance(error_msg, dict):
            return f"Error: {error_msg.get('message', error_msg)}"
        return f"Error: {error_msg}"

    results = data.get("results", [])
    if not results:
        return "No content extracted"

    output = []
    for r in results:
        title = r.get("title", "Extracted Content")
        url = r.get("url", "N/A")
        content = r.get("content") or r.get("excerpt") or "No content extracted"

        output.append(f"## {title}")
        output.append(f"**URL:** {url}")
        output.append("")
        output.append(content)
        output.append("")
        output.append("---")
        output.append("")

    return "\n".join(output)


def cmd_search(args: list[str]) -> None:
    """Execute search command."""
    query_parts = []
    limit = 5
    i = 0

    while i < len(args):
        if args[i] == "--limit":
            if i + 1 >= len(args) or args[i + 1].startswith("--"):
                error("--limit requires a numeric value")
            try:
                limit = int(args[i + 1])
                if limit <= 0:
                    error(f"--limit must be a positive integer, got '{args[i + 1]}'")
            except ValueError:
                error(f"--limit must be a positive integer, got '{args[i + 1]}'")
            i += 2
        else:
            query_parts.append(args[i])
            i += 1

    query = " ".join(query_parts)
    if not query:
        error("Search query required", "Usage: parallel search \"your query\" [--limit N]")

    payload = {
        "objective": f"Find relevant information about: {query}",
        "search_queries": [query],
        "max_results": limit,
        "excerpts": {"max_chars_per_result": 500},
    }

    result = call_api("/search", payload)
    print(format_search_results(result))


def cmd_extract(args: list[str]) -> None:
    """Execute extract command."""
    url = None
    full_content = False

    for arg in args:
        if arg == "--full":
            full_content = True
        elif not url:
            url = arg

    if not url:
        error("URL required", "Usage: parallel extract <url> [--full]")

    if not url.startswith(("http://", "https://")):
        error("URL must start with http:// or https://", f"Got: {url}")

    payload = {
        "urls": [url],
        "objective": "Extract the main content from this page",
        "excerpts": not full_content,
        "full_content": full_content,
    }

    result = call_api("/extract", payload)
    print(format_extract_results(result))


def cmd_raw(args: list[str]) -> None:
    """Execute raw API command."""
    if not args:
        error("Endpoint required", "Usage: parallel raw <endpoint> [json-payload]")

    endpoint = args[0]
    payload_str = args[1] if len(args) > 1 else "{}"

    try:
        payload = json.loads(payload_str)
    except json.JSONDecodeError as e:
        error(f"Invalid JSON payload: {e}")

    result = call_api(endpoint, payload)
    print(json.dumps(result, indent=2))


def cmd_help() -> None:
    """Show help message."""
    print("""Parallel.ai CLI - Web search and content extraction

Commands:
  search "query"              Web search with AI-optimized results
  search "query" --limit N    Limit results (default: 5)
  extract <url>               Extract content from URL
  extract <url> --full        Include full page content
  raw <endpoint> [json]       Raw API call
  help                        Show this help

Environment:
  PARALLEL_API_KEY            Required - your Parallel.ai API key

Examples:
  parallel search "latest AI developments"
  parallel search "React 19 features" --limit 10
  parallel extract https://example.com/article
  parallel extract https://example.com/doc.pdf --full
  parallel raw /search '{"search_queries":["test"]}'

Get your API key: https://platform.parallel.ai""")


def main() -> None:
    """Main entry point."""
    args = sys.argv[1:]
    command = args[0] if args else "help"

    if command == "help":
        cmd_help()
    elif command == "search":
        cmd_search(args[1:])
    elif command == "extract":
        cmd_extract(args[1:])
    elif command == "raw":
        cmd_raw(args[1:])
    else:
        error(f"Unknown command: {command}", "Run 'parallel help' for available commands")


if __name__ == "__main__":
    main()
