"""
LitmusDetector — thin HTTP client for the Litmus AI backend.

Authentication:
    Set LITMUS_API_KEY and LITMUS_URL in your .env file or environment.
    The SDK reads them automatically; never pass them as arguments.

Usage:
    from litmus_ai import LitmusDetector

    detector = LitmusDetector(type="context-grounding")
    result = detector.check(context="...", output="...")
    print(result.score, result.is_hallucination)
"""

import os
from typing import Optional, Union, List

import requests
from dotenv import load_dotenv

from .result import LitmusResult
from .exceptions import LitmusAuthError, LitmusAPIError

load_dotenv()

# ── Backend URL ───────────────────────────────────────────────────────────────
LITMUS_URL = os.getenv("LITMUS_URL", "").rstrip("/")

_VALID_TYPES = {
    "context-grounding",
    "context-contradiction",
    "relational-inversion",
    "instruction-drift",
}


class LitmusDetector:
    """
    Thin client that sends detection requests to the private Litmus AI backend.

    Args:
        type: Detection strategy. One of:
            - "context-grounding"     (default)
            - "context-contradiction"
            - "relational-inversion"
            - "instruction-drift"
        threshold: Optional score threshold override (0.0–1.0).

    Environment:
        LITMUS_API_KEY  — your Litmus API key (required, starts with 'lm-')
        LITMUS_URL      — the Litmus backend URL (required)
    """

    def __init__(
        self,
        type: str = "context-grounding",
        threshold: Optional[float] = None,
    ):
        if type not in _VALID_TYPES:
            raise ValueError(
                f"Invalid type '{type}'. Choose from: {', '.join(sorted(_VALID_TYPES))}"
            )

        self.type = type
        self.threshold = threshold

        # Read API key from environment
        self._api_key = os.getenv("LITMUS_API_KEY")
        if not self._api_key:
            raise LitmusAuthError(
                "LITMUS_API_KEY is not set.\n"
                "Add it to your .env file:  LITMUS_API_KEY=lm-your-key-here\n"
                "Or export it:              export LITMUS_API_KEY=lm-your-key-here"
            )

        if not LITMUS_URL:
            raise LitmusAuthError(
                "LITMUS_URL is not set in .env file.\n"
            )

    def check(
        self,
        context: Union[str, List[str]],
        output: Union[str, List[str]],
        query: Optional[str] = None,
        threshold: Optional[float] = None,
    ) -> LitmusResult:
        """
        Check if the output is supported by the context.

        Args:
            context: The context/evidence text (str or list of sentences).
            output:  The LLM output / claims to verify (str or list).
            query:   Required only for type='instruction-drift'.
            threshold: Per-call threshold override (0.0–1.0).

        Returns:
            LitmusResult with .score, .is_hallucination, .details
        """
        if self.type == "instruction-drift" and query is None:
            raise ValueError("query is required for type='instruction-drift'")

        # Normalise inputs to strings for the API
        if isinstance(context, list):
            context = " ".join(context)
        if isinstance(output, list):
            output = " ".join(output)

        effective_threshold = threshold if threshold is not None else self.threshold

        payload = {
            "type": self.type,
            "context": context,
            "output": output,
            "query": query,
            "threshold": effective_threshold,
        }

        try:
            response = requests.post(
                f"{LITMUS_URL}/check",
                json=payload,
                headers={"X-API-Key": self._api_key},
                timeout=60,
            )
        except requests.exceptions.ConnectionError:
            raise LitmusAPIError(0, "Could not connect to the Litmus backend. Check your internet connection.")
        except requests.exceptions.Timeout:
            raise LitmusAPIError(0, "Request to Litmus backend timed out.")

        if response.status_code == 401:
            raise LitmusAuthError("Invalid or inactive API key. Check your LITMUS_API_KEY.")
        if response.status_code == 422:
            detail = response.json().get("detail", "Unprocessable request")
            raise ValueError(f"Invalid request: {detail}")
        if not response.ok:
            raise LitmusAPIError(response.status_code, response.text)

        data = response.json()
        return LitmusResult(
            score=data["score"],
            is_hallucination=data["is_hallucination"],
            details=data.get("details", {}),
        )
