#!/usr/bin/env python3
"""
端内购买用户行为分析 V3 - 详细交叉统计
"""
import psycopg2
from datetime import datetime, date
from collections import defaultdict, Counter
import csv
import os

PG_PASS = os.environ.get('PG_ONLINE_PASSWORD', "LdfjdjL83h3h3^$&**YGG*")
conn = psycopg2.connect(
    host="bj-postgres-16pob4sg.sql.tencentcdb.com", port=28591,
    user="ai_member", password=PG_PASS, database="vala_bi"
)
cur = conn.cursor()

L1_U0_LESSONS = {343: 'L01', 344: 'L02', 345: 'L03', 346: 'L04', 348: 'L05'}
L2_U0_LESSONS = {55: 'L01', 56: 'L02', 57: 'L03', 58: 'L04', 59: 'L05'}
LEVEL_MAP = {57:'L1',60:'L1',63:'L1', 31:'L2',32:'L2',33:'L2',54:'L2'}

print("=" * 80)
print("端内购买用户行为分析 V3 — 详细交叉统计")
print("=" * 80)

# ============ 查询数据 ============
print("\n[数据加载]")

cur.execute("""
    SELECT o.id, o.account_id, o.goods_id, o.pay_success_date
    FROM bi_vala_order o
    JOIN bi_vala_app_account a ON o.account_id = a.id AND a.status = 1
    WHERE o.key_from = 'app-active-h5-0-0' AND o.order_status IN (3, 4) AND o.pay_success_date IS NOT NULL
    ORDER BY o.pay_success_date
""")
orders = []
for row in cur.fetchall():
    lv = LEVEL_MAP.get(row[2])
    if not lv: continue
    pd_ = row[3]; orders.append({'order_id':row[0],'account_id':row[1],'goods_id':row[2],'purchase_date':pd_.date() if isinstance(pd_,datetime) else pd_,'level':lv})
print(f"  订单: {len(orders)}")

all_accounts = list(set(o['account_id'] for o in orders))
register_map = {}
for i in range(0,len(all_accounts),500):
    cur.execute("SELECT id,created_at FROM bi_vala_app_account WHERE id IN %s",(tuple(all_accounts[i:i+500]),))
    for r in cur.fetchall():
        d=r[1];register_map[r[0]]=d.date() if isinstance(d,(datetime,date)) else d
print(f"  注册时间: {len(register_map)}")

char_map = defaultdict(list)
for i in range(0,len(all_accounts),500):
    cur.execute("SELECT account_id,id FROM bi_vala_app_character WHERE account_id IN %s",(tuple(all_accounts[i:i+500]),))
    for r in cur.fetchall(): char_map[r[0]].append(r[1])
print(f"  角色映射: {sum(1 for k,v in char_map.items() if v)} 用户")

all_user_ids = set()
for acct in all_accounts:
    for uid in char_map.get(acct,[]): all_user_ids.add(uid)

def query_lesson_completion(user_ids, chapter_ids_dict):
    result = defaultdict(dict)
    if not user_ids: return result
    cids = list(chapter_ids_dict.keys())
    union = []
    for t in range(8):
        union.append(f"""
            SELECT user_id, chapter_id, MIN(created_at) as fd
            FROM bi_user_chapter_play_record_{t}
            WHERE chapter_id IN ({','.join(map(str,cids))})
              AND play_status=1 AND user_id IN %(uids)s
            GROUP BY user_id, chapter_id
        """)
    sql = f"SELECT user_id, chapter_id, MIN(fd) FROM ({' UNION ALL '.join(union)}) sub GROUP BY user_id, chapter_id"
    for i in range(0,len(user_ids),2000):
        cur.execute(sql,{'uids':tuple(user_ids[i:i+2000])})
        for r in cur.fetchall(): result[r[0]][r[1]]=r[2].date() if isinstance(r[2],(datetime,date)) else r[2]
    return result

l1_data = query_lesson_completion(list(all_user_ids), L1_U0_LESSONS)
l2_data = query_lesson_completion(list(all_user_ids), L2_U0_LESSONS)
print(f"  L1序章完成: {len(l1_data)} user_ids, L2序章完成: {len(l2_data)} user_ids")

