"""VedicSpace API — Python client (zero dependencies, stdlib only).

The free, cited Jyotish engine: every verdict endpoint returns `prove_refs`,
the classical verse behind the ruling. Everything is free — no card, no tiers.
An optional free API key raises your rate-limit headroom 4x:

    >>> vs = VedicSpace()
    >>> vs.register_key("you@example.com", "my-app")   # instant, free
    {'success': True, 'api_key': 'vsk_...', ...}
    >>> vs = VedicSpace(api_key="vsk_...")

Docs: https://vedicspace.com/developers · Limits: GET /api/v2/limits
Full endpoint list: https://vedicspace.com/openapi.json (219 routes).

Usage:
    vs = VedicSpace()
    vs.panchang("2026-08-01", 28.6139, 77.2090)
    vs.kundli("1990-05-15", "10:30", 28.6139, 77.2090)
    vs.dashakoot(groom={"date": "1990-05-15", "time": "10:30",
                        "latitude": 28.61, "longitude": 77.21},
                 bride={"date": "1992-08-20", "time": "14:15",
                        "latitude": 19.07, "longitude": 72.87})
    vs.get("/api/v2/limits")                       # any GET route
    vs.post("/api/v2/shadbala", date="1990-05-15", # any POST route
            time="10:30", latitude=28.61, longitude=77.21)
"""
import json
import urllib.request
import urllib.parse

__version__ = "1.0.0"
BASE_URL = "https://vedicspace.com"


class VedicSpaceError(RuntimeError):
    """Raised when the API returns a non-2xx response."""


class VedicSpace:
    def __init__(self, api_key: str = "", base_url: str = BASE_URL, timeout: int = 60):
        self.api_key = api_key
        self.base_url = base_url.rstrip("/")
        self.timeout = timeout

    # -- transport ----------------------------------------------------------
    def _headers(self):
        h = {"Content-Type": "application/json", "User-Agent": f"vedicspace-py/{__version__}"}
        if self.api_key:
            h["X-API-Key"] = self.api_key
        return h

    def get(self, path: str, **params):
        url = self.base_url + path
        if params:
            url += "?" + urllib.parse.urlencode(params)
        req = urllib.request.Request(url, headers=self._headers())
        return self._send(req)

    def post(self, path: str, **body):
        req = urllib.request.Request(
            self.base_url + path, data=json.dumps(body).encode(),
            headers=self._headers(), method="POST")
        return self._send(req)

    def _send(self, req):
        try:
            with urllib.request.urlopen(req, timeout=self.timeout) as r:
                return json.load(r)
        except urllib.error.HTTPError as e:
            try:
                detail = json.load(e)
            except Exception:
                detail = e.read()[:500]
            raise VedicSpaceError(f"HTTP {e.code}: {detail}") from None

    # -- conveniences (each is just a thin wrapper over get/post) -----------
    def register_key(self, email: str, app_name: str = ""):
        """Instant free API key — identity, never a paywall."""
        return self.post("/api/v2/keys/register", email=email, app_name=app_name)

    def limits(self):
        return self.get("/api/v2/limits")

    def panchang(self, date: str, latitude: float, longitude: float):
        return self.post("/api/v2/panchanga", date=date,
                         latitude=latitude, longitude=longitude)

    def kundli(self, date: str, time: str, latitude: float, longitude: float):
        return self.post("/api/v2/kundli", date=date, time=time,
                         latitude=latitude, longitude=longitude)

    def match_ashtakoot(self, groom: dict, bride: dict):
        """36-guna North-Indian matching with dosha flags."""
        return self.post("/api/v2/koota-extended", groom=groom, bride=bride)

    def dashakoot(self, groom: dict, bride: dict):
        """Ten South-Indian poruthams — the only cited porutham API."""
        return self.post("/api/v2/dashakoot", groom=groom, bride=bride)

    def match_batch(self, proband: dict, candidates: list,
                    proband_role: str = "groom", system: str = "ashtakoota"):
        """One profile vs many (max 50/call), ranked."""
        return self.post("/api/v2/match/batch", proband=proband,
                         candidates=candidates, proband_role=proband_role,
                         system=system)

    def manglik(self, date: str, time: str, latitude: float, longitude: float):
        """3-lens Manglik with explicit cancellation rules."""
        return self.post("/api/v2/mangal-dosha", date=date, time=time,
                         latitude=latitude, longitude=longitude)

    def kalasarpa(self, date: str, time: str, latitude: float, longitude: float):
        return self.post("/api/v2/kalasarpa", date=date, time=time,
                         latitude=latitude, longitude=longitude)

    def bphs(self, chapter: int):
        """Our clean-room BPHS translation, per chapter — a category of one."""
        return self.get(f"/api/v2/bphs/chapter/{chapter}")


if __name__ == "__main__":
    vs = VedicSpace()
    p = vs.panchang("2026-08-01", 28.6139, 77.2090)
    print(json.dumps(p, indent=2)[:800])
