#!/usr/bin/env python3
"""
Bot 销转看板 Step2 刷新 — XXTEA 精确匹配版 (v3)
E列11位明文手机号 → XXTEA加密 → bi_vala_app_account.tel_encrypt精确匹配 → H列UID

S2 规则:
  ① E→H: phone_encrypt.py XXTEA 精确匹配, 查不到留空
  ② H→D/I/J: 只补空, 不覆盖已有值
  ③ Y=1: 仅当 K(下单日) >= C(线索日期)
  ④ 全额退清: 所有订单都退费 → N/O/P 全部清空
  ⑤ N/O/P 0留空, O整元
  ⑥ G列不动, 订单汇总不动
覆盖列: D/H/I/J + K-U + X/Y/Z
"""
import json, re, time, sys, os, requests, psycopg2
from datetime import datetime
from collections import defaultdict

SCRIPTS_DIR = os.path.dirname(os.path.abspath(__file__))
WORKSPACE = os.path.dirname(SCRIPTS_DIR)
CRED_DIR = "/root/.openclaw/credentials/xiaoxi"

# XXTEA 加密
sys.path.insert(0, SCRIPTS_DIR)
from phone_encrypt import encrypt_phone

SPREADSHEET_TOKEN = "NoZqsFi47hIOHEt9j8WcfRtbnug"

SALES_SHEETS = [
    ("qJF4I", "小龙", "A1:Z1200"),
    ("f975f0", "吴迪", "A1:Z700"),
    ("qJF4J", "成都", "A1:Z2500"),
]

CS_MAP = {"吴迪": "吴迪", "小龙": "小龙", "Tom": "Tom", "Bob": "Bob"}

GOODS_NAMES = {
    57: "瓦拉英语level1·单季", 60: "瓦拉英语level1", 63: "瓦拉英语level1·单季",
    31: "瓦拉英语年包", 32: "瓦拉英语单季度包", 33: "瓦拉英语level2", 54: "瓦拉英语季度包",
    61: "瓦拉英语level1+2",
}

CHANNEL_MAP = {
    "Apple App Store": "苹果", "科大讯飞学习机": "讯飞", "学而思学习机": "学而思",
    "华为应用市场": "华为", "小米应用市场": "小米", "应用宝应用市场": "应用宝",
    "希沃学习机": "希沃", "荣耀应用市场": "荣耀", "小度学习机": "小度",
    "oppo应用市场": "OPPO", "vivo应用市场": "VIVO", "京东方学习机": "京东方",
    "步步高学习机": "步步高", "作业帮学习机": "作业帮", "魅族应用市场": "魅族",
    "官网": "官网",
}

# Z列渠道归属分类规则 [王虹茗确认 2026-06-15]
def classify_channel(key_from):
    """将 key_from 归类为: 端内 / 销转 / 达人 / 直购"""
    if not key_from:
        return "直购"
    kf = key_from.strip()
    if kf in ("app-active-h5-0-0", "app-sales-bj-qhm-0", "app-sales-bj-wd-0"):
        return "端内"
    if kf.startswith("sales-adp-"):
        return "销转"
    if kf.startswith("newmedia-daren-") or kf == "newmedia-dianpu-wwxx-0-0":
        return "达人"
    # 其余: dianpu(不含wwxx) + partner/stream/miniprogram/jingxuan/空/shuadan等 → 直购
    return "直购"


def parse_clue_date(date_str):
    """解析进线日期: '6月14日 19:09:12' 或 '6月14日' → datetime.date"""
    if not date_str:
        return None
    date_str = date_str.strip()
    # 格式: "6月14日 19:09:12" 或 "6月14日"
    m = re.match(r'(\d+)月(\d+)日', date_str)
    if not m:
        return None
    month, day = int(m.group(1)), int(m.group(2))
    return datetime(2026, month, day).date()

LOG_FILE = "/var/log/xiaoxi_step2_refresh.log"

def log(msg):
    ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    line = f"[{ts}] {msg}"
    print(line)
    with open(LOG_FILE, "a") as f:
        f.write(line + "\n")

def get_secret(key):
    with open(os.path.join(WORKSPACE, "secrets.env")) as f:
        for line in f:
            if line.startswith(f"{key}="):
                return line.strip().split("=", 1)[1].strip("'\"")