# ============ 逐订单分析 ============
records = []
for o in orders:
    lesson_map = L1_U0_LESSONS if o['level']=='L1' else L2_U0_LESSONS
    lesson_data = l1_data if o['level']=='L1' else l2_data
    reg_d = register_map.get(o['account_id'])
    if not reg_d: continue
    pur_d = o['purchase_date']
    
    # 汇总所有角色的序章完成情况
    merged = {}
    for uid in char_map.get(o['account_id'],[]):
        if uid not in lesson_data: continue
        for cid, dt in lesson_data[uid].items():
            if cid not in merged or dt < merged[cid]: merged[cid]=dt
    
    # 每节课的完成日期和距注册天数
    lesson_dates = {}
    for cid, dt in merged.items():
        lesson_dates[lesson_map[cid]] = {'date': dt, 'days_from_reg': (dt - reg_d).days}
    
    # 购课前完成哪些
    done_before = {ln: info for ln, info in lesson_dates.items() if info['date'] <= pur_d}
    done_after = {ln: info for ln, info in lesson_dates.items() if info['date'] > pur_d}
    
    all5_done = len(lesson_dates) == 5
    all5_date = max(info['date'] for info in lesson_dates.values()) if all5_done else None
    all5_to_pur = (pur_d - all5_date).days if all5_date else None
    reg_to_all5 = (all5_date - reg_d).days if all5_date else None
    
    # 购课时进度：完成了几节，已完成哪些节
    progress = len(done_before)
    lessons_done = sorted(done_before.keys(), key=lambda x: int(x[1:]))
    
    # 购课落在哪两节之间
    # e.g. 完成了 L01 L02，则购课时间在 L02完成后 → "L02之后"
    if progress == 0:
        purchase_position = "L01之前"
    elif progress == 5:
        purchase_position = "L05之后"
    else:
        purchase_position = f"{lessons_done[-1]}之后(L{int(lessons_done[-1][1:])+1:02d}之前)"
    
    records.append({
        'order_id': o['order_id'], 'account_id': o['account_id'],
        'level': o['level'], 'register_date': reg_d, 'purchase_date': pur_d,
        'reg_to_pur': (pur_d - reg_d).days,
        'progress': progress, 'lessons_done': lessons_done,
        'done_before': done_before, 'done_after': done_after,
        'all5_done': all5_done, 'all5_date': all5_date,
        'reg_to_all5': reg_to_all5, 'all5_to_pur': all5_to_pur,
        'purchase_position': purchase_position,
        'lesson_dates': lesson_dates,
    })

print(f"  分析记录: {len(records)}")
l1_recs = [r for r in records if r['level']=='L1']
l2_recs = [r for r in records if r['level']=='L2']
all5_recs = [r for r in records if r['all5_done']]

# ====================================================================
# 交叉统计1: 购课时完成节数 × 注册→购课天数
# ====================================================================
print(f"\n{'='*80}")
print("【交叉1】购课时完成节数 × 注册→购课天数")
print(f"{'='*80}")

for level, data in [('L1',l1_recs),('L2',l2_recs),('ALL',records)]:
    print(f"\n[{level}]")
    # 构建交叉表
    day_ranges = [(0,1),(2,3),(4,7),(8,14),(15,30),(31,60),(61,999)]
    print(f"  {'购课进度':<20}", end='')
    for lo, hi in day_ranges:
        label = f"{lo}天" if lo==hi else f"{lo}-{hi}天"
        print(f"{label:>10}", end='')
    print(f"  {'合计':>8}")
    
    for prog in range(6):
        subset = [r for r in data if r['progress']==prog]
        label = f"  完成{prog}节" if prog>0 else f"  完成0节"
        print(f"{label:<20}", end='')
        total = 0
        for lo, hi in day_ranges:
            cnt = sum(1 for r in subset if lo <= r['reg_to_pur'] <= hi)
            print(f"{cnt:>10}", end='')
            total += cnt
        print(f"  {total:>8}")

# ====================================================================
# 交叉统计2: 购课时完成节数 × 完课→购课天数（仅五节全完成用户）
# ====================================================================
print(f"\n{'='*80}")
print("【交叉2】购课时完成节数 × 全完成→购课天数（仅五节全完成用户）")
print(f"{'='*80}")

all5_day_ranges = [(-999,-1),(0,0),(1,3),(4,7),(8,14),(15,30),(31,60),(61,999)]

