"""Thin Airtable REST client for skills that run outside the MCP-tool context (cron, test harness).

Authentication via env var `AIRTABLE_PAT` (or `AIRTABLE_API_KEY` legacy). Skills invoked
through the Cowork/CC MCP layer should use the MCP tools directly — this client is the
fallback for headless runs.
"""

from __future__ import annotations

import json
import os
import time
import urllib.error
import urllib.parse
import urllib.request
from typing import Any, Iterator


API_BASE = "https://api.airtable.com/v0"


def _token() -> str:
    tok = os.environ.get("AIRTABLE_PAT") or os.environ.get("AIRTABLE_API_KEY")
    if not tok:
        raise RuntimeError("AIRTABLE_PAT (or AIRTABLE_API_KEY) env var not set.")
    return tok


def _request(method: str, url: str, payload: dict | None = None) -> dict:
    """One-shot HTTP request. Raises urllib.error.HTTPError on non-2xx; caller handles."""
    body = json.dumps(payload).encode("utf-8") if payload is not None else None
    headers = {
        "Authorization": f"Bearer {_token()}",
        "Content-Type": "application/json",
    }
    req = urllib.request.Request(url, data=body, headers=headers, method=method)
    with urllib.request.urlopen(req, timeout=30) as resp:
        raw = resp.read()
    return json.loads(raw) if raw else {}


def list_records(
    base_id: str,
    table_id: str,
    filter_by_formula: str | None = None,
    fields: list[str] | None = None,
    page_size: int = 100,
) -> Iterator[dict]:
    """Yield records (each a dict with `id` + `fields`) across all pages."""
    offset: str | None = None
    while True:
        params: dict[str, Any] = {"pageSize": page_size}
        if filter_by_formula:
            params["filterByFormula"] = filter_by_formula
        if fields:
            params["fields[]"] = fields
        if offset:
            params["offset"] = offset
        url = f"{API_BASE}/{base_id}/{table_id}?{urllib.parse.urlencode(params, doseq=True)}"
        data = _retry_get(url)
        for r in data.get("records", []):
            yield r
        offset = data.get("offset")
        if not offset:
            return


def _retry_get(url: str, max_retries: int = 5) -> dict:
    """GET with exponential backoff on 429/5xx."""
    delay = 0.5
    for attempt in range(max_retries):
        try:
            return _request("GET", url)
        except urllib.error.HTTPError as e:
            if e.code in (429, 500, 502, 503, 504) and attempt < max_retries - 1:
                time.sleep(delay)
                delay *= 2
                continue
            raise


def create_records(base_id: str, table_id: str, records: list[dict]) -> list[dict]:
    """Create up to 10 records per call (Airtable limit). Caller chunks if more."""
    if len(records) > 10:
        raise ValueError(f"Airtable create_records limit is 10 per call; got {len(records)}.")
    url = f"{API_BASE}/{base_id}/{table_id}"
    payload = {"records": [{"fields": r} for r in records]}
    data = _request("POST", url, payload)
    return data.get("records", [])


def update_records(base_id: str, table_id: str, updates: list[dict]) -> list[dict]:
    """Patch records. `updates` is a list of {"id": "rec...", "fields": {...}}."""
    if len(updates) > 10:
        raise ValueError(f"Airtable update_records limit is 10 per call; got {len(updates)}.")
    url = f"{API_BASE}/{base_id}/{table_id}"
    data = _request("PATCH", url, {"records": updates})
    return data.get("records", [])


def get_record(base_id: str, table_id: str, record_id: str) -> dict:
    url = f"{API_BASE}/{base_id}/{table_id}/{record_id}"
    return _retry_get(url)
