"""HTTP client for RunSentry backend API."""

import logging
import sys
import time
import requests
from typing import List, Dict, Any, Optional
from runsentry import __version__
from runsentry.config import get_config
from runsentry.storage.buffer import EventBuffer

logger = logging.getLogger(__name__)


class APIClient:
    """HTTP client with retry logic and offline buffering."""

    def __init__(self):
        self.config = get_config()
        self.buffer = EventBuffer()
        self.session = requests.Session()
        self.session.headers.update({
            "Content-Type": "application/json",
            "User-Agent": f"RunSentry-Agent/{__version__}",
        })

    def _get_auth_header(self) -> Optional[tuple[str, str]]:
        """Get authentication header name and value."""
        if not self.config.token:
            return None
        return ("X-Agent-Api-Key", self.config.token)

    def _request(
        self,
        method: str,
        endpoint: str,
        json_data: Optional[Dict[str, Any]] = None,
        retries: int = 3,
    ) -> Optional[requests.Response]:
        """Make HTTP request with retry logic."""
        url = f"{self.config.backend_url}{endpoint}"
        headers = {}
        auth_header = self._get_auth_header()
        if auth_header:
            header_name, header_value = auth_header
            headers[header_name] = header_value

        for attempt in range(retries):
            try:
                response = self.session.request(
                    method,
                    url,
                    json=json_data,
                    headers=headers,
                    timeout=30,
                )
                response.raise_for_status()
                return response
            except requests.exceptions.RequestException as e:
                if attempt == retries - 1:
                    # Last attempt failed, buffer if applicable
                    if json_data and "/executions" in endpoint:
                        self.buffer.add(json_data)
                    raise
                time.sleep(2 ** attempt)  # Exponential backoff

        return None

    def discover_jobs(self, jobs: List[Dict[str, Any]]) -> bool:
        """Send discovered jobs to backend."""
        if not self.config.agent_id:
            return False

        payload = {
            "agentId": self.config.agent_id,
            "jobs": jobs,
        }

        try:
            response = self._request("POST", "/api/v1/jobs/discover", json_data=payload)
            if response is not None:
                logger.info(f"Successfully sent {len(jobs)} jobs to backend")
            return response is not None
        except Exception as e:
            logger.warning(f"Failed to send job discovery: {e}")
            return False

    def send_execution_event(self, event: Dict[str, Any]) -> Optional[requests.Response]:
        """Send execution event to backend."""
        if not self.config.agent_id:
            return None

        try:
            response = self._request(
                "POST", "/api/v1/executions/event", json_data=event
            )
            return response
        except Exception as e:
            # Event is already buffered in _request if it failed
            logger.warning(f"Failed to send execution event: {e}")
            return None

    def send_heartbeat(self) -> bool:
        """Send heartbeat to backend to indicate agent is alive."""
        if not self.config.agent_id:
            return False

        payload = {
            "agentId": self.config.agent_id,
            "timestamp": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
            "version": __version__,
            "status": "alive",
        }

        try:
            response = self._request("POST", "/api/v1/agents/heartbeat", json_data=payload)
            return response is not None
        except Exception as e:
            logger.warning(f"Failed to send heartbeat: {e}")
            return False

    def flush_buffer(self):
        """Flush buffered events to backend."""
        pending = self.buffer.get_pending(limit=50)
        if pending:
            logger.debug(f"Flushing {len(pending)} buffered events")
        for event in pending:
            try:
                if self.send_execution_event(event):
                    # Event sent successfully, already removed from buffer
                    pass
            except Exception:
                # Re-buffer if send fails
                self.buffer.add(event)