def get_fs_token():
    with open(os.path.join(CRED_DIR, "config.json")) as f:
        cfg = json.load(f)
    resp = requests.post(
        "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
        json={"app_id": cfg["apps"][0]["appId"], "app_secret": cfg["apps"][0]["appSecret"]},
        timeout=15
    )
    return resp.json()["tenant_access_token"]

def read_sheet(token, sheet_id, range_str=None):
    url = f"https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{SPREADSHEET_TOKEN}/values/{sheet_id}"
    if range_str:
        url += f"!{range_str}"
    resp = requests.get(url, headers={"Authorization": f"Bearer {token}"}, timeout=30)
    data = resp.json()
    if data.get("code") != 0:
        raise RuntimeError(f"读取失败 {sheet_id}: {data}")
    return data["data"]["valueRange"]["values"]

def put_values(token, sheet_id, range_str, values):
    url = f"https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{SPREADSHEET_TOKEN}/values"
    body = {"valueRange": {"range": f"{sheet_id}!{range_str}", "values": values}}
    resp = requests.put(url, headers={
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json"
    }, json=body, timeout=30)
    r = resp.json()
    if r.get("code") != 0:
        log(f"  ❌ {range_str}: {r.get('code')} {r.get('msg')}")
        return False
    return True

def batch_in(cur, sql_tpl, params, chunk=500):
    results = []
    for i in range(0, len(params), chunk):
        batch = params[i:i+chunk]
        ph = ",".join(["%s"] * len(batch))
        cur.execute(sql_tpl % ph, batch)
        results.extend(cur.fetchall())
    return results

# ── Step 1: 解析销售三表 ──
def parse_sales_sheets(token):
    """返回 {sheet_id: [(row_idx, sales_name, nickname, date_str, phone, existing_uid, g_val, existing_d, existing_i, existing_j), ...]}"""
    all_data = {}
    for sid, sname, rng in SALES_SHEETS:
        rows = read_sheet(token, sid, rng)
        entries = []
        for idx, row in enumerate(rows[2:], start=3):
            if not row:
                continue
            sr = str(row[0]).strip() if len(row) > 0 and row[0] else ""
            sales = None
            for k, v in CS_MAP.items():
                if k in sr:
                    sales = v
                    break
            if not sales:
                continue
            nickname = str(row[1]).strip() if len(row) > 1 and row[1] else ""
            date_str = str(row[2]).strip() if len(row) > 2 and row[2] else ""
            phone = ""
            if len(row) > 4 and row[4]:
                try:
                    phone = str(int(float(row[4])))
                except:
                    pass
            uid = ""
            if len(row) > 7 and row[7]:
                try:
                    uid = str(int(float(row[7])))
                except:
                    pass
            g_val = str(row[6]).strip() if len(row) > 6 and row[6] else ""
            # 读取已有 D/I/J 值 (用于只补空判断)
            d_val = str(row[3]).strip() if len(row) > 3 and row[3] else ""
            i_val = str(row[8]).strip() if len(row) > 8 and row[8] else ""
            j_val = str(row[9]).strip() if len(row) > 9 and row[9] else ""
            entries.append((idx, sales, nickname, date_str, phone, uid, g_val, d_val, i_val, j_val))
        all_data[sid] = entries
        log(f"  {sname}: {len(entries)} rows, {sum(1 for e in entries if e[5] and e[5].isdigit() and int(e[5])>0)} with uid")
    return all_data

