"""给飞书「自动化账户」表地域列设置下拉多选（选项来自 regions_all.json）。

用法:
    cd examples/auto_put_ad_mini
    python3 setup_region_dropdown.py             # 设置下拉（幂等，可重复跑）
    python3 setup_region_dropdown.py --dry-run   # 只打印选项，不调飞书

选项集(顺序):
    1. 不限(全国) / 排地域(回退投放模板) / 中国未知(156)
    2. 其余省级/直辖市/特别行政区按拼音排序(依赖 pypinyin,缺失回退 Unicode)
    排除: 市级/区级、国外(9000)、中国(1156)

说明:
    本脚本刻意保持自包含(仅依赖 httpx + python-dotenv)，不引入 agent 框架，
    方便在任意环境独立执行。token 获取逻辑与 tools/feishu_doc 保持一致。
"""

from __future__ import annotations

import argparse
import json
import os
import time
from pathlib import Path

import httpx
from dotenv import load_dotenv

# 拼音排序依赖（可选）：未安装时回退到 Unicode 排序
try:
    from pypinyin import lazy_pinyin as _lazy_pinyin
except ImportError:
    _lazy_pinyin = None

_HERE = Path(__file__).parent
load_dotenv(_HERE / ".env")

# 飞书「自动化账户」表
DEFAULT_SPREADSHEET_TOKEN = "D8f4sKEb2hfBnNttX1ucZTklnHf"
DEFAULT_SHEET_ID = "y3uCcz"
DEFAULT_RANGE = "A1:N300"

FEISHU_BASE_URL = "https://open.feishu.cn/open-apis"
_REGION_DICTIONARY_PATH = (
    _HERE / "data" / "tencent_constants" / "regions_all.json"
)

# 顶层节点中排除的 id: 国外(9000)、中国(1156,整包节点,与"不限"语义重叠)
_EXCLUDED_TOP_IDS = {9000, 1156}
# 固定前导项，保持 parse_geo_location_config 语义（不限→全国 / 排地域→模板），
# 中国未知(156)紧随其后，其余省级按名称排序。
_SPECIAL_OPTIONS = ["不限", "排地域", "中国未知"]
_EXCLUDED_NAMES = {"中国未知"}
# 下拉选项胶囊颜色（与 conditionValues 一一对应；highlightValidData=true 时飞书强制要求）
_COLOR_PALETTE = [
    "#1FB6C1", "#F006C2", "#FB16C3", "#FFB6C1", "#A6D6FF",
    "#FFB84D", "#4CD3A0", "#8A7DFF", "#FF8FA3", "#5B8DEF",
    "#F7B500", "#00C2A8", "#B08CF9", "#FF7A45", "#36CFC9",
    "#597EF7", "#F759AB", "#7CB305", "#FAAD14", "#13C2C2",
]
# 表头在第 2 行，数据从第 3 行开始
HEADER_ROW_INDEX = 1
DATA_START_INDEX = 2
# 地域列下拉覆盖到第 300 行（与 sync 的 DEFAULT_RANGE A1:N300 对齐）
MAX_ROW = 300

# tenant token 缓存
_token_cache: dict = {}
_TOKEN_TTL = 1500  # 25 分钟


def _get_tenant_token() -> str:
    """获取飞书 tenant_access_token（带缓存，逻辑同 tools.feishu_doc）。"""
    cached = _token_cache.get("tenant")
    if cached and time.time() - cached["ts"] < _TOKEN_TTL:
        return cached["token"]
    app_id = os.getenv("FEISHU_APP_ID", "cli_a955e97067f85cb3")
    app_secret = os.getenv("FEISHU_APP_SECRET", "NQaG4ci1plXRDTgwCqrLJgMLLoA2tdF8")
    resp = httpx.post(
        f"{FEISHU_BASE_URL}/auth/v3/tenant_access_token/internal",
        json={"app_id": app_id, "app_secret": app_secret},
        timeout=30,
    )
    data = resp.json()
    if data.get("code") != 0:
        raise RuntimeError(f"获取飞书 token 失败: {data}")
    token = data["tenant_access_token"]
    _token_cache["tenant"] = {"token": token, "ts": time.time()}
    return token


def _auth_headers(token: str) -> dict:
    return {"Authorization": f"Bearer {token}"}


def _pinyin_sort_key(name: str) -> str:
    """按拼音排序键；pypinyin 缺失时回退 Unicode 码位排序。"""
    if _lazy_pinyin is not None:
        return "".join(_lazy_pinyin(name))
    return name


def build_region_options() -> list[str]:
    """构建下拉选项：不限、排地域、中国未知固定在前，其余省级按拼音排序。"""
    regions = json.loads(_REGION_DICTIONARY_PATH.read_text(encoding="utf-8"))
    names: list[str] = []
    for r in regions:
        if r.get("parent_id") != 0:
            continue
        rid = int(r["id"])
        if rid in _EXCLUDED_TOP_IDS:
            continue
        name = str(r.get("name") or "").strip()
        if not name or name in _EXCLUDED_NAMES:
            continue
        names.append(name)
    names.sort(key=_pinyin_sort_key)
    return _SPECIAL_OPTIONS + names


def _column_letter(idx: int) -> str:
    """0-based 列索引转列字母（A→0，Z→25，AA→26）。"""
    letters = ""
    idx += 1
    while idx:
        idx, rem = divmod(idx - 1, 26)
        letters = chr(65 + rem) + letters
    return letters


