"""
Vedika API Client
Main client class for interacting with the Vedika Astrology API.
"""

import os
from typing import Dict, Any, Optional, Iterator
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

from .models import (
    BirthDetails,
    QuestionResponse,
    BirthChart,
    DashaResponse,
    CompatibilityResponse,
    YogaResponse,
    DoshaResponse,
    MuhurthaResponse,
    NumerologyResponse
)
from .exceptions import (
    VedikaAPIError,
    AuthenticationError,
    RateLimitError,
    InsufficientCreditsError,
    ValidationError
)


class VedikaClient:
    """
    Main client for the Vedika Astrology API.

    The ONLY B2B astrology API with AI-powered chatbot queries.

    Args:
        api_key: Your Vedika API key (get one at https://vedika.io/dashboard.html)
        base_url: API base URL (default: production URL)
        timeout: Request timeout in seconds (default: 60)
        max_retries: Maximum number of retries for failed requests (default: 3)
        cache_enabled: Enable prompt caching for cost savings (default: True)
        language: Default language for responses (default: "en")

    Example:
        >>> from vedika import VedikaClient
        >>> client = VedikaClient(api_key="vk_test_...")
        >>> response = client.ask_question(
        ...     question="What are my career prospects?",
        ...     birth_details={"datetime": "1990-06-15T14:30:00+05:30", ...}
        ... )
    """

    def __init__(
        self,
        api_key: Optional[str] = None,
        base_url: str = "https://api.vedika.io",
        timeout: int = 60,
        max_retries: int = 3,
        cache_enabled: bool = True,
        language: str = "en"
    ):
        self.api_key = api_key or os.getenv("VEDIKA_API_KEY")
        if not self.api_key:
            raise AuthenticationError(
                "API key is required. Get one at https://vedika.io/dashboard.html"
            )

        self.base_url = base_url.rstrip("/")
        self.timeout = timeout
        self.cache_enabled = cache_enabled
        self.language = language

        # Configure session with retries
        self.session = requests.Session()
        retry_strategy = Retry(
            total=max_retries,
            status_forcelist=[429, 500, 502, 503, 504],
            backoff_factor=1
        )
        adapter = HTTPAdapter(max_retries=retry_strategy)
        self.session.mount("https://", adapter)
        self.session.mount("http://", adapter)

        # Set default headers
        self.session.headers.update({
            "Content-Type": "application/json",
            "X-API-Key": self.api_key,
            "User-Agent": "vedika-python-sdk/2.1.0"
        })

    def _request(
        self,
        method: str,
        endpoint: str,
        data: Optional[Dict[str, Any]] = None,
        params: Optional[Dict[str, Any]] = None
    ) -> Dict[str, Any]:
        """Make HTTP request to the API."""
        url = f"{self.base_url}{endpoint}"

        try:
            response = self.session.request(
                method=method,
                url=url,
                json=data,
                params=params,
                timeout=self.timeout
            )

            # Handle specific HTTP errors
            if response.status_code == 401:
                raise AuthenticationError("Invalid API key")
            elif response.status_code == 402:
                raise InsufficientCreditsError("Insufficient credits. Add more at https://vedika.io/dashboard.html")
            elif response.status_code == 429:
                raise RateLimitError("Rate limit exceeded. Please wait a moment.")
            elif response.status_code == 422:
                raise ValidationError(f"Validation error: {response.json().get('message', 'Invalid input')}")
            elif response.status_code >= 400:
                error_msg = response.json().get("message", "API request failed")
                raise VedikaAPIError(f"HTTP {response.status_code}: {error_msg}")

            return response.json()

        except requests.exceptions.Timeout:
            raise VedikaAPIError("Request timed out. For complex queries, try increasing timeout.")
        except requests.exceptions.RequestException as e:
            raise VedikaAPIError(f"Request failed: {str(e)}")

    def ask_question(
        self,
        question: str,
        birth_details: Dict[str, Any],
        language: Optional[str] = None,
        *,
        system: Optional[str] = None,
        speed: Optional[str] = None,
        conversation_id: Optional[str] = None,
        partner_birth_details: Optional[Dict[str, Any]] = None,
        include_remedies: Optional[bool] = None,
        category: Optional[str] = None,
        response_format: Optional[str] = None
    ) -> QuestionResponse:
        """
        Ask a conversational astrology question (UNIQUE to Vedika!).

        This is the only B2B astrology API that supports natural language queries.

        Args:
            question: Your astrology question in natural language
            birth_details: Birth information (datetime, latitude, longitude, timezone)
            language: Response language (default: client's default language)
            system: Astrology system — "vedic", "western", or "kp"
            speed: Response speed — "fast" or "quality"
            conversation_id: Continue a previous conversation
            partner_birth_details: Partner's birth details for compatibility queries
            include_remedies: Include BPHS remedies in the response
            category: Query category hint (career, marriage, health, etc.)
            response_format: Response format — "text" or "json"

        Returns:
            QuestionResponse with answer, confidence, and metadata

        Example:
            >>> response = client.ask_question(
            ...     question="What are my career prospects this year?",
            ...     birth_details={
            ...         "datetime": "1990-06-15T14:30:00+05:30",
            ...         "latitude": 28.6139,
            ...         "longitude": 77.2090,
            ...         "timezone": "+05:30"
            ...     },
            ...     language="en",
            ...     system="vedic",
            ...     include_remedies=True
            ... )
            >>> print(response.answer)
        """
        data = {
            "question": question,
            "birthDetails": birth_details,
            "language": language or self.language
        }

        if system is not None:
            data["system"] = system
        if speed is not None:
            data["speed"] = speed
        if conversation_id is not None:
            data["conversationId"] = conversation_id
        if partner_birth_details is not None:
            data["partnerBirthDetails"] = partner_birth_details
        if include_remedies is not None:
            data["includeRemedies"] = include_remedies
        if category is not None:
            data["category"] = category
        if response_format is not None:
            data["responseFormat"] = response_format

        result = self._request("POST", "/api/v1/astrology/query", data=data)
        return QuestionResponse.from_dict(result)

    def ask_question_stream(
        self,
        question: str,
        birth_details: Dict[str, Any],
        language: Optional[str] = None
    ) -> Iterator[str]:
        """
        Stream conversational astrology question response in real-time.

        Args:
            question: Your astrology question
            birth_details: Birth information
            language: Response language

        Yields:
            Response chunks as they're generated

        Example:
            >>> for chunk in client.ask_question_stream(
            ...     question="What are my career prospects?",
            ...     birth_details=birth_info
            ... ):
            ...     print(chunk, end="", flush=True)
        """
        url = f"{self.base_url}/api/v1/astrology/query/stream"
        data = {
            "question": question,
            "birthDetails": birth_details,
            "language": language or self.language
        }

        with requests.post(url, json=data, headers=self.session.headers, stream=True, timeout=self.timeout) as response:
            if response.status_code != 200:
                raise VedikaAPIError(f"Stream request failed: HTTP {response.status_code}")

            for line in response.iter_lines():
                if line:
                    decoded = line.decode('utf-8')
                    if decoded.startswith('data: '):
                        yield decoded[6:]  # Remove 'data: ' prefix

    def get_birth_chart(
        self,
        datetime: str,
        latitude: float,
        longitude: float,
        timezone: str = "UTC",
        ayanamsa: str = "lahiri"
    ) -> BirthChart:
        """
        Generate a complete birth chart.

        Args:
            datetime: Birth datetime in ISO 8601 format
            latitude: Birth location latitude
            longitude: Birth location longitude
            timezone: Timezone (default: "UTC")
            ayanamsa: Ayanamsa system (default: "lahiri")

        Returns:
            BirthChart with planets, houses, and ascendant
        """
        data = {
            "birthDetails": {
                "datetime": datetime,
                "latitude": latitude,
                "longitude": longitude,
                "timezone": timezone
            },
            "ayanamsa": ayanamsa
        }

        result = self._request("POST", "/api/v1/charts/birth", data=data)
        return BirthChart.from_dict(result)

    def get_dashas(
        self,
        birth_details: Dict[str, Any]
    ) -> DashaResponse:
        """
        Get Vimshottari Dasha periods.

        Args:
            birth_details: Birth information

        Returns:
            DashaResponse with mahadashas, antardashas, and pratyantardashas
        """
        result = self._request("POST", "/api/vedika/dasha-periods", data={"birthDetails": birth_details})
        return DashaResponse.from_dict(result)

    def check_compatibility(
        self,
        person1_details: Dict[str, Any],
        person2_details: Dict[str, Any]
    ) -> CompatibilityResponse:
        """
        Check marriage compatibility using Ashtakoota matching.

        Args:
            person1_details: First person's birth details
            person2_details: Second person's birth details

        Returns:
            CompatibilityResponse with score and analysis
        """
        data = {
            "person1": person1_details,
            "person2": person2_details
        }

        result = self._request("POST", "/api/v1/compatibility/match", data=data)
        return CompatibilityResponse.from_dict(result)

    def detect_yogas(
        self,
        birth_details: Dict[str, Any]
    ) -> YogaResponse:
        """
        Detect 300+ astrological yogas.

        Args:
            birth_details: Birth information

        Returns:
            YogaResponse with detected yogas and descriptions
        """
        result = self._request("POST", "/api/vedika/yoga-detection", data={"birthDetails": birth_details})
        return YogaResponse.from_dict(result)

    def analyze_doshas(
        self,
        birth_details: Dict[str, Any]
    ) -> DoshaResponse:
        """
        Analyze doshas (Kaal Sarp, Mangal, Sade Sati, etc.).

        Args:
            birth_details: Birth information

        Returns:
            DoshaResponse with dosha analysis and remedies
        """
        result = self._request("POST", "/api/vedika/dosha-analysis", data={"birthDetails": birth_details})
        return DoshaResponse.from_dict(result)

    def get_muhurtha(
        self,
        date: str,
        location: Dict[str, float],
        event_type: str
    ) -> MuhurthaResponse:
        """
        Find auspicious times (Muhurtha) for important events.

        Args:
            date: Date in YYYY-MM-DD format
            location: Location coordinates (latitude, longitude)
            event_type: Type of event (wedding, business, etc.)

        Returns:
            MuhurthaResponse with auspicious and inauspicious times
        """
        data = {
            "date": date,
            "location": location,
            "eventType": event_type
        }

        result = self._request("POST", "/api/vedika/muhurtha", data=data)
        return MuhurthaResponse.from_dict(result)

    def get_numerology(
        self,
        name: str,
        birth_date: str
    ) -> NumerologyResponse:
        """
        Get numerology analysis (37 calculations).

        Args:
            name: Full name
            birth_date: Birth date in YYYY-MM-DD format

        Returns:
            NumerologyResponse with life path, expression, and soul urge numbers
        """
        data = {
            "name": name,
            "birthDate": birth_date
        }

        result = self._request("POST", "/api/vedika/numerology", data=data)
        return NumerologyResponse.from_dict(result)

    # ═══════════════════════════════════════════
    # V2 Vedic Computation Endpoints
    # ═══════════════════════════════════════════

    def get_birth_chart_v2(self, type: str, birth_details: Dict[str, Any]) -> Dict[str, Any]:
        """Get birth chart via V2 endpoint (faster, cheaper).

        Args:
            type: kundli, birth-chart, planet-positions, house-cusps, or ascendant
            birth_details: dict with datetime, latitude, longitude, timezone
        """
        return self._request("POST", f"/v2/astrology/{type}", data=birth_details)

    def get_dasha_v2(self, system: str, birth_details: Dict[str, Any]) -> Dict[str, Any]:
        """Get Dasha periods via V2 endpoint.

        Args:
            system: vimshottari-dasha, mahadasha, antardasha, pratyantardasha, or yogini-dasha
            birth_details: dict with datetime, latitude, longitude, timezone
        """
        return self._request("POST", f"/v2/astrology/{system}", data=birth_details)

    def get_doshas_v2(self, type: str, birth_details: Dict[str, Any]) -> Dict[str, Any]:
        """Get Dosha analysis via V2 endpoint.

        Args:
            type: mangal-dosha, kaal-sarp-dosha, sade-sati, pitru-dosha, or all-doshas
            birth_details: dict with datetime, latitude, longitude, timezone
        """
        return self._request("POST", f"/v2/astrology/{type}", data=birth_details)

    def get_compatibility_v2(
        self, type: str, male: Dict[str, Any], female: Dict[str, Any]
    ) -> Dict[str, Any]:
        """Get compatibility matching via V2 endpoint.

        Args:
            type: guna-milan, kundali-matching, ashtakoot-match, or nakshatra-porutham
            male: dict with datetime, latitude, longitude, timezone
            female: dict with datetime, latitude, longitude, timezone
        """
        return self._request("POST", f"/v2/astrology/{type}", data={"male": male, "female": female})

    def get_panchang(
        self,
        date: Optional[str] = None,
        latitude: Optional[float] = None,
        longitude: Optional[float] = None,
        timezone: Optional[str] = None
    ) -> Dict[str, Any]:
        """Get Panchang (Hindu calendar) data.

        Args:
            date: YYYY-MM-DD format (defaults to today)
            latitude: Location latitude (defaults to Delhi)
            longitude: Location longitude (defaults to Delhi)
            timezone: UTC offset (e.g., "+05:30"). NOT IANA names
        """
        params = {}
        if date: params["date"] = date
        if latitude is not None: params["latitude"] = latitude
        if longitude is not None: params["longitude"] = longitude
        if timezone: params["timezone"] = timezone
        return self._request("GET", "/v2/astrology/panchang", params=params)

    def get_muhurta_v2(
        self,
        type: str,
        latitude: Optional[float] = None,
        longitude: Optional[float] = None,
        timezone: Optional[str] = None
    ) -> Dict[str, Any]:
        """Get Muhurta (auspicious timing) via V2 endpoint.

        Args:
            type: choghadiya, hora, rahu-kaal, abhijit-muhurta, brahma-muhurta, etc.
            latitude: Location latitude
            longitude: Location longitude
            timezone: IANA timezone
        """
        params = {}
        if latitude is not None: params["latitude"] = latitude
        if longitude is not None: params["longitude"] = longitude
        if timezone: params["timezone"] = timezone
        return self._request("GET", f"/v2/astrology/{type}", params=params)

    def get_divisional_chart(self, chart: str, birth_details: Dict[str, Any]) -> Dict[str, Any]:
        """Get divisional chart (D2-D60).

        Args:
            chart: navamsa, dashamsa, saptamsa, dwadashamsa, etc.
            birth_details: dict with datetime, latitude, longitude, timezone
        """
        return self._request("POST", f"/v2/astrology/{chart}", data=birth_details)

    def get_prediction(
        self,
        period: str,
        rashi: Optional[str] = None,
        birth_details: Optional[Dict[str, Any]] = None
    ) -> Dict[str, Any]:
        """Get predictions (daily/weekly/monthly/quarterly/yearly).

        Args:
            period: daily, weekly, monthly, quarterly, or yearly
            rashi: Zodiac sign (e.g., aries, taurus)
            birth_details: Alternative to rashi — derives moon sign automatically
        """
        data = {}
        if rashi: data["rashi"] = rashi
        if birth_details: data["birthDetails"] = birth_details
        return self._request("POST", f"/v2/astrology/prediction/{period}", data=data)

    def get_ashtakavarga(self, type: str, birth_details: Dict[str, Any]) -> Dict[str, Any]:
        """Get Ashtakavarga analysis.

        Args:
            type: ashtakavarga or sarvashtakavarga
            birth_details: dict with datetime, latitude, longitude, timezone
        """
        return self._request("POST", f"/v2/astrology/{type}", data=birth_details)

    def get_varshaphal(self, birth_details: Dict[str, Any], year: Optional[int] = None) -> Dict[str, Any]:
        """Get Varshaphal (annual horoscope / solar return).

        Args:
            birth_details: dict with datetime, latitude, longitude, timezone
            year: Year for the annual chart (defaults to current year)
        """
        data = {**birth_details}
        if year: data["year"] = year
        return self._request("POST", "/v2/astrology/varshaphal", data=data)

    def get_strength(self, type: str, birth_details: Dict[str, Any]) -> Dict[str, Any]:
        """Get planetary strength analysis.

        Args:
            type: shadbala, chandra-bala, or tara-bala
            birth_details: dict with datetime, latitude, longitude, timezone
        """
        return self._request("POST", f"/v2/astrology/{type}", data=birth_details)

    def get_numerology_v2(
        self,
        type: str,
        name: Optional[str] = None,
        birth_date: Optional[str] = None,
        system: Optional[str] = None,
        year: Optional[int] = None
    ) -> Dict[str, Any]:
        """Get numerology via V2 endpoint.

        Args:
            type: complete, life-path, destiny, personality, soul-urge, personal-year, or compatibility
            name: Full name (required for destiny/personality/soul-urge/complete)
            birth_date: YYYY-MM-DD (required for life-path/personal-year/complete)
            system: pythagorean or chaldean (default pythagorean)
            year: For personal-year calculation
        """
        data = {}
        if name: data["name"] = name
        if birth_date: data["birthDate"] = birth_date
        if system: data["system"] = system
        if year: data["year"] = year
        return self._request("POST", f"/v2/astrology/numerology/{type}", data=data)

    # ═══════════════════════════════════════════
    # Horoscope
    # ═══════════════════════════════════════════

    def get_horoscope(
        self,
        sign: str,
        period: str = "daily",
        system: str = "vedic"
    ) -> Dict[str, Any]:
        """Get horoscope for a zodiac sign.

        Args:
            sign: aries, taurus, gemini, etc.
            period: daily, weekly, or monthly
            system: vedic or western
        """
        base = "/v2/western" if system == "western" else "/v2/astrology"
        path = f"{base}/horoscope/{sign}" if period == "daily" else f"{base}/horoscope/{sign}/{period}"
        return self._request("GET", path)

    # ═══════════════════════════════════════════
    # Western Astrology
    # ═══════════════════════════════════════════

    def get_western_transits(
        self,
        birth_details: Dict[str, Any],
        transit_date_time: Optional[str] = None
    ) -> Dict[str, Any]:
        """Get Western transit chart and aspects.

        Args:
            birth_details: dict with datetime, latitude, longitude, timezone
            transit_date_time: ISO 8601 datetime for transits (defaults to now)
        """
        data = {**birth_details}
        if transit_date_time: data["transitDateTime"] = transit_date_time
        return self._request("POST", "/v2/western/transit-chart", data=data)

    def get_western_progressions(
        self,
        birth_details: Dict[str, Any],
        progression_date: Optional[str] = None
    ) -> Dict[str, Any]:
        """Get Western secondary progressions.

        Args:
            birth_details: dict with datetime, latitude, longitude, timezone
            progression_date: ISO 8601 date for progressions (defaults to now)
        """
        data = {**birth_details}
        if progression_date: data["progressionDate"] = progression_date
        return self._request("POST", "/v2/western/progressions", data=data)

    def get_western_solar_return(
        self,
        birth_details: Dict[str, Any],
        year: Optional[int] = None
    ) -> Dict[str, Any]:
        """Get Western solar return chart.

        Args:
            birth_details: dict with datetime, latitude, longitude, timezone
            year: Year for solar return (defaults to current year)
        """
        data = {**birth_details}
        if year: data["year"] = year
        return self._request("POST", "/v2/western/solar-return", data=data)

    def get_western_relationship(
        self,
        type: str,
        person1: Dict[str, Any],
        person2: Dict[str, Any]
    ) -> Dict[str, Any]:
        """Get Western relationship analysis.

        Args:
            type: synastry, synastry-aspects, composite, or composite-aspects
            person1: dict with datetime, latitude, longitude, timezone
            person2: dict with datetime, latitude, longitude, timezone
        """
        return self._request("POST", f"/v2/western/{type}", data={"person1": person1, "person2": person2})

    # ═══════════════════════════════════════════
    # Convenience Methods (Common Use Cases)
    # ═══════════════════════════════════════════

    def get_panchang_today(self) -> Dict[str, Any]:
        """Get today's panchang (no parameters needed)."""
        return self._request("GET", "/v2/astrology/panchang/today")

    def get_sade_sati(self, birth_details: Dict[str, Any]) -> Dict[str, Any]:
        """Get Sade Sati status for a birth chart."""
        return self.get_doshas_v2("sade-sati", birth_details)

    def get_chandrashtama(self, birth_details: Dict[str, Any]) -> Dict[str, Any]:
        """Get Chandrashtama (Moon transit) periods."""
        return self._request("POST", "/v2/astrology/chandrashtama", data=birth_details)

    def get_kundli(self, birth_details: Dict[str, Any]) -> Dict[str, Any]:
        """Get Kundli (complete birth chart with all details)."""
        return self.get_birth_chart_v2("kundli", birth_details)

    def get_navamsa(self, birth_details: Dict[str, Any]) -> Dict[str, Any]:
        """Get Navamsa (D9) chart."""
        return self.get_divisional_chart("navamsa", birth_details)

    def get_guna_milan(self, male: Dict[str, Any], female: Dict[str, Any]) -> Dict[str, Any]:
        """Get Guna Milan (36-point matching)."""
        return self.get_compatibility_v2("guna-milan", male, female)

    def get_vimshottari_dasha(self, birth_details: Dict[str, Any]) -> Dict[str, Any]:
        """Get Vimshottari Dasha timeline."""
        return self.get_dasha_v2("vimshottari-dasha", birth_details)

    def get_daily_prediction(self, rashi: str) -> Dict[str, Any]:
        """Get daily prediction for a zodiac sign."""
        return self.get_prediction("daily", rashi=rashi)

    def get_shadbala(self, birth_details: Dict[str, Any]) -> Dict[str, Any]:
        """Get Shadbala (6-fold planetary strength)."""
        return self.get_strength("shadbala", birth_details)

    # ═══════════════════════════════════════════
    # Utility
    # ═══════════════════════════════════════════

    def get_conversations(self) -> Dict[str, Any]:
        """List all conversations."""
        return self._request("GET", "/api/v1/conversations")

    def delete_conversation(self, conversation_id: str) -> None:
        """Delete a conversation.

        Args:
            conversation_id: The conversation ID to delete
        """
        self._request("DELETE", f"/api/v1/conversations/{conversation_id}")

    def get_usage(self) -> Dict[str, Any]:
        """Get wallet usage and balance."""
        return self._request("GET", "/api/v1/usage/wallet")

    def batch_process(
        self,
        queries: list[Dict[str, Any]]
    ) -> list[QuestionResponse]:
        """
        Process multiple queries in batch for efficiency.

        Args:
            queries: List of query dictionaries with 'question' and 'birth_details'

        Returns:
            List of QuestionResponse objects
        """
        results = []
        for query in queries:
            response = self.ask_question(
                question=query["question"],
                birth_details=query["birth_details"],
                language=query.get("language")
            )
            results.append(response)

        return results

    def __repr__(self) -> str:
        return f"VedikaClient(api_key={'***' + self.api_key[-4:] if self.api_key else 'None'})"