# ── Step 2: XXTEA 加密 → PG tel_encrypt 精确匹配 ──
def phone_to_uid_xxtea(all_entries):
    """E列11位明文手机号 → XXTEA加密 → bi_vala_app_account.tel_encrypt精确匹配 → UID"""
    # 收集所有 11 位手机号
    phone_rows = []
    for sid, entries in all_entries.items():
        for idx, sales, nick, date_str, phone, uid, g_val, d_val, i_val, j_val in entries:
            if re.match(r'^\d{11}$', phone):
                phone_rows.append((sid, idx, phone))

    if not phone_rows:
        return {}

    log(f"  XXTEA 加密匹配: {len(phone_rows)} 个手机号")

    # 加密所有手机号
    phone_enc_map = {}  # {encrypted: phone}
    for _, _, phone in phone_rows:
        try:
            enc = encrypt_phone(phone)
            phone_enc_map[enc] = phone
        except Exception as e:
            log(f"  加密失败 {phone}: {e}")

    log(f"  加密完成, 唯一密文: {len(phone_enc_map)}")

    # PG 精确查询
    conn = psycopg2.connect(
        host="bj-postgres-16pob4sg.sql.tencentcdb.com", port=28591,
        user="ai_member", password=get_secret("PG_ONLINE_PASSWORD"),
        dbname="vala_bi", connect_timeout=30
    )
    cur = conn.cursor()

    enc_list = list(phone_enc_map.keys())
    phone_to_uid = {}
    for i in range(0, len(enc_list), 500):
        chunk = enc_list[i:i+500]
        ph = ",".join(["%s"] * len(chunk))
        cur.execute(
            f"SELECT id, tel_encrypt FROM bi_vala_app_account WHERE tel_encrypt IN ({ph}) AND status=1 AND deleted_at IS NULL",
            chunk
        )
        for uid, tel_enc in cur.fetchall():
            plain = phone_enc_map.get(tel_enc)
            if plain:
                phone_to_uid[plain] = str(uid)
        time.sleep(0.05)

    cur.close()
    conn.close()
    log(f"  精确匹配到 {len(phone_to_uid)} 个 UID (via XXTEA)")
    return phone_to_uid

