#!/usr/bin/env python3
"""Build comprehensive report: 小红书线索 → 用户学习数据匹配"""
import csv
import psycopg2
import psycopg2.extras
from collections import defaultdict
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.utils import get_column_letter
import os, re

PG_PASS = "LdfjdjL83h3h3^$&**YGG*"
PG_CONFIG = {
    "host": "bj-postgres-16pob4sg.sql.tencentcdb.com",
    "port": 28591,
    "user": "ai_member",
    "password": PG_PASS,
    "dbname": "vala_bi",
}

CSV_PATH = os.path.join(os.path.dirname(__file__), "../tmp/may2026_xhs_leads_user_sales.csv")
OUTPUT = os.path.join(os.path.dirname(__file__), "../output/xhs_leads_learning_report.xlsx")

# Read CSV
rows = []
with open(CSV_PATH, "r", encoding="utf-8-sig") as f:
    reader = csv.DictReader(f)
    for r in reader:
        rows.append(r)

print(f"Total rows: {len(rows)}")

# Extract unique user IDs (non-empty)
all_ids = set()
for r in rows:
    uid = r["用户ID"].strip()
    if uid:
        all_ids.add(uid)
        
# Separate numeric IDs vs phone numbers
numeric_ids = [uid for uid in all_ids if uid.isdigit() and len(uid) <= 10]
phone_ids = [uid for uid in all_ids if len(uid) == 11 and uid.isdigit()]
print(f"Numeric IDs (<=10 digits): {len(numeric_ids)}")
print(f"Phone numbers (11 digits): {len(phone_ids)}")
print(f"Total unique IDs: {len(all_ids)}")

conn = psycopg2.connect(**PG_CONFIG)
cur = conn.cursor()

# Query 1: Account info for numeric IDs
numeric_ids_str = ",".join(numeric_ids)
cur.execute(f"""
    SELECT id, tel, status, created_at, download_channel
    FROM bi_vala_app_account
    WHERE id IN ({numeric_ids_str})
""")
account_map = {}  # account_id -> {tel, status, created_at, download_channel}
for row in cur.fetchall():
    account_map[row[0]] = {
        "tel": row[1], "status": row[2], 
        "created_at": row[3], "download_channel": row[4]
    }
print(f"Accounts matched (by id): {len(account_map)}")

# Try matching phone numbers via tel (since tel is masked like 137****3958, we need last 4)
# Actually tel is masked, can't match easily. Let's skip for now.

# Query 2: Course details for matched accounts
matched_account_ids = sorted(account_map.keys())
matched_str = ",".join(str(x) for x in matched_account_ids)

cur.execute(f"""
    SELECT cd.account_id, cd.user_id, cd.course_level, cd.expire_time, cd.deleted_at, cd.created_at
    FROM bi_user_course_detail cd
    WHERE cd.account_id IN ({matched_str})
    ORDER BY cd.account_id, cd.course_level, cd.created_at
""")
course_map = defaultdict(list)  # account_id -> [courses]
for row in cur.fetchall():
    course_map[row[0]].append({
        "user_id": row[1],
        "course_level": row[2],
        "expire_time": row[3],
        "is_deleted": row[4] is not None,
        "created_at": row[5],
    })

# Query 3: Chapter completion across all 8 shards
user_ids_str = ",".join(str(c["user_id"]) for courses in course_map.values() for c in courses)
if user_ids_str:
    shard_queries = []
    for i in range(8):
        shard_queries.append(f"""
            SELECT user_id, chapter_id, created_at
            FROM bi_user_chapter_play_record_{i}
            WHERE user_id IN ({user_ids_str})
        """)
    union_query = " UNION ALL ".join(shard_queries)
    cur.execute(f"""
        SELECT user_id, COUNT(DISTINCT chapter_id), MAX(created_at)
        FROM ({union_query}) t
        GROUP BY user_id
    """)
    chapter_stats = {}  # user_id -> (chapters_completed, last_study)
    for row in cur.fetchall():
        chapter_stats[row[0]] = (row[1], row[2])
