# Copyright (c) 2025 Airbyte, Inc., all rights reserved.
"""Common utilities for interacting with the Devin API.

Provides reusable helpers for session ID extraction, URL resolution, and
message delivery.  These are consumed by CLI commands, MCP tools, and
workflow scripts that need to communicate with Devin sessions.

Environment variables:
    `DEVIN_API_KEY` or `DEVIN_AI_ADMIN_SERVICE_TOKEN` — Bearer token
    for the Devin v1 API.
"""

from __future__ import annotations

import logging
import os
import re

import requests

logger = logging.getLogger(__name__)

# ---------------------------------------------------------------------------
# Constants
# ---------------------------------------------------------------------------

DEVIN_API_BASE = "https://api.devin.ai"
"""Base URL for the Devin REST API."""

DEVIN_SESSION_URL_PREFIX = "https://app.devin.ai/sessions/"
"""URL prefix for Devin session deep-links."""

_SESSION_ID_PATTERN = re.compile(r"[0-9a-fA-F]{32}")
"""Matches a 32-character lowercase/uppercase hex session ID."""

_TOKEN_ENV_VARS = ("DEVIN_API_KEY", "DEVIN_AI_ADMIN_SERVICE_TOKEN")
"""Environment variables checked (in order) for the Devin API bearer token."""


# ---------------------------------------------------------------------------
# Session helpers
# ---------------------------------------------------------------------------


def extract_session_id(session: str) -> str:
    """Extract a 32-character hex session ID from a URL or raw string.

    Args:
        session: A Devin session URL
            (e.g. `https://app.devin.ai/sessions/abc123...`) or a bare
            32-character hex session ID.

    Returns:
        The 32-character hex session ID.

    Raises:
        ValueError: If no valid session ID is found in the input.
    """
    match = _SESSION_ID_PATTERN.search(session)
    if not match:
        raise ValueError(
            f"No valid session ID found in '{session}'. "
            "Expected a 32-character hex string."
        )
    return match.group(0)


def resolve_session_url(session: str) -> str:
    """Return a full Devin session URL, constructing one if needed.

    If *session* already contains the Devin session URL prefix, it is
    returned as-is.  Otherwise `extract_session_id` is called and
    a URL is constructed.

    Args:
        session: A Devin session URL or bare session ID.

    Returns:
        A full `https://app.devin.ai/sessions/<id>` URL.

    Raises:
        ValueError: If no valid session ID is found (delegated to
            `extract_session_id`).
    """
    if DEVIN_SESSION_URL_PREFIX in session:
        return session
    session_id = extract_session_id(session)
    return f"{DEVIN_SESSION_URL_PREFIX}{session_id}"


# ---------------------------------------------------------------------------
# Token resolution
# ---------------------------------------------------------------------------


def resolve_devin_api_token() -> str:
    """Resolve the Devin API bearer token from environment variables.

    Checks `DEVIN_API_KEY` and `DEVIN_AI_ADMIN_SERVICE_TOKEN` in
    order and returns the first non-empty value.

    Returns:
        The bearer token string.

    Raises:
        RuntimeError: If no token is found in any of the expected
            environment variables.
    """
    for var in _TOKEN_ENV_VARS:
        token = os.environ.get(var)
        if token:
            return token
    raise RuntimeError(
        "No Devin API token found. Set one of: " + ", ".join(_TOKEN_ENV_VARS)
    )


# ---------------------------------------------------------------------------
# API operations
# ---------------------------------------------------------------------------


def send_session_message(session_id: str, message: str) -> None:
    """Send a message to a Devin session via the v1 API.

    Args:
        session_id: 32-character hex session ID.
        message: The message body to deliver.

    Raises:
        RuntimeError: If no API token is found.
        requests.RequestException: If the API call fails.
    """
    token = resolve_devin_api_token()

    response = requests.post(
        f"{DEVIN_API_BASE}/v1/sessions/{session_id}/message",
        headers={
            "Authorization": f"Bearer {token}",
            "Content-Type": "application/json",
        },
        json={"message": message},
        timeout=30,
    )
    response.raise_for_status()