# ── Step 3: PostgreSQL 批量查询 ──
def query_all_pg(all_entries, phone_map):
    """查询所有需要的数据"""
    uid_set = set()
    for sid, entries in all_entries.items():
        for idx, sales, nick, date_str, phone, uid, g_val, d_val, i_val, j_val in entries:
            if re.match(r'^\d{11}$', phone) and phone in phone_map:
                uid_set.add(int(phone_map[phone]))
            if uid and uid.isdigit() and int(uid) > 0:
                uid_set.add(int(uid))

    uid_list = list(uid_set)
    log(f"  有效 user_id: {len(uid_list)}")

    conn = psycopg2.connect(
        host="bj-postgres-16pob4sg.sql.tencentcdb.com", port=28591,
        user="ai_member", password=get_secret("PG_ONLINE_PASSWORD"),
        dbname="vala_bi", connect_timeout=30
    )
    cur = conn.cursor()

    info = {uid: {
        "reg_date": "", "download_channel": "", "trial_count": 0,
        "has_order": False, "order_date": "", "order_channel": "", "product": "",
        "gmv": 0, "refund": 0, "gsv": 0,
        "activation": "", "lesson_progress": "", "lesson_time": "", "lesson_minutes": 0,
        "max_lesson": 0, "trade_no": "",
    } for uid in uid_set}

    # 3a. 注册信息
    log("  查询注册信息...")
    reg_info = batch_in(cur,
        "SELECT id, created_at, download_channel FROM bi_vala_app_account WHERE id IN (%s) AND status=1 AND deleted_at IS NULL",
        uid_list
    )
    for aid, created_at, dc in reg_info:
        if aid in info:
            info[aid]["reg_date"] = created_at.strftime("%Y-%m-%d") if created_at else ""
            raw_ch = dc or ""
            info[aid]["download_channel"] = CHANNEL_MAP.get(raw_ch, raw_ch)

    # 3b. 体验节数
    log("  查询体验节数...")
    trial_info = batch_in(cur,
        "SELECT account_id, COUNT(*) FROM bi_user_course_detail WHERE account_id IN (%s) AND expire_time IS NULL AND deleted_at IS NULL GROUP BY account_id",
        uid_list
    )
    for aid, cnt in trial_info:
        if aid in info:
            info[aid]["trial_count"] = cnt

    # 3c. 订单信息
    log("  查询订单信息...")
    orders = batch_in(cur,
        "SELECT account_id, trade_no, pay_success_date, key_from, goods_id, pay_amount_int, order_status FROM bi_vala_order WHERE account_id IN (%s) AND pay_success_date IS NOT NULL AND order_status IN (3,4) ORDER BY pay_success_date DESC",
        uid_list
    )
    user_orders = defaultdict(list)
    for o in orders:
        user_orders[o[0]].append(o)

    trade_nos = [o[1] for o in orders if o[1]]
    refund_map = {}
    if trade_nos:
        refunds = batch_in(cur,
            "SELECT trade_no, refund_amount_int FROM bi_refund_order WHERE trade_no IN (%s) AND status=3",
            trade_nos
        )
        for tn, amt in refunds:
            refund_map[tn] = amt

    for aid, olist in user_orders.items():
        if aid not in info:
            continue
        info[aid]["has_order"] = True
        latest = olist[0]
        info[aid]["order_date"] = f"{latest[2].month}月{latest[2].day}日 {latest[2].strftime('%H:%M:%S')}" if latest[2] else ""
        info[aid]["pay_success_dt"] = latest[2]  # raw datetime for date comparison
        info[aid]["order_channel"] = latest[3] or ""
        info[aid]["product"] = GOODS_NAMES.get(latest[4], f"商品{latest[4]}")
        info[aid]["trade_no"] = latest[1] or ""
        # 所有订单号，换行分隔
        all_trade_nos = [o[1] for o in olist if o[1]]
        info[aid]["all_trade_nos"] = "\n".join(all_trade_nos)
        total_gmv = sum(o[5] for o in olist) / 100.0
        total_refund = sum(refund_map.get(o[1], 0) for o in olist) / 100.0
        info[aid]["gmv"] = total_gmv
        info[aid]["refund"] = total_refund
        info[aid]["gsv"] = total_gmv - total_refund

    # 3d. 激活课程
    log("  查询激活课程...")
    try:
        activations = batch_in(cur,
            "SELECT t.account_id, t.season_package_level FROM bi_vala_seasonal_ticket t WHERE t.account_id IN (%s) AND t.status=1 AND t.deleted_at IS NULL AND t.season_package_level IN ('A1','A2')",
            uid_list
        )
        for aid, lvl in activations:
            if aid in info:
                info[aid]["activation"] = lvl
    except Exception as e:
        log(f"  激活查询异常: {e}")

    # 3e. 角色信息
    log("  查询角色信息...")
    char_info = batch_in(cur,
        "SELECT account_id, id FROM bi_vala_app_character WHERE account_id IN (%s) AND deleted_at IS NULL",
        uid_list
    )
    account_chars = defaultdict(list)
    char_to_account = {}
    for aid, cid in char_info:
        account_chars[aid].append(cid)
        char_to_account[cid] = aid
    char_ids = list(char_to_account.keys())
    log(f"  角色数: {len(char_ids)}")

    # 3f. 课程映射
    cur.execute("SELECT id, course_level, course_season, course_unit, course_lesson FROM bi_level_unit_lesson")
    chapter_map = {}
    for ch_id, cl, cs, cu, cl2 in cur.fetchall():
        chapter_map[ch_id] = (cl or "", cs or "", cu or "", cl2 or "")

    # 3g. 课时完成记录
    log("  查询课时完成记录...")
    char_plays = defaultdict(lambda: {"latest_time": None, "latest_chapter": None, "max_lesson_idx": 0, "total_ms": 0})
    for tbl_idx in range(8):
        table = f"bi_user_chapter_play_record_{tbl_idx}"
        try:
            cur.execute(
                f"SELECT user_id, chapter_id, created_at FROM {table} WHERE play_status=1 AND deleted_at IS NULL AND user_id = ANY(%s)",
                (char_ids,)
            )
            for uid, ch_id, created_at in cur.fetchall():
                ch_data = chapter_map.get(ch_id)
                if not ch_data:
                    continue
                rec = char_plays[uid]
                if rec["latest_time"] is None or created_at > rec["latest_time"]:
                    rec["latest_time"] = created_at
                    rec["latest_chapter"] = (ch_id, ch_data)
                cl, cs, cu, cl2 = ch_data
                try:
                    u_num = int(cu[1:]) if cu and len(cu) >= 2 else 0
                    l_num = int(cl2[1:]) if cl2 and len(cl2) >= 2 else 0
                    lesson_idx = u_num * 5 + l_num
                    if lesson_idx > rec["max_lesson_idx"]:
                        rec["max_lesson_idx"] = lesson_idx
                except:
                    pass
        except Exception as e:
            log(f"  警告 {table}: {e}")

    # 3h. 学习总耗时
    log("  查询学习耗时...")
    for tbl_idx in range(8):
        table = f"bi_user_component_play_record_{tbl_idx}"
        try:
            cur.execute(
                f"SELECT user_id, SUM(COALESCE(interval_time,0)) FROM {table} WHERE user_id = ANY(%s) AND deleted_at IS NULL GROUP BY user_id",
                (char_ids,)
            )
            for uid, total_ms in cur.fetchall():
                if uid in char_plays:
                    char_plays[uid]["total_ms"] += (total_ms or 0)
        except Exception as e:
            log(f"  警告 {table}: {e}")

    cur.close()
    conn.close()

    # 汇总到 account 级别
    for aid in uid_set:
        chars = account_chars.get(aid, [])
        best_time = None
        best_ch = None
        max_lesson = 0
        total_ms = 0
        for cid in chars:
            play = char_plays.get(cid)
            if not play:
                continue
            if play["latest_chapter"]:
                if best_time is None or play["latest_time"] > best_time:
                    best_time = play["latest_time"]
                    best_ch = play["latest_chapter"]
            if play["max_lesson_idx"] > max_lesson:
                max_lesson = play["max_lesson_idx"]
            total_ms += play["total_ms"]

        info[aid]["max_lesson"] = max_lesson
        info[aid]["lesson_minutes"] = round(total_ms / 60000, 1)
        if info[aid]["lesson_minutes"] == int(info[aid]["lesson_minutes"]):
            info[aid]["lesson_minutes"] = int(info[aid]["lesson_minutes"])

        if best_ch:
            ch_id, (cl, cs, cu, cl2) = best_ch
            info[aid]["lesson_progress"] = f"{cl}-{cs}-{cu}-{cl2}"
            info[aid]["lesson_time"] = best_time.strftime("%Y-%m-%d") if best_time else ""

    log(f"  数据库查询完成")
    return info