else:
    chapter_stats = {}

# Query 4: Orders
cur.execute(f"""
    SELECT o.account_id, o.order_status, o.pay_amount_int, o.goods_name, o.pay_success_date, o.key_from
    FROM bi_vala_order o
    WHERE o.account_id IN ({matched_str})
    ORDER BY o.account_id, o.pay_success_date
""")
order_map = defaultdict(list)
for row in cur.fetchall():
    order_map[row[0]].append({
        "order_status": row[1],
        "pay_amount": row[2] / 100,
        "goods_name": row[3],
        "pay_time": row[4],
        "key_from": row[5],
    })

cur.close()
conn.close()

# Build account-level summary
def get_user_summary(account_id):
    """Get a text summary of courses and learning progress for an account"""
    if account_id not in account_map:
        return "未匹配", "", "", "", "", "", ""
    
    acc = account_map[account_id]
    courses = course_map.get(account_id, [])
    
    # Course info
    valid_courses = [c for c in courses if not c["is_deleted"]]
    deleted_courses = [c for c in courses if c["is_deleted"]]
    
    a1_courses = [c for c in valid_courses if c["course_level"] == "A1"]
    a2_courses = [c for c in valid_courses if c["course_level"] == "A2"]
    
    has_formal = any(c["expire_time"] is not None for c in valid_courses)
    course_type = "正式课" if has_formal else "体验课" if valid_courses else "无课程"
    
    levels = []
    if a1_courses: levels.append("L1")
    if a2_courses: levels.append("L2")
    level_str = "+".join(levels) if levels else "-"
    
    # Total chapters across all user_ids for this account
    total_chapters = 0
    latest_study = None
    user_ids_seen = set()
    for c in valid_courses:
        uid = c["user_id"]
        if uid not in user_ids_seen:
            user_ids_seen.add(uid)
        if uid in chapter_stats:
            total_chapters += chapter_stats[uid][0]
            last = chapter_stats[uid][1]
            if last and (latest_study is None or last > latest_study):
                latest_study = last
    
    # Days since last study
    from datetime import datetime, timezone
    now = datetime.now(timezone.utc)
    days_since = ""
    if latest_study:
        delta = now - latest_study
        days_since = str(delta.days)
    
    # Orders
    orders = order_map.get(account_id, [])
    valid_orders = [o for o in orders if o["order_status"] in (3, 4)]  # completed or refunded
    refunded = [o for o in valid_orders if o["order_status"] == 4]
    completed = [o for o in valid_orders if o["order_status"] == 3]
    
    has_purchased = "是" if valid_orders else "否"
    order_summary = "; ".join([f"{o['goods_name']}(¥{o['pay_amount']})" for o in valid_orders[:3]])
    if len(valid_orders) > 3: order_summary += f" ...共{len(valid_orders)}单"
    
    gmv = sum(o["pay_amount"] for o in valid_orders)
    gsv = sum(o["pay_amount"] for o in completed)
    
    return (
        "已匹配",
        acc["created_at"].strftime("%Y-%m-%d %H:%M") if acc["created_at"] else "",
        acc["download_channel"] or "未设置",
        course_type,
        level_str,
        str(total_chapters),
        latest_study.strftime("%Y-%m-%d %H:%M") if latest_study else "未学习",
        days_since,
        has_purchased,
        gmv,
        gsv,
        order_summary,
    )

# Also try phone number matching via tel last 4
def match_by_phone(phone):
    """Check if phone number (11 digits) can match tel in database"""
    if not phone or len(phone) != 11:
        return None
    # tel is masked like 137****3958, so we can match by first 3 + last 4
    prefix = phone[:3]
    suffix = phone[-4:]
    pattern = f"{prefix}****{suffix}"
    
    conn2 = psycopg2.connect(**PG_CONFIG)
    cur2 = conn2.cursor()
    cur2.execute("SELECT id FROM bi_vala_app_account WHERE tel = %s AND status = 1", (pattern,))
    result = cur2.fetchone()
    cur2.close()
    conn2.close()
    return result[0] if result else None

