"""HTTP client for the Coda API.

Provides async methods to call the Coda quantum computing API.
All quantum computation happens remotely - this is just an HTTP client.
"""

import os

import httpx


def _get_api_url() -> str:
    return os.environ.get("CODA_API_URL", "https://coda.conductorquantum.com/mcp")


def _get_headers() -> dict[str, str]:
    """Build HTTP headers with auth token if configured."""
    headers = {"Content-Type": "application/json"}
    token = os.environ.get("CODA_API_TOKEN", "")
    if token:
        headers["Authorization"] = f"Bearer {token}"
    return headers


# Reusable client with 10-minute timeout for long simulations
_client: httpx.AsyncClient | None = None
TIMEOUT = 600.0


async def _get_client() -> httpx.AsyncClient:
    """Get or create the shared HTTP client."""
    global _client
    if _client is None or _client.is_closed:
        _client = httpx.AsyncClient(timeout=TIMEOUT)
    return _client


async def _post(endpoint: str, data: dict) -> dict:
    """Make a POST request to the API."""
    http = await _get_client()
    response = await http.post(
        f"{_get_api_url()}{endpoint}",
        json=data,
        headers=_get_headers(),
    )
    response.raise_for_status()
    result: dict = response.json()
    return result


async def _get(endpoint: str) -> dict:
    """Make a GET request to the API."""
    http = await _get_client()
    response = await http.get(
        f"{_get_api_url()}{endpoint}",
        headers=_get_headers(),
    )
    response.raise_for_status()
    result: dict = response.json()
    return result


# === Quantum Circuit Tools ===


async def transpile(source_code: str, target: str) -> dict:
    """Transpile quantum code to a target framework.

    Args:
        source_code: Quantum circuit code in any supported framework
        target: Target framework (qiskit, cirq, pennylane, braket, pyquil, cudaq, openqasm)

    Returns:
        Transpiled code and metadata
    """
    return await _post("/transpile", {"source_code": source_code, "target": target})


async def simulate(
    code: str,
    method: str = "qasm",
    shots: int = 1024,
    seed_simulator: int | None = None,
    backend: str = "auto",
) -> dict:
    """Simulate a quantum circuit.

    Args:
        code: Python code containing the quantum circuit
        method: "qasm", "statevector", or "density_matrix"
        shots: Number of shots for qasm simulation
        seed_simulator: Random seed for reproducibility
        backend: "aer" (CPU), "cudaq" (GPU), or "auto"

    Returns:
        Simulation results with counts/probabilities
    """
    return await _post(
        "/simulate",
        {
            "code": code,
            "method": method,
            "shots": shots,
            "seed_simulator": seed_simulator,
            "backend": backend,
        },
    )


async def to_openqasm3(code: str) -> dict:
    """Convert a quantum circuit to OpenQASM 3.0.

    Args:
        code: Python code containing the quantum circuit

    Returns:
        OpenQASM 3.0 string and circuit metrics
    """
    return await _post("/to-openqasm3", {"code": code})


async def estimate_resources(code: str) -> dict:
    """Estimate resource requirements for a quantum circuit.

    Args:
        code: Python code containing the quantum circuit

    Returns:
        Qubit count, depth, gate counts, and complexity analysis
    """
    return await _post("/estimate-resources", {"code": code})


async def split_circuit(code: str) -> dict:
    """Split a complex circuit using circuit cutting.

    Args:
        code: Python code containing the quantum circuit

    Returns:
        Split sub-circuits and cutting metadata
    """
    return await _post("/split-circuit", {"code": code})


# === QPU Tools ===


async def qpu_submit(openqasm3: str, backend: str, shots: int = 100, accept_overage: bool = False) -> dict:
    """Submit a circuit to a QPU backend.

    Args:
        openqasm3: OpenQASM 3.0 circuit source code
        backend: Target device (simulator, ionq, ionq-forte, iqm, rigetti, aqt)
        shots: Number of circuit executions (1-10000)
        accept_overage: Acknowledge overage charges if credits insufficient

    Returns:
        Task ID and submission status
    """
    return await _post(
        "/qpu/submit",
        {
            "openqasm3": openqasm3,
            "backend": backend,
            "shots": shots,
            "accept_overage": accept_overage,
        },
    )


async def qpu_status(job_id: str) -> dict:
    """Check status of a submitted QPU job.

    Args:
        job_id: Job ID from qpu_submit

    Returns:
        Job status and results if completed
    """
    return await _post("/qpu/status", {"job_id": job_id})


async def qpu_devices() -> dict:
    """List available QPU devices.

    Returns:
        List of devices with names, IDs, and capabilities
    """
    return await _get("/qpu/devices")


async def qpu_estimate_cost(backend: str, shots: int) -> dict:
    """Estimate cost for a QPU job without submitting.

    Args:
        backend: Target device (simulator, ionq, ionq-forte, iqm, rigetti, aqt)
        shots: Number of circuit executions (1-10000)

    Returns:
        Estimated cost in cents and pricing details
    """
    return await _post("/qpu/estimate-cost", {"backend": backend, "shots": shots})


# === Search Tools ===


async def search_papers(
    query: str,
    search_type: str = "auto",
    num_results: int = 10,
    category: str | None = None,
    include_domains: list[str] | None = None,
    exclude_domains: list[str] | None = None,
) -> dict:
    """Search for papers and research content.

    Args:
        query: Search query string
        search_type: "auto", "neural", or "keyword"
        num_results: Number of results (max 100)
        category: Optional filter (e.g., "research paper")
        include_domains: Only include results from these domains
        exclude_domains: Exclude results from these domains

    Returns:
        Search results with titles, URLs, and snippets
    """
    return await _post(
        "/search",
        {
            "query": query,
            "search_type": search_type,
            "num_results": num_results,
            "category": category,
            "include_domains": include_domains,
            "exclude_domains": exclude_domains,
        },
    )


async def get_paper(
    urls: list[str],
    include_text: bool = True,
    max_characters: int | None = None,
) -> dict:
    """Fetch full page contents for URLs.

    Args:
        urls: List of URLs to fetch
        include_text: Whether to include full text content
        max_characters: Max characters per page (None for full)

    Returns:
        Page contents with text and metadata
    """
    return await _post(
        "/fetch",
        {
            "urls": urls,
            "include_text": include_text,
            "max_characters": max_characters,
        },
    )