# ── Step 4: 写入销售三表 ──
def write_sales_sheets(token, all_entries, phone_map, db_info):
    """全覆盖写入销售表的自动列: D/H/I/J + K-U + X/Y/Z"""
    now_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    for sid, sname, _ in SALES_SHEETS:
        entries = all_entries[sid]
        log(f"  写入 {sname} ({sid})...")

        groups = []
        cur_grp = []
        for idx, sales, nick, date_str, phone, uid, g_val, d_val, i_val, j_val in entries:
            item = {"row": idx, "phone": phone, "uid": uid, "g_val": g_val,
                    "date": date_str, "d_val": d_val, "i_val": i_val, "j_val": j_val}
            if not cur_grp or idx == cur_grp[-1]["row"] + 1:
                cur_grp.append(item)
            else:
                groups.append(cur_grp)
                cur_grp = [item]
        if cur_grp:
            groups.append(cur_grp)

        for g in groups:
            sr, er = g[0]["row"], g[-1]["row"]

            d_vals, h_vals, i_vals, j_vals = [], [], [], []
            k_vals, l_vals, m_vals, n_vals = [], [], [], []
            o_vals, p_vals, q_vals, r_vals = [], [], [], []
            s_vals, t_vals, u_vals = [], [], []
            x_vals, y_vals, z_vals = [], [], []

            for item in g:
                phone = item["phone"]
                existing_uid = item["uid"]
                existing_d = item.get("d_val", "")
                existing_i = item.get("i_val", "")
                existing_j = item.get("j_val", "")
                clue_date = item.get("date", "")

                # 确定 UID: XXTEA 精确匹配优先
                aid = 0
                uid_str = ""
                if re.match(r'^\d{11}$', phone) and phone in phone_map:
                    uid_str = phone_map[phone]
                    aid = int(uid_str)
                elif existing_uid and existing_uid.isdigit() and int(existing_uid) > 0:
                    uid_str = existing_uid
                    aid = int(existing_uid)

                # H: UID — XXTEA匹配到就写，否则留空
                if re.match(r'^\d{11}$', phone) and phone in phone_map:
                    h_vals.append([phone_map[phone]])
                elif re.match(r'^\d{11}$', phone):
                    h_vals.append([""])
                elif existing_uid and existing_uid.isdigit():
                    h_vals.append([existing_uid])
                else:
                    h_vals.append([""])

                if aid > 0 and aid in db_info:
                    di = db_info[aid]

                    # D: 体验节数 — 只补空
                    if existing_d:
                        d_vals.append([existing_d])
                    else:
                        tc = di["trial_count"]
                        d_vals.append([tc if tc > 0 else ""])

                    # I: 注册日 — 只补空
                    if existing_i:
                        i_vals.append([existing_i])
                    else:
                        i_vals.append([di["reg_date"]])

                    # J: 下载渠道 — 只补空
                    if existing_j:
                        j_vals.append([existing_j])
                    else:
                        j_vals.append([di["download_channel"]])

                    # 判断全额退清: gmv == refund 且 gmv > 0
                    gmv_int = int(di["gmv"])
                    refund_int = int(di["refund"])
                    gsv_int = int(di["gsv"])
                    is_full_refund = (gmv_int > 0 and gmv_int == refund_int)

                    # 有效订单判定: GSV>0 且 支付时间≥进线日期 且非全额退清
                    order_date = di["order_date"]
                    should_y_yes = False
                    if di["has_order"] and not is_full_refund and gsv_int > 0:
                        pay_dt = di.get("pay_success_dt")
                        if pay_dt and clue_date:
                            try:
                                clue_dt = parse_clue_date(clue_date)
                                if clue_dt and pay_dt.date() >= clue_dt.date():
                                    should_y_yes = True
                            except:
                                should_y_yes = True
                        else:
                            should_y_yes = True

                    if is_full_refund:
                        # 全额退清 → N/O/P 全部清空
                        n_vals.append([""])
                        o_vals.append([""])
                        p_vals.append([""])
                    else:
                        n_vals.append([gmv_int if gmv_int > 0 else ""])
                        o_vals.append([refund_int if refund_int > 0 else ""])
                        p_vals.append([gsv_int if gsv_int > 0 else ""])

                    # K: 下单日期, L: 成交渠道, M: 产品
                    k_vals.append([order_date])
                    l_vals.append([di["order_channel"]])
                    m_vals.append([di["product"] if di["has_order"] else ""])

                    # Q: 激活课程
                    act = di["activation"]
                    if act:
                        q_vals.append([f"{act}体验课" if act in ("A1", "A2") else act])
                    else:
                        q_vals.append([""])

                    # R: 行课进度, S: 最近行课时间
                    lp = di["lesson_progress"]
                    r_vals.append([lp if lp else ""])
                    s_vals.append([di["lesson_time"]])

                    # T: 学习时长
                    lm = di["lesson_minutes"]
                    t_vals.append([lm if lm > 0 else ""])

                    # X: 订单号 — 所有订单号换行分隔
                    x_vals.append([di.get("all_trade_nos", di.get("trade_no", ""))])
                    # Y: 有效订单
                    y_vals.append([1 if should_y_yes else ""])
                    # Z: 渠道归属 — 有订单才归类，否则留空
                    if di["has_order"]:
                        z_vals.append([classify_channel(di["order_channel"])])
                    else:
                        z_vals.append([""])
                else:
                    for arr in [d_vals, i_vals, j_vals, k_vals, l_vals, m_vals, n_vals,
                                o_vals, p_vals, q_vals, r_vals, s_vals, t_vals,
                                x_vals, y_vals, z_vals]:
                        arr.append([""])

                # U: 更新时间
                u_vals.append([now_str])

            cols = [
                ("D", d_vals), ("H", h_vals), ("I", i_vals), ("J", j_vals),
                ("K", k_vals), ("L", l_vals), ("M", m_vals), ("N", n_vals),
                ("O", o_vals), ("P", p_vals), ("Q", q_vals), ("R", r_vals),
                ("S", s_vals), ("T", t_vals), ("U", u_vals),
                ("X", x_vals), ("Y", y_vals), ("Z", z_vals),
            ]
            for col_letter, vals in cols:
                put_values(token, sid, f"{col_letter}{sr}:{col_letter}{er}", vals)
                time.sleep(0.1)

        log(f"    {sname}: {len(entries)} rows done")

# ── Main ──
def main():
    log("=" * 50)
    log("Bot 销转看板 Step2 刷新 (XXTEA精确匹配版) 启动")

    try:
        token = get_fs_token()

        log("Step 1: 解析销售三表")
        all_entries = parse_sales_sheets(token)

        log("Step 2: XXTEA 加密 → PG tel_encrypt 精确匹配")
        phone_map = phone_to_uid_xxtea(all_entries)

        log("Step 3: PostgreSQL 批量查询")
        db_info = query_all_pg(all_entries, phone_map)

        log("Step 4: 写入销售三表")
        write_sales_sheets(token, all_entries, phone_map, db_info)

        log("✅ Step2 刷新完成 (XXTEA)")
        return 0
    except Exception as e:
        log(f"❌ ERROR: {e}")
        import traceback
        traceback.print_exc()
        return 1

if __name__ == "__main__":
    sys.exit(main())