for level, data in [('L1',[r for r in l1_recs if r['all5_done']]),
                     ('L2',[r for r in l2_recs if r['all5_done']]),
                     ('ALL',all5_recs)]:
    print(f"\n[{level}]")
    print(f"  {'购课进度':<20}", end='')
    for lo, hi in all5_day_ranges:
        if lo==-999: label="先购课后完课"
        elif lo==hi: label=f"{lo}天"
        else: label=f"{lo}-{hi}天"
        print(f"{label:>14}", end='')
    print(f"  {'合计':>8}")
    
    for prog in range(6):
        subset = [r for r in data if r['progress']==prog]
        label = f"  完成{prog}节" if prog>0 else f"  完成0节"
        print(f"{label:<20}", end='')
        total = 0
        for lo, hi in all5_day_ranges:
            cnt = sum(1 for r in subset if r['all5_to_pur'] is not None and lo <= r['all5_to_pur'] <= hi)
            print(f"{cnt:>14}", end='')
            total += cnt
        print(f"  {total:>8}")

# ====================================================================
# 交叉统计3: 购课落在序章各节之间的分布
# ====================================================================
print(f"\n{'='*80}")
print("【交叉3】购课时机：落在序章第几节之后")
print(f"{'='*80}")

for level, data in [('L1',l1_recs),('L2',l2_recs),('ALL',records)]:
    dist = Counter(r['purchase_position'] for r in data)
    print(f"\n[{level}]")
    positions = ['L01之前','L01之后(L02之前)','L02之后(L03之前)','L03之后(L04之前)','L04之后(L05之前)','L05之后']
    for pos in positions:
        cnt = dist.get(pos, 0)
        pct = cnt/len(data)*100
        bar = '█' * int(pct)
        print(f"  {pos:24s}: {cnt:4d} ({pct:5.1f}%) {bar}")

# ====================================================================
# 交叉统计4: 逐节课完成时间分布
# ====================================================================
print(f"\n{'='*80}")
print("【交叉4】序章每节课：从注册到完成的天数分布")
print(f"{'='*80}")

lesson_days_ranges = [(0,0),(1,1),(2,3),(4,7),(8,14),(15,30),(31,999)]
lesson_order = ['L01','L02','L03','L04','L05']

