"""税务 STANDARD 最小接入示例。默认读取现有订单；--create 才创建授权。"""
import hashlib
import hmac
import json
import os
import sys
import time
import uuid
from pathlib import Path
from urllib.parse import quote
from urllib.request import Request, urlopen


def request(method, path, payload=None):
    body = None if payload is None else json.dumps(
        payload, ensure_ascii=False, separators=(",", ":")
    ).encode("utf-8")
    headers = {"X-Channel-Key": os.environ["RPA_CHANNEL_KEY"]}
    if body is not None:
        headers["Content-Type"] = "application/json; charset=utf-8"
    if path.startswith("/api/channel/data/"):
        secret = os.environ["RPA_CALLBACK_SECRET"]
        timestamp, nonce = str(int(time.time())), uuid.uuid4().hex
        canonical = "\n".join([
            timestamp, nonce, method, path, "",
            hashlib.sha256(body or b"").hexdigest(),
        ])
        signature = hmac.new(secret.encode(), canonical.encode(), hashlib.sha256).hexdigest()
        headers.update({
            "X-Channel-Timestamp": timestamp,
            "X-Channel-Nonce": nonce,
            "X-Channel-Signature": "sha256=" + signature,
        })
    base = os.environ["RPA_BASE_URL"].rstrip("/")
    # urlopen 对非 2xx 响应抛出 HTTPError；停止后按错误码处理，不循环重试鉴权失败。
    with urlopen(Request(base + path, body, headers, method=method), timeout=60) as response:
        result = json.load(response)
    if result.get("encrypted") is True:
        raise RuntimeError("本示例使用 NONE 订单；SM4 订单请先按加密指南认证解密。")
    return result


def main():
    order_id = os.environ["RPA_ORDER_ID"]
    if "--create" in sys.argv:
        link = request("POST", "/api/v1/channel/authorization-links", {
            "external_order_id": order_id,
            "enterprise_name": os.environ["RPA_ENTERPRISE_NAME"],
            "taxpayer_id": os.environ["RPA_TAXPAYER_ID"],
            "collect_tax": True,
            "collect_invoice": True,
            "tax_data_types": ["ALL", "STANDARD"],
            "invoice_data_types": ["ALL", "STANDARD"],
            "invoice_period": "LAST_24_MONTHS",
            "output_encryption": "NONE",
        })
        print("请原样打开授权链接：", link["authorization_url"], flush=True)

    order_path = "/api/v1/channel/orders/" + quote(order_id, safe="")
    deadline = time.monotonic() + 7200
    while time.monotonic() < deadline:
        order = request("GET", order_path)
        if order.get("job_id"):
            product = request("GET", order_path + "/products/TAX")
            if product["state"] == "SUCCEEDED" and product["available"] is True:
                result = request("POST", "/api/channel/data/tax/standardTable", {
                    "channelCode": os.environ["RPA_CHANNEL_CODE"],
                    "channelOrderNo": order_id,
                    "orderNo": product["job_id"],
                })
                if result.get("success") is True:
                    # 保存完整响应及来源身份、质量信息，不能把 WARNING 当成完整覆盖。
                    saved = {"order": order, "product": product, "response": result}
                    Path("tax-standard.json").write_text(
                        json.dumps(saved, ensure_ascii=False, indent=2), encoding="utf-8"
                    )
                    print("已保存税务 STANDARD 到 tax-standard.json；质量状态：", product.get("qualityStatus"))
                    print("整单状态：", order["state"], "；税务取数完成不代表整单校验通过。")
                    if order["state"] == "ERROR":
                        print("整单存在失败结果：", order.get("errorCode"), order.get("reason"),
                              "；已保存订单、产品及取数结果，请结合 revenueCheck 和质量信息处理。")
                    return
                if result.get("code") != 60003:
                    raise RuntimeError(f"取数失败：{result.get('code')} {result.get('msg')}")
            elif product["state"] in {"FAILED", "SKIPPED"}:
                raise RuntimeError(f"税务产品：{product['state']} {product.get('error_code')}")
        if order["state"] == "ERROR":
            raise RuntimeError(f"本轮订单失败：{order.get('errorCode')}；请查询产品和质量明细。")
        time.sleep(20)
    raise TimeoutError("等待已达 2 小时；保留原订单排查，不自动创建新订单。")


if __name__ == "__main__":
    main()
