import json
import os
import sys
import time
import urllib.error
import urllib.parse
import urllib.request


OPEN_FEISHU_BASE = "https://open.feishu.cn/open-apis"


def configure_output_encoding():
    for stream in (sys.stdout, sys.stderr):
        if hasattr(stream, "reconfigure"):
            stream.reconfigure(encoding="utf-8", errors="replace")


class LarkApiError(RuntimeError):
    def __init__(self, message, code=None, log_id=None):
        super().__init__(message)
        self.code = code
        self.log_id = log_id


def load_dotenv(path=None):
    if path is None:
        candidates = [".env", os.path.join(os.path.dirname(__file__), ".env")]
    else:
        candidates = [path]

    for candidate in candidates:
        _load_dotenv_file(candidate)


def _load_dotenv_file(path):
    if not os.path.exists(path):
        return

    with open(path, "r", encoding="utf-8") as f:
        for raw_line in f:
            line = raw_line.strip()
            if not line or line.startswith("#") or "=" not in line:
                continue

            key, value = line.split("=", 1)
            key = key.strip()
            value = value.strip()
            if not key or key in os.environ:
                continue

            if len(value) >= 2 and value[0] == value[-1] and value[0] in ("'", '"'):
                value = value[1:-1]
            os.environ[key] = value


def require_env(name):
    value = os.environ.get(name)
    if not value:
        raise RuntimeError(f"Missing required environment variable: {name}")
    return value


def optional_env(name, default=""):
    return os.environ.get(name, default)


def api_request(method, path, token=None, query=None, body=None, timeout=20):
    url = f"{OPEN_FEISHU_BASE}{path}"
    if query:
        url = f"{url}?{urllib.parse.urlencode(query)}"

    data = None
    headers = {"Content-Type": "application/json; charset=utf-8"}
    if token:
        headers["Authorization"] = f"Bearer {token}"
    if body is not None:
        data = json.dumps(body, ensure_ascii=False).encode("utf-8")

    request = urllib.request.Request(url, data=data, headers=headers, method=method)
    try:
        with urllib.request.urlopen(request, timeout=timeout) as response:
            raw = response.read().decode("utf-8")
            log_id = response.headers.get("X-Tt-Logid")
    except urllib.error.HTTPError as exc:
        raw = exc.read().decode("utf-8", errors="replace")
        log_id = exc.headers.get("X-Tt-Logid")
        raise LarkApiError(
            f"HTTP {exc.code}: {raw}",
            log_id=log_id,
        ) from exc
    except urllib.error.URLError as exc:
        raise LarkApiError(f"Network error: {exc}") from exc

    try:
        payload = json.loads(raw)
    except json.JSONDecodeError as exc:
        raise LarkApiError(f"Invalid JSON response: {raw}", log_id=log_id) from exc

    code = payload.get("code", 0)
    if code != 0:
        msg = payload.get("msg", "unknown error")
        raise LarkApiError(f"Lark API error {code}: {msg}", code=code, log_id=log_id)

    return payload


_token_cache = {"token": None, "expires_at": 0}


def get_tenant_access_token():
    now = int(time.time())
    if _token_cache["token"] and _token_cache["expires_at"] - now > 300:
        return _token_cache["token"]

    app_id = require_env("LARK_APP_ID")
    app_secret = require_env("LARK_APP_SECRET")
    payload = api_request(
        "POST",
        "/auth/v3/tenant_access_token/internal",
        body={"app_id": app_id, "app_secret": app_secret},
    )
    token = payload.get("tenant_access_token")
    expire = int(payload.get("expire", 0))
    if not token:
        raise LarkApiError("tenant_access_token missing in response")

    _token_cache["token"] = token
    _token_cache["expires_at"] = now + expire
    return token
