"""
PingPulse Hook for Apache Airflow.

Handles connection management and API calls to PingPulse.
"""

from typing import Any, Dict, Optional
import requests
from airflow.hooks.base import BaseHook
from airflow.exceptions import AirflowException


class PingPulseHook(BaseHook):
    """
    Hook for interacting with PingPulse API.

    Configure connection in Airflow UI:
        - Conn Id: pingpulse_default
        - Conn Type: HTTP
        - Host: https://app.pingpulse.com (or your self-hosted URL)
        - Password: Your PingPulse API key (ppk_xxx_xxx)

    Alternatively, pass api_key and base_url directly.
    """

    conn_name_attr = "pingpulse_conn_id"
    default_conn_name = "pingpulse_default"
    conn_type = "http"
    hook_name = "PingPulse"

    def __init__(
        self,
        pingpulse_conn_id: str = default_conn_name,
        api_key: Optional[str] = None,
        base_url: Optional[str] = None,
    ) -> None:
        """
        Initialize PingPulse hook.

        Args:
            pingpulse_conn_id: Airflow connection ID for PingPulse
            api_key: Direct API key (overrides connection)
            base_url: Direct base URL (overrides connection)
        """
        super().__init__()
        self.pingpulse_conn_id = pingpulse_conn_id
        self._api_key = api_key
        self._base_url = base_url
        self._session: Optional[requests.Session] = None

    def get_conn(self) -> requests.Session:
        """
        Get or create a requests session.

        Returns:
            requests.Session configured for PingPulse API
        """
        if self._session is None:
            self._session = requests.Session()
            self._session.headers.update({
                "Content-Type": "application/json",
                "User-Agent": "pingpulse-airflow/0.1.0",
            })
        return self._session

    @property
    def api_key(self) -> str:
        """Get API key from direct config or Airflow connection."""
        if self._api_key:
            return self._api_key

        conn = self.get_connection(self.pingpulse_conn_id)
        if conn.password:
            return conn.password

        raise AirflowException(
            f"No API key found. Either pass api_key directly or configure "
            f"the password field in Airflow connection '{self.pingpulse_conn_id}'"
        )

    @property
    def base_url(self) -> str:
        """Get base URL from direct config or Airflow connection."""
        if self._base_url:
            return self._base_url.rstrip("/")

        try:
            conn = self.get_connection(self.pingpulse_conn_id)
            if conn.host:
                schema = conn.schema or "https"
                return f"{schema}://{conn.host}".rstrip("/")
        except AirflowException:
            pass

        return "https://app.pingpulse.com"

    def send_ping(
        self,
        workflow_id: str,
        stage_path: str,
        payload: Optional[Dict[str, Any]] = None,
        start: bool = False,
        final: bool = False,
        workflow_type: str = "dynamic",
    ) -> Dict[str, Any]:
        """
        Send a ping to PingPulse.

        Args:
            workflow_id: The workflow ID (e.g., 'dwf123abc')
            stage_path: Stage path/order (e.g., '1', '2.1')
            payload: Optional JSON payload with metrics/metadata
            start: Set True to start a new workflow instance
            final: Set True to mark workflow as complete
            workflow_type: 'dynamic' (dhk) or 'static' (shk)

        Returns:
            Response JSON from PingPulse API

        Raises:
            AirflowException: If the request fails
        """
        # Determine endpoint prefix
        prefix = "dhk" if workflow_type == "dynamic" else "shk"
        url = f"{self.base_url}/{prefix}/{workflow_id}"

        # Build query params
        params = {
            "key": self.api_key,
            "stagePath": stage_path,
        }
        if start:
            params["start"] = "yes"
        if final:
            params["final"] = "yes"

        # Send request
        session = self.get_conn()
        try:
            response = session.post(
                url,
                params=params,
                json=payload or {},
                timeout=30,
            )
            response.raise_for_status()
            return response.json()
        except requests.exceptions.RequestException as e:
            raise AirflowException(f"PingPulse API request failed: {e}")

    def send_heartbeat(
        self,
        monitor_id: str,
        payload: Optional[Dict[str, Any]] = None,
    ) -> Dict[str, Any]:
        """
        Send a heartbeat ping to PingPulse.

        Args:
            monitor_id: The heartbeat monitor ID (e.g., 'hb123abc')
            payload: Optional JSON payload

        Returns:
            Response JSON from PingPulse API
        """
        url = f"{self.base_url}/hb/{monitor_id}/ping"

        params = {"key": self.api_key}

        session = self.get_conn()
        try:
            response = session.post(
                url,
                params=params,
                json=payload or {},
                timeout=30,
            )
            response.raise_for_status()
            return response.json()
        except requests.exceptions.RequestException as e:
            raise AirflowException(f"PingPulse heartbeat request failed: {e}")

    def test_connection(self) -> tuple[bool, str]:
        """
        Test the PingPulse connection.

        Returns:
            Tuple of (success, message)
        """
        try:
            session = self.get_conn()
            response = session.get(
                f"{self.base_url}/health",
                timeout=10,
            )
            if response.status_code == 200:
                return True, "Connection successful"
            return False, f"Health check returned status {response.status_code}"
        except Exception as e:
            return False, f"Connection failed: {e}"