# Match phone numbers
phone_matches = {}
for phone in phone_ids:
    aid = match_by_phone(phone)
    if aid:
        phone_matches[phone] = aid
print(f"Phone matches: {len(phone_matches)}")

# Build Excel
wb = Workbook()

# --- Sheet 1: Complete Match ---
ws = wb.active
ws.title = "小红书线索学习数据"

# Header style
header_font = Font(name="微软雅黑", bold=True, size=11, color="FFFFFF")
header_fill = PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid")
header_align = Alignment(horizontal="center", vertical="center", wrap_text=True)
cell_border = Border(
    left=Side(style="thin"), right=Side(style="thin"),
    top=Side(style="thin"), bottom=Side(style="thin")
)

headers = [
    "用户ID", "销售归属", "匹配方式", "微伴昵称", "销售表昵称", "手机号",
    "进线日期", "微伴添加时间", "企业标签",
    "账号ID(DB)", "注册时间", "下载渠道", "课程类型", "课程等级",
    "完成课时数", "最后学习时间", "距今日数", "是否付费", "GMV", "GSV", "订单摘要"
]

for col, h in enumerate(headers, 1):
    cell = ws.cell(row=1, column=col, value=h)
    cell.font = header_font
    cell.fill = header_fill
    cell.alignment = header_align
    cell.border = cell_border

# Data rows
row_num = 2
for r in rows:
    uid = r["用户ID"].strip()
    account_id = None
    
    if uid and uid.isdigit() and len(uid) <= 10:
        account_id = int(uid)
    elif uid and len(uid) == 11 and uid.isdigit():
        # Check phone match
        account_id = phone_matches.get(uid)
    
    vals = [
        uid,
        r["销售归属"].strip(),
        r["匹配方式"].strip(),
        r["微伴昵称"].strip(),
        r["销售表昵称"].strip(),
        r["手机号"].strip(),
        r["进线日期"].strip(),
        r["微伴添加时间"].strip(),
        r["企业标签"].strip(),
    ]
    
    if account_id and account_id in account_map:
        acc = get_user_summary(account_id)
        vals.extend([
            str(account_id), acc[1], acc[2], acc[3], acc[4],
            acc[5], acc[6], acc[7], acc[8],
            f"¥{acc[9]:.2f}" if isinstance(acc[9], (int, float)) and acc[9] > 0 else "-",
            f"¥{acc[10]:.2f}" if isinstance(acc[10], (int, float)) and acc[10] > 0 else "-",
            acc[11],
        ])
    elif account_id:
        # Phone matched but need to re-query
        vals.extend([str(account_id), "手机号匹配(需进一步验证)", "", "", "", "", "", "", "", "", "", ""])
    else:
        vals.extend(["未匹配", "", "", "", "", "", "", "", "", "", "", ""])
    
    for col, v in enumerate(vals, 1):
        cell = ws.cell(row=row_num, column=col, value=v)
        cell.border = cell_border
        cell.alignment = Alignment(vertical="center")
    row_num += 1

# Freeze header
ws.freeze_panes = "A2"
# Auto-filter
ws.auto_filter.ref = f"A1:{get_column_letter(len(headers))}{row_num-1}"

# Column widths
col_widths = [10, 10, 22, 22, 18, 14, 12, 20, 18, 12, 18, 14, 10, 8, 10, 18, 8, 8, 10, 10, 30]
for i, w in enumerate(col_widths, 1):
    ws.column_dimensions[get_column_letter(i)].width = w

# --- Sheet 2: Summary Stats ---
ws2 = wb.create_sheet("汇总统计")

# Stats
total_rows_all = len(rows)
matched_count = sum(1 for r in rows if r["用户ID"].strip() and r["用户ID"].strip().isdigit() and len(r["用户ID"].strip()) <= 10 and int(r["用户ID"].strip()) in account_map)
phone_matched_count = sum(1 for r in rows if r["用户ID"].strip() and len(r["用户ID"].strip()) == 11 and r["用户ID"].strip() in phone_matches)
unmatched = total_rows_all - matched_count - phone_matched_count

