import requests
from django.conf import settings

from .exceptions import (
    NotifiedByAPIError,
    NotifiedByAuthError,
    NotifiedByConfigError,
    NotifiedByError,
    NotifiedByNotFoundError,
    NotifiedByValidationError,
)

API_VERSION = 1
BASE_URL = "https://api.notifiedby.com"


class NotifiedByClient:
    """Low-level HTTP client for NotifiedBy API."""

    def __init__(self, api_key=None, base_url=None):
        """
        Initialize NotifiedBy API client.

        Args:
            api_key: API key for authentication. If not provided, reads from
                     Django settings.NOTIFIEDBY_API_KEY.
            base_url: Base URL for API. Defaults to https://api.notifiedby.com.

        Raises:
            NotifiedByConfigError: If API key is not provided or configured.
        """
        self.api_key = api_key or getattr(settings, 'NOTIFIEDBY_API_KEY', None)
        self.base_url = base_url or BASE_URL
        self.encryption_key = getattr(settings, 'NOTIFIEDBY_ENCRYPTION_KEY', None)

        if not self.api_key:
            raise NotifiedByConfigError(
                "NOTIFIEDBY_API_KEY is not set in Django settings. "
                "Please add NOTIFIEDBY_API_KEY = 'your-api-key' to your settings.py"
            )

    def _get_headers(self):
        """Build authorization headers for API requests."""
        headers = {"Authorization": f"Api-Key {self.api_key}"}
        if self.encryption_key:
            headers["Encryption-Key"] = self.encryption_key
        return headers

    def _request(self, method, endpoint, **kwargs):
        """
        Make HTTP request with error handling.

        Args:
            method: HTTP method (GET, POST, DELETE, etc.)
            endpoint: API endpoint path (e.g., '/flows/subscribe/')
            **kwargs: Additional arguments passed to requests.request()

        Returns:
            Parsed JSON response or empty dict if no content.

        Raises:
            NotifiedByError: If request fails
            NotifiedByAPIError: If API returns error response
            NotifiedByValidationError: If API returns 400
            NotifiedByAuthError: If API returns 401/403
            NotifiedByNotFoundError: If API returns 404
        """
        url = f"{self.base_url}/v{API_VERSION}{endpoint}"
        headers = self._get_headers()

        # Merge custom headers if provided
        if 'headers' in kwargs:
            headers.update(kwargs['headers'])
        kwargs['headers'] = headers

        # Set default timeout if not provided
        if 'timeout' not in kwargs:
            kwargs['timeout'] = 30

        try:
            response = requests.request(method, url, **kwargs)
        except requests.exceptions.RequestException as e:
            raise NotifiedByError(f"Request failed: {e}") from e

        # Success (2xx)
        if 200 <= response.status_code < 300:
            if response.content:
                try:
                    return response.json()
                except ValueError:
                    return {}
            return {}

        # Map status codes to specific exceptions
        if response.status_code == 400:
            raise NotifiedByValidationError(
                response.status_code, response.text, endpoint
            )
        elif response.status_code in (401, 403):
            raise NotifiedByAuthError(
                response.status_code, response.text, endpoint
            )
        elif response.status_code == 404:
            raise NotifiedByNotFoundError(
                response.status_code, response.text, endpoint
            )
        else:
            raise NotifiedByAPIError(
                response.status_code, response.text, endpoint
            )

    def get(self, endpoint, params=None):
        """
        Make GET request.

        Args:
            endpoint: API endpoint path
            params: Query parameters dict

        Returns:
            Parsed JSON response
        """
        return self._request("GET", endpoint, params=params)

    def post(self, endpoint, data=None, json=None, files=None):
        """
        Make POST request.

        Args:
            endpoint: API endpoint path
            data: Form data dict
            json: JSON data dict
            files: Files dict for multipart upload

        Returns:
            Parsed JSON response
        """
        return self._request("POST", endpoint, data=data, json=json, files=files)

    def delete(self, endpoint):
        """
        Make DELETE request.

        Args:
            endpoint: API endpoint path

        Returns:
            Parsed JSON response (usually empty)
        """
        return self._request("DELETE", endpoint)