for level, data in [('L1',l1_recs),('L2',l2_recs),('ALL',records)]:
    print(f"\n[{level}] ({len(data)} 条记录)")
    print(f"  {'课时':<8}", end='')
    for lo, hi in lesson_days_ranges:
        label = f"{lo}天" if lo==hi else f"{lo}-{hi}天"
        print(f"{label:>10}", end='')
    print(f"  {'完成人数':>10}  {'中位数':>8}  {'均值':>8}")
    
    for ln in lesson_order:
        vals = []
        for r in data:
            if ln in r['lesson_dates']:
                vals.append(r['lesson_dates'][ln]['days_from_reg'])
        if not vals:
            print(f"  {ln:<8} {'(无数据)':>60}")
            continue
        sv = sorted(vals)
        med = sv[len(sv)//2]
        avg = sum(vals)/len(vals)
        print(f"  {ln:<8}", end='')
        for lo, hi in lesson_days_ranges:
            cnt = sum(1 for v in vals if lo <= v <= hi)
            pct = cnt / len(vals) * 100
            print(f"{cnt:>5}({pct:4.1f}%)", end='')
        print(f"  {len(vals):>10}  {med:>8}天  {avg:>8.1f}天")

# ====================================================================
# 交叉统计5: 购课时完成节数 × L1/L2 × 是否有后续完成
# ====================================================================
print(f"\n{'='*80}")
print("【交叉5】购课时未全部完成的用户：购课后完成余下序章的情况")
print(f"{'='*80}")

not_all5_at_purchase = [r for r in records if r['progress'] < 5]
for level, data in [('L1',[r for r in l1_recs if r['progress']<5]),
                     ('L2',[r for r in l2_recs if r['progress']<5]),
                     ('ALL',not_all5_at_purchase)]:
    if not data: continue
    print(f"\n[{level}] 购课时未全部完成: {len(data)} 人")
    for prog in range(5):
        subset = [r for r in data if r['progress']==prog]
        if not subset: continue
        after_done_dist = Counter(r['all5_done'] for r in subset)
        all5_cnt = after_done_dist.get(True, 0)
        not5_cnt = after_done_dist.get(False, 0)
        print(f"  购课时完成{prog}节: {len(subset)}人 → 最终完成为5节: {all5_cnt}人, 仍未完成: {not5_cnt}人")

# ====================================================================
# 交叉统计6: 先购课后完成序章的用户详细分析
# ====================================================================
print(f"\n{'='*80}")
print("【交叉6】先购课后才完成序章的用户（16人）详细分析")
print(f"{'='*80}")

buy_before_finish = [r for r in all5_recs if r['all5_to_pur'] is not None and r['all5_to_pur'] < 0]
print(f"\n共 {len(buy_before_finish)} 人，购课后才完成序章全部五节\n")
print(f"  {'等级':<6} {'购课时进度':<14} {'购课日期':<14} {'全完成日期':<14} {'购课→完课':>10} {'注册→购课':>10} {'注册→全完':>10}")
for r in sorted(buy_before_finish, key=lambda x: x['all5_to_pur']):
    print(f"  {r['level']:<6} {'完成'+str(r['progress'])+'节':<14} {str(r['purchase_date']):<14} {str(r['all5_date']):<14} {abs(r['all5_to_pur']):>10}天 {r['reg_to_pur']:>10}天 {r['reg_to_all5']:>10}天")

# ====================================================================
# 交叉统计7: 逐月趋势 — 购课时完成节数分布
# ====================================================================
print(f"\n{'='*80}")
print("【交叉7】逐月趋势：购课时完成 5/5 节的比例")
print(f"{'='*80}")

monthly = defaultdict(lambda: {'total':0,'all5':0,'partial':0})
for r in records:
    m = r['purchase_date'].strftime('%Y-%m')
    monthly[m]['total'] += 1
    if r['progress'] == 5:
        monthly[m]['all5'] += 1
    else:
        monthly[m]['partial'] += 1

for m in sorted(monthly.keys()):
    d = monthly[m]
    pct = d['all5']/d['total']*100
    print(f"  {m}: 总{d['total']:>4}单 | 购课时已完成5节={d['all5']:>3}人({pct:5.1f}%) | 未完成={d['partial']:>2}人")

# ====================================================================
# 交叉统计8: 注册→购课 按购课时进度分层
# ====================================================================
print(f"\n{'='*80}")
print("【交叉8】按购课时进度分层：各组注册→购课 天数统计")
print(f"{'='*80}")

def pct(vals, p):
    if not vals: return '-'
    s = sorted(vals)
    return s[int(len(s)*p/100)]

for level, data in [('L1',l1_recs),('L2',l2_recs),('ALL',records)]:
    print(f"\n[{level}]")
    print(f"  {'购课时进度':<16} {'人数':>6} {'P10':>6} {'P25':>6} {'中位':>6} {'P75':>6} {'P90':>6} {'均值':>8}")
    for prog in range(6):
        subset = [r for r in data if r['progress']==prog]
        if not subset: continue
        vals = [r['reg_to_pur'] for r in subset]
        print(f"  {'完成'+str(prog)+'节':<16} {len(subset):>6} "
              f"{pct(vals,10):>6} {pct(vals,25):>6} {pct(vals,50):>6} "
              f"{pct(vals,75):>6} {pct(vals,90):>6} {sum(vals)/len(vals):>8.1f}")

# ====================================================================
# 导出CSV（带序章每节课完成明细）
# ====================================================================
output_path = '/root/.openclaw/workspace/output/endor_purchase_analysis_v3.csv'
with open(output_path, 'w', newline='', encoding='utf-8-sig') as f:
    writer = csv.writer(f)
    writer.writerow([
        'order_id','account_id','等级','注册日期','购课日期','注册→购课(天)',
        '购课时序章进度(节)','购课时已完成哪些课',
        'L01完成日期','L01注册后几天','L02完成日期','L02注册后几天',
        'L03完成日期','L03注册后几天','L04完成日期','L04注册后几天',
        'L05完成日期','L05注册后几天',
        '五节全完成','五节全完成日期','注册→全完成(天)','全完成→购课(天)',
        '购课时机'
    ])
    for r in sorted(records, key=lambda x: x['purchase_date']):
        row = [r['order_id'],r['account_id'],r['level'],
               r['register_date'].isoformat(),r['purchase_date'].isoformat(),r['reg_to_pur'],
               r['progress'],','.join(r['lessons_done'])]
        for ln in lesson_order:
            info = r['lesson_dates'].get(ln, {})
            row.append(info.get('date','').isoformat() if info.get('date') else '')
            row.append(info.get('days_from_reg','') if info else '')
        row.append('是' if r['all5_done'] else '否')
        row.append(r['all5_date'].isoformat() if r['all5_date'] else '')
        row.append(r['reg_to_all5'] if r['reg_to_all5'] is not None else '')
        row.append(r['all5_to_pur'] if r['all5_to_pur'] is not None else '')
        row.append(r['purchase_position'])
        writer.writerow(row)

print(f"\n{'='*80}")
print(f"已导出: {output_path}")
print(f"{'='*80}")
cur.close()
conn.close()
print("分析完成!")
