from __future__ import annotations

import logging
from typing import Any

import httpx

from app.integrations.base import BaseClient


logger = logging.getLogger("connecthub.integrations.seeyon")


class SeeyonClient(BaseClient):
    """
    致远 OA REST Client：
    - POST /seeyon/rest/token 获取 token（id）
    - 业务请求 header 自动携带 token
    - 遇到 401/Invalid token 自动刷新 token 并重试一次
    """

    def __init__(self, *, base_url: str, rest_user: str, rest_password: str, loginName: str | None = None) -> None:
        super().__init__(base_url=base_url)
        self.rest_user = rest_user
        self.rest_password = rest_password
        self.loginName = loginName
        self._token: str | None = None

    def authenticate(self) -> str:
        body: dict[str, Any] = {
            "userName": self.rest_user,
            "password": self.rest_password,
        }
        if self.loginName:
            body["loginName"] = self.loginName

        # 文档：POST /seeyon/rest/token
        resp = super().request(
            "POST",
            "/seeyon/rest/token",
            json=body,
            headers={"Accept": "application/json", "Content-Type": "application/json"},
        )
        data = resp.json()
        token = str(data.get("id", "") or "")
        if not token or token == "-1":
            raise RuntimeError("Seeyon auth failed (token id missing or -1)")

        self._token = token
        logger.info("Seeyon token acquired")
        return token

    def _get_token(self) -> str:
        return self._token or self.authenticate()

    def request(self, method: str, path: str, **kwargs: Any) -> httpx.Response:  # type: ignore[override]
        token = self._get_token()
        headers = dict(kwargs.pop("headers", {}) or {})
        headers["token"] = token

        try:
            return super().request(method, path, headers=headers, **kwargs)
        except httpx.HTTPStatusError as e:
            # token 失效：401 或返回包含 Invalid token
            resp = e.response
            text = ""
            try:
                text = resp.text or ""
            except Exception:
                text = ""
            if resp.status_code == 401 or ("Invalid token" in text):
                logger.info("Seeyon token invalid, refreshing and retrying once")
                self._token = None
                token2 = self._get_token()
                headers["token"] = token2
                # 仅重试一次；仍失败则抛出
                return super().request(method, path, headers=headers, **kwargs)
            raise

    def export_cap4_form_soap(
        self,
        *,
        templateCode: str,
        senderLoginName: str | None = None,
        rightId: str | None = None,
        doTrigger: str | bool | None = None,
        param: str | None = None,
        extra: dict[str, Any] | None = None,
    ) -> httpx.Response:
        """
        无流程表单导出（CAP4）：
        POST /seeyon/rest/cap4/form/soap/export

        返回 httpx.Response，调用方可自行读取 resp.text / resp.headers 等信息。
        """
        body: dict[str, Any] = {"templateCode": templateCode}
        if senderLoginName:
            body["senderLoginName"] = senderLoginName
        if rightId:
            body["rightId"] = rightId
        if doTrigger is not None:
            body["doTrigger"] = doTrigger
        if param is not None:
            body["param"] = param
        if extra:
            # 兜底扩展字段：仅当 key 不冲突时注入，避免覆盖已显式指定的参数
            for k, v in extra.items():
                if k not in body:
                    body[k] = v

        return self.request(
            "POST",
            "/seeyon/rest/cap4/form/soap/export",
            json=body,
            headers={"Content-Type": "application/json"},
        )


