#!/usr/bin/env python3
"""吴迪销转表行课更新 — 手机号补ID + 行课回填"""
import json, requests, os, sys, psycopg2
from datetime import datetime
from collections import defaultdict

SCRIPTS_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, SCRIPTS_DIR)
from phone_encrypt import encrypt_phone

SPREADSHEET_TOKEN = "NoZqsFi47hIOHEt9j8WcfRtbnug"
SHEET_ID = "f975f0"
CRED_DIR = "/root/.openclaw/credentials/xiaoxi"

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):
    url = f"https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{SPREADSHEET_TOKEN}/values/{sheet_id}"
    resp = requests.get(url, headers={"Authorization": f"Bearer {token}"}, timeout=30)
    data = resp.json()
    if data.get("code") != 0:
        raise RuntimeError(f"读取失败: {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)
    return resp.json()

def get_pg():
    secrets_path = os.path.join(SCRIPTS_DIR, "..", "secrets.env")
    with open(secrets_path) as f:
        pg_pass = None
        for line in f:
            if line.startswith("PG_ONLINE_PASSWORD="):
                pg_pass = line.strip().split("=", 1)[1].strip("'\"")
    return psycopg2.connect(
        host="bj-postgres-16pob4sg.sql.tencentcdb.com",
        port=28591, user="ai_member", password=pg_pass, dbname="vala_bi"
    )

def clean_phone(val):
    if not val: return None
    val = str(val).strip().replace(" ", "").replace("-", "").replace("'", "")
    if "e" in val.lower():
        try: val = str(int(float(val)))
        except: return None
    digits = "".join(c for c in val if c.isdigit())
    return digits if len(digits) == 11 and digits.startswith("1") else None

def main():
    token = get_fs_token()
    rows = read_sheet(token, SHEET_ID)
    print(f"总行数: {len(rows)}")
    
    # 分析缺口
    need_phone_to_id = []  # E有手机 H空
    need_course = []       # H有UID D空
    for idx, row in enumerate(rows[2:], start=3):  # 第3行起
        e_val = str(row[4]).strip() if len(row) > 4 and row[4] else ""
        h_val = str(row[7]).strip() if len(row) > 7 and row[7] else ""
        d_val = str(row[3]).strip() if len(row) > 3 and row[3] else ""
        
        phone = clean_phone(e_val)
        uid = h_val if h_val and h_val != "None" and h_val != "" else None
        
        if phone and not uid:
            need_phone_to_id.append((idx, phone))
        if uid and not d_val:
            need_course.append((idx, uid))
    
    print(f"手机号→ID待补: {len(need_phone_to_id)}")
    print(f"有UID但D空: {len(need_course)}")
    
    conn = get_pg()
    cur = conn.cursor()
    
    # ── Step 1: 手机号→ID ──
    if need_phone_to_id:
        # 批量加密
        enc_map = {}
        for idx, phone in need_phone_to_id:
            enc = encrypt_phone(phone)
            enc_map[enc] = phone
        
        enc_list = list(enc_map.keys())
        enc_to_aid = {}
        for i in range(0, len(enc_list), 500):
            batch = enc_list[i:i+500]
            ph = ",".join(["%s"] * len(batch))
            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",
                batch
            )
            for aid, tel_enc in cur.fetchall():
                enc_to_aid[tel_enc] = aid
        
        # 回填H列
        updates = []
        for idx, phone in need_phone_to_id:
            enc = encrypt_phone(phone)
            aid = enc_to_aid.get(enc)
            updates.append((idx, str(aid) if aid else ""))
        
        # 分批写入
        for i in range(0, len(updates), 50):
            batch = updates[i:i+50]
            vals = [[u[1]] for u in batch]
            sr, er = batch[0][0], batch[-1][0]
            put_values(token, SHEET_ID, f"H{sr}:H{er}", vals)
        
        matched = sum(1 for u in updates if u[1])
        print(f"H列回填完成: {len(updates)}行, 匹配{matched}")
    
    # ── Step 2: 行课回填 ──
    if need_course:
        uids = list(set(uid for _, uid in need_course))
        
        # 查注册日期和下载渠道
        cur.execute(f"""
            SELECT id, created_at, download_channel FROM bi_vala_app_account
            WHERE id::text IN ({','.join(['%s']*len(uids))}) AND status=1 AND deleted_at IS NULL
        """, uids)
        acc_info = {str(row[0]): (row[1], row[2]) for row in cur.fetchall()}
        
        # 查角色
        cur.execute(f"""
            SELECT account_id, id FROM bi_vala_app_character
            WHERE account_id::text IN ({','.join(['%s']*len(uids))})
        """, uids)
        aid_to_chars = defaultdict(list)
        for aid, cid in cur.fetchall():
            aid_to_chars[str(aid)].append(cid)
        
        # 查体验课节数 (expire_time IS NULL)
        cur.execute(f"""
            SELECT account_id, course_level, COUNT(*) as cnt
            FROM bi_user_course_detail
            WHERE account_id::text IN ({','.join(['%s']*len(uids))})
              AND expire_time IS NULL AND deleted_at IS NULL
            GROUP BY account_id, course_level
        """, uids)
        trial_info = defaultdict(dict)
        for aid, level, cnt in cur.fetchall():
            trial_info[str(aid)][level] = cnt
        
        # 查完课记录
        char_ids = []
        for cids in aid_to_chars.values():
            char_ids.extend(cids)
        
        char_completed = defaultdict(set)
        if char_ids:
            for tbl_idx in range(8):
                cur.execute(f"""
                    SELECT user_id, chapter_id FROM bi_user_chapter_play_record_{tbl_idx}
                    WHERE user_id IN ({','.join(['%s']*len(char_ids))})
                      AND play_status = 1
                """, char_ids)
                for uid, ch_id in cur.fetchall():
                    char_completed[uid].add(ch_id)
        
        # 组装回填数据
        updates_d = []
        updates_i = []
        updates_j = []
        for idx, uid in need_course:
            info = acc_info.get(uid)
            reg_date = info[0].strftime("%Y-%m-%d") if info and info[0] else ""
            dl_channel = info[1] if info and info[1] else ""
            
            # 体验节数
            trials = trial_info.get(uid, {})
            trial_parts = []
            for level in sorted(trials.keys()):
                trial_parts.append(f"{level}体验{trials[level]}节")
            trial_str = ", ".join(trial_parts) if trial_parts else "无体验课"
            
            # 完课数
            chars = aid_to_chars.get(uid, [])
            completed = set()
            for cid in chars:
                completed |= char_completed.get(cid, set())
            
            d_val = f"{trial_str} | 已完课{len(completed)}节" if completed else trial_str
            
            updates_d.append((idx, d_val))
            updates_i.append((idx, reg_date))
            updates_j.append((idx, dl_channel))
        
        # 分批写入
        for col_letter, updates in [("D", updates_d), ("I", updates_i), ("J", updates_j)]:
            for i in range(0, len(updates), 50):
                batch = updates[i:i+50]
                vals = [[u[1]] for u in batch]
                sr, er = batch[0][0], batch[-1][0]
                put_values(token, SHEET_ID, f"{col_letter}{sr}:{col_letter}{er}", vals)
        
        print(f"D/I/J列回填完成: {len(updates_d)}行")
    
    cur.close()
    conn.close()
    print("完成")

if __name__ == "__main__":
    main()