# Learning stats
matched_accounts = set()
for r in rows:
    uid = r["用户ID"].strip()
    if uid and uid.isdigit() and len(uid) <= 10:
        aid = int(uid)
        if aid in account_map:
            matched_accounts.add(aid)
    elif uid and len(uid) == 11 and uid.isdigit():
        if uid in phone_matches:
            matched_accounts.add(phone_matches[uid])

accounts_with_courses = [aid for aid in matched_accounts if aid in course_map]
has_learning = 0
total_chapters = 0
purchased = 0
for aid in matched_accounts:
    courses = course_map.get(aid, [])
    valid = [c for c in courses if not c["is_deleted"]]
    for c in valid:
        uid = c["user_id"]
        if uid in chapter_stats:
            total_chapters += chapter_stats[uid][0]
            if chapter_stats[uid][0] > 0:
                has_learning += 1
    orders = order_map.get(aid, [])
    if any(o["order_status"] in (3, 4) for o in orders):
        purchased += 1

stats_data = [
    ["指标", "数值"],
    ["CSV总行数", str(total_rows_all)],
    ["有用户ID行数", str(sum(1 for r in rows if r["用户ID"].strip()))],
    ["已匹配账号(by ID)", str(matched_count)],
    ["已匹配账号(by手机号)", str(phone_matched_count)],
    ["未匹配", str(unmatched)],
    ["匹配账号有课程", str(len(accounts_with_courses))],
    ["完成过课时学习的用户", str(has_learning)],
    ["有付费订单的用户", str(purchased)],
    ["总完成课时数", str(total_chapters)],
]

# Sales breakdown
sales_stats = defaultdict(lambda: {"total": 0, "matched": 0, "with_course": 0, "with_learning": 0, "purchased": 0})
for r in rows:
    sales = r["销售归属"].strip()
    sales_stats[sales]["total"] += 1
    uid = r["用户ID"].strip()
    aid = None
    if uid and uid.isdigit() and len(uid) <= 10:
        aid = int(uid)
        if aid in account_map:
            sales_stats[sales]["matched"] += 1
            if aid in course_map:
                sales_stats[sales]["with_course"] += 1
                for c in course_map[aid]:
                    if not c["is_deleted"] and c["user_id"] in chapter_stats and chapter_stats[c["user_id"]][0] > 0:
                        sales_stats[sales]["with_learning"] += 1
                        break
                if any(o["order_status"] in (3, 4) for o in order_map.get(aid, [])):
                    sales_stats[sales]["purchased"] += 1

stats_data.append([])
stats_data.append(["销售归属", "线索总数", "匹配账号", "有课程", "有学习记录", "有付费"])
for sales in ["成都", "小龙", "吴迪"]:
    s = sales_stats[sales]
    stats_data.append([sales, s["total"], s["matched"], s["with_course"], s["with_learning"], s["purchased"]])

for row_idx, row_data in enumerate(stats_data, 1):
    for col_idx, val in enumerate(row_data, 1):
        cell = ws2.cell(row=row_idx, column=col_idx, value=val)
        if row_idx == 1:
            cell.font = header_font
            cell.fill = header_fill
            cell.alignment = header_align
        cell.border = cell_border

ws2.column_dimensions["A"].width = 25
ws2.column_dimensions["B"].width = 15
ws2.column_dimensions["C"].width = 12
ws2.column_dimensions["D"].width = 10
ws2.column_dimensions["E"].width = 12
ws2.column_dimensions["F"].width = 10

wb.save(OUTPUT)
print(f"\nReport saved to: {OUTPUT}")
print(f"Total matched accounts: {len(matched_accounts)}")
print(f"With courses: {len(accounts_with_courses)}")
print(f"With learning: {has_learning}")
print(f"With purchases: {purchased}")
