from __future__ import annotations

import os
import sys
import time
import webbrowser
from typing import Any

import requests

from ._credentials import delete_token, is_token_expired, load_scopes, load_token, save_token

DEFAULT_SERVICE_URL = "https://it.kxxxl.com"

_POLL_INTERVAL = 2.0
_POLL_TIMEOUT = 120.0


def _service_url() -> str:
    return os.environ.get("FEISHU_SERVICE_URL", DEFAULT_SERVICE_URL).rstrip("/")


def _post(path: str, **kwargs) -> Any:
    url = f"{_service_url()}{path}"
    resp = requests.post(url, timeout=15, **kwargs)
    resp.raise_for_status()
    return resp.json()


def _get(path: str, **kwargs) -> Any:
    url = f"{_service_url()}{path}"
    resp = requests.get(url, timeout=15, **kwargs)
    resp.raise_for_status()
    return resp.json()


def login(
    *,
    open_browser: bool = True,
    extra_scopes: list[str] | None = None,
) -> str:
    body: dict = {}
    if extra_scopes:
        body["extra_scopes"] = extra_scopes

    data = _post("/webhook/auth/session", json=body)
    session_id: str = data["session_id"]
    auth_url: str = data["auth_url"]

    print(f"🔗 授权 URL: {auth_url}", file=sys.stderr)
    if open_browser:
        print("🚀 正在打开浏览器...", file=sys.stderr)
        webbrowser.open(auth_url)
    else:
        print("请手动访问以上 URL 完成授权", file=sys.stderr)

    print("⏳ 等待授权回调...", file=sys.stderr)
    deadline = time.time() + _POLL_TIMEOUT
    while time.time() < deadline:
        time.sleep(_POLL_INTERVAL)
        try:
            result = _get(f"/webhook/auth/session/{session_id}")
        except requests.HTTPError as e:
            status = e.response.status_code if e.response is not None else 0
            if status == 404:
                raise RuntimeError("授权 session 不存在或已过期，请重试")
            if status == 410:
                raise RuntimeError("授权 session 已超时，请重试")
            if status == 400:
                detail = e.response.json().get("detail", "未知错误")
                raise RuntimeError(f"授权失败: {detail}")
            raise

        if result.get("status") == "done":
            access_token: str = result["access_token"]
            refresh_token: str = result.get("refresh_token", "")
            expires_at: float = float(result.get("expires_at", time.time() + 7200))
            granted_scopes: list[str] = result.get("scopes", extra_scopes or [])
            save_token(access_token, refresh_token, expires_at, scopes=granted_scopes)
            print("✅ 授权成功，token 已缓存", file=sys.stderr)
            return access_token

    raise RuntimeError(f"授权等待超时（{int(_POLL_TIMEOUT)}s），请重试")


def _refresh(refresh_token: str) -> str:
    data = _post("/webhook/auth/refresh", json={"refresh_token": refresh_token})
    access_token: str = data["access_token"]
    new_refresh: str = data.get("refresh_token", refresh_token)
    expires_at: float = float(data.get("expires_at", time.time() + 7200))
    save_token(access_token, new_refresh, expires_at)
    return access_token


def get_token(*, force_refresh: bool = False, open_browser: bool = True) -> str:
    access_token, refresh_token, expires_at = load_token()

    if not force_refresh and access_token and not is_token_expired(expires_at):
        return access_token

    if refresh_token and not force_refresh:
        try:
            return _refresh(refresh_token)
        except (requests.HTTPError, RuntimeError) as e:
            print(f"⚠️  自动刷新失败 ({e})，重新授权...", file=sys.stderr)

    return login(open_browser=open_browser)


def ensure_scopes(
    required: list[str],
    *,
    open_browser: bool = True,
    token: str | None = None,
) -> str:
    current_token = token or get_token()
    have = set(load_scopes())
    missing = [s for s in required if s not in have]
    if not missing:
        return current_token

    print(f"⚠️  当前 token 缺少权限: {' '.join(missing)}", file=sys.stderr)
    print("🔐 正在请求补充授权...", file=sys.stderr)
    return login(open_browser=open_browser, extra_scopes=missing)


def logout() -> bool:
    return delete_token()


def token_status() -> dict:
    access_token, refresh_token, expires_at = load_token()
    now = time.time()
    if not access_token:
        return {"status": "no_token"}
    remaining = int(expires_at - now)
    return {
        "status": "expired" if remaining <= 0 else "valid",
        "expires_in_seconds": max(0, remaining),
        "has_refresh_token": bool(refresh_token),
        "scopes": load_scopes(),
        "service_url": _service_url(),
    }