def _cell_text(value) -> str:
    """飞书单元格值（可能是富文本 list/dict）转纯文本。"""
    if value is None:
        return ""
    if isinstance(value, list):
        parts = []
        for item in value:
            if isinstance(item, dict):
                parts.append(str(item.get("text") or item.get("link") or ""))
            else:
                parts.append(str(item))
        return "".join(parts).strip()
    if isinstance(value, dict):
        return str(value.get("text") or value.get("link") or "").strip()
    return str(value).strip()


def _get_sheet_row_count(spreadsheet_token: str, sheet_id: str) -> int:
    """查询 sheet 的实际网格行数（数据验证 range 不能超出网格）。"""
    token = _get_tenant_token()
    resp = httpx.get(
        f"{FEISHU_BASE_URL}/sheets/v2/spreadsheets/{spreadsheet_token}/metainfo",
        headers=_auth_headers(token),
        timeout=30,
    )
    resp.raise_for_status()
    data = resp.json()
    if data.get("code") != 0:
        raise RuntimeError(f"读取表格元数据失败: {json.dumps(data, ensure_ascii=False)[:500]}")
    for s in (data.get("data") or {}).get("sheets") or []:
        if s.get("sheetId") == sheet_id:
            return int(s.get("rowCount") or 0)
    raise RuntimeError(f"未找到 sheet: {sheet_id}")


def _find_geo_column_letter(
    spreadsheet_token: str, sheet_id: str, cell_range: str
) -> str:
    """读取表头，返回地域列字母（优先「地域」，回退「投放地域」）。"""
    token = _get_tenant_token()
    url = (
        f"{FEISHU_BASE_URL}/sheets/v2/spreadsheets/{spreadsheet_token}/values/"
        f"{sheet_id}!{cell_range}"
    )
    resp = httpx.get(url, headers=_auth_headers(token), timeout=30)
    resp.raise_for_status()
    data = resp.json()
    if data.get("code") != 0:
        raise RuntimeError(f"读取飞书配置表失败: {json.dumps(data, ensure_ascii=False)[:500]}")
    values = (data.get("data") or {}).get("valueRange") or {}
    rows = values.get("values") or []
    if len(rows) <= HEADER_ROW_INDEX:
        raise RuntimeError("飞书配置表缺少表头行")
    headers = [_cell_text(v) for v in rows[HEADER_ROW_INDEX]]
    mapping = {name: idx for idx, name in enumerate(headers) if name}
    col = "地域" if "地域" in mapping else "投放地域"
    if col not in mapping:
        raise RuntimeError(f"飞书表缺少地域列; 当前表头={headers}")
    return _column_letter(mapping[col])


def _set_dropdown(
    spreadsheet_token: str,
    sheet_id: str,
    col_letter: str,
    options: list[str],
    row_count: int,
    dry_run: bool = False,
) -> dict:
    """幂等设置下拉：PUT 更新接口同时支持创建/更新，直接覆盖目标 range。"""
    end_row = min(max(row_count, DATA_START_INDEX + 1), MAX_ROW)
    data_range = f"{sheet_id}!{col_letter}{DATA_START_INDEX + 1}:{col_letter}{end_row}"

    if dry_run:
        return {"range": data_range, "options": len(options), "dry_run": True}

    token = _get_tenant_token()
    headers = _auth_headers(token)
    url = (
        f"{FEISHU_BASE_URL}/sheets/v2/spreadsheets/{spreadsheet_token}"
        f"/dataValidation/{sheet_id}"
    )

    # highlightValidData=true 时 colors 必须与选项一一对应
    colors = [_COLOR_PALETTE[i % len(_COLOR_PALETTE)] for i in range(len(options))]
    body = {
        "ranges": [data_range],
        "dataValidationType": "list",
        "dataValidation": {
            "conditionValues": options,
            "options": {
                "multipleValues": True,
                "highlightValidData": True,
                "colors": colors,
            },
        },
    }
    resp = httpx.put(
        url,
        headers={**headers, "Content-Type": "application/json"},
        json=body,
        timeout=30,
    )
    data = resp.json()
    if data.get("code") != 0:
        raise RuntimeError(f"设置下拉失败: {json.dumps(data, ensure_ascii=False)[:500]}")
    return {"range": data_range, "options": len(options), "dry_run": False}


def main() -> int:
    parser = argparse.ArgumentParser(description="给飞书「自动化账户」表地域列设置下拉多选")
    parser.add_argument("--spreadsheet-token", default=DEFAULT_SPREADSHEET_TOKEN)
    parser.add_argument("--sheet-id", default=DEFAULT_SHEET_ID)
    parser.add_argument("--range", default=DEFAULT_RANGE)
    parser.add_argument("--dry-run", action="store_true", help="只打印选项，不调用飞书 API")
    args = parser.parse_args()

    options = build_region_options()
    print(f"下拉选项共 {len(options)} 个:")
    for i, name in enumerate(options, start=1):
        print(f"  {i:2d}. {name}")

    col_letter = _find_geo_column_letter(
        args.spreadsheet_token, args.sheet_id, args.range
    )
    print(f"地域列: {col_letter}")

    row_count = _get_sheet_row_count(args.spreadsheet_token, args.sheet_id)
    print(f"表格行数: {row_count}")

    result = _set_dropdown(
        args.spreadsheet_token,
        args.sheet_id,
        col_letter,
        options,
        row_count=row_count,
        dry_run=args.dry_run,
    )
    if result.get("dry_run"):
        print("(dry-run) 未调用飞书 API。正式设置请去掉 --dry-run 重新运行。")
    else:
        print(
            f"✅ 下拉已设置: range={result['range']} 选项数={result['options']}"
        )
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
