#!/usr/bin/env python3
"""
Script-component-production pipeline:
Reads script sheet → identifies component rows → generates component config (structured text) → writes back to column H
"""
import requests, json, time, os, sys

CRED_PATH = "/root/.openclaw/credentials/xiaoyan/config.json"
SHEET_TOKEN = "VgPwsodzthsk9Vti8GocrbH2nEe"
SHEET_ID = "wMQVyV"
OUTPUT_DIR = "/root/.openclaw/workspace-xiaoyan/business_production/组件生产/output"

# LLM config
LLM_BASE = "https://ark.cn-beijing.volces.com/api/v3"
LLM_KEY = "32994652-505c-492b-b6da-616ec5c5733c"
LLM_MODEL = "ep-m-20260301164317-vmmj4"

def get_token():
    with open(CRED_PATH) as f:
        c = json.load(f)
    r = requests.post("https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
        json={"app_id": c["apps"][0]["appId"], "app_secret": c["apps"][0]["appSecret"]})
    return r.json()["tenant_access_token"]

def get_sheet_data(token):
    r = requests.get(
        f"https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{SHEET_TOKEN}/values/{SHEET_ID}?majorDimension=ROWS",
        headers={"Authorization": f"Bearer {token}"})
    return r.json()["data"]["valueRange"]["values"]

def parse_row(row):
    """Parse a sheet row into structured data"""
    while len(row) < 9: row.append(None)
    e_text = ""
    if isinstance(row[4], list):
        e_text = "".join([seg.get("text","") for seg in row[4] if isinstance(seg, dict)])
    elif row[4]:
        e_text = str(row[4])
    f_text = ""
    if isinstance(row[5], list):
        f_text = "".join([seg.get("text","") for seg in row[5] if isinstance(seg, dict)])
    elif row[5]:
        f_text = str(row[5])
    return {
        "index": 0,
        "type": str(row[0]).strip() if row[0] else "",
        "id": str(row[1]).strip() if row[1] else "",
        "plot": str(row[2]).strip() if row[2] else "",
        "char": str(row[3]).strip() if row[3] else "",
        "script_cn": e_text.strip(),
        "script_en": f_text.strip(),
        "kp": str(row[6]).strip() if row[6] else "",
        "config_info": str(row[8]).strip() if row[8] else "",
    }

def build_component_rows(values):
    """Extract component rows with context"""
    rows = []
    for i, row in enumerate(values):
        r = parse_row(row)
        r["index"] = i
        r["sheet_row"] = i + 1  # 1-indexed
        rows.append(r)
    
    comp_rows = []
    for i, r in enumerate(rows):
        t = r["type"]
        if t in ("", "TL", "类型"):  # Skip header, TL, and empty
            continue
        # Get context
        ctx_before = []
        for j in range(max(0, i-5), i):
            if rows[j]["char"] and rows[j]["script_cn"]:
                ctx_before.append(f"{rows[j]['char']}: {rows[j]['script_cn']}")
        ctx_after = []
        for j in range(i+1, min(len(rows), i+5)):
            if rows[j]["char"] and rows[j]["script_cn"]:
                ctx_after.append(f"{rows[j]['char']}: {rows[j]['script_cn']}")
        r["ctx_before"] = ctx_before
        r["ctx_after"] = ctx_after
        comp_rows.append(r)
    return comp_rows

SYSTEM_PROMPT = """You are a kids' English education content producer. Generate interactive component configurations for a children's English learning script.

## OUTPUT FORMAT (structured text, NOT JSON)
Use exactly this format:

【组件ID】{id}
【任务标题】{short title, ≤10 chars, no numbers/bullets}
【资源配置】
图片时机：{无 or stage name}
音频载体：{无 or resource}

【情境引入】
{Character: line, one per line, or 无 if none}

【互动内容】
{content based on component type}

【互动反馈】
正确：无
错误：{Character: natural error feedback}

【后置对话】
{Character: next dialogue lines, or 无}

## COMPONENT TYPE RULES

### 对话朗读 (dialogue reading)
- 互动内容: "{Character}: {sentence}（朗读）"
- NO 互动反馈 section
- Example: User: Our living room is the space!（朗读）

### 对话挖空 (dialogue fill-in-blanks)  
- 互动内容:
  题目：{task description}
  {Character}: {sentence with ___}
  选项1：{correct word}（正确）
  选项2：{distractor word}
  辅助信息：{vocab meaning}

### 对话组句 (dialogue sentence building)
- Similar to fill-in-blanks but options are word/phrase chunks
- Max 4 options total
- 答案：{complete sentence}

### 对话选择 (dialogue choice)
- 互动内容:
  要求：{instruction}
  选项：（音频）
  选项1：{correct answer}（正确）
  - 反馈 {Character}: {feedback}
  选项2：{distractor}
  - 反馈 {Character}: {feedback}
- NO 互动反馈 section (feedback inline)

### 对话选读 (dialogue selective reading)
- Similar to choice but ALL options correct (attitude check)
- NO 正确/错误 distinction
- Each option has its own feedback

### 对话表达 (dialogue expression)
- 互动内容:
  表达：{what to express}
  示例回答：User: {example sentence}
  辅助信息：{grammar tip}

### 图片单选/图片多选 (picture choice)
- 互动内容:
  题干：{imperative instruction}
  选项：
  00
  01（正确）[for 单选]
  02
  辅助信息：{word meaning}

### 听力拖拽 (listening drag)
- Options as items to drag/drop
- 正确顺序：XX→XX→XX

### 场景互动 (scene interaction)
- 互动内容: "instruction" the object!
  选项1：word（正确）
  选项2：distractor

## KEY RULES
1. Titles: NO numbers, NO dash separators, ≤10 characters
2. Feedback: ALWAYS character dialogue format "Name: message", NEVER teacher-like instructions. NO space before colon (e.g. "Tom: Hmm." NOT "Tom : Hmm.")
3. Correct feedback: ALWAYS 无
4. Fill blanks: use ___ (3 underscores) with spaces
5. Dialogue choice/selective: options with inline feedback, NO separate 互动反馈
6. Image choice: 仅选项编号00/01/02, 知识点单词用$高亮
7. Dialogue types: 选项1：格式, lowercase start
8. 情境引入/后置对话: 无内容时填 无
9. Context: use both context_before and context_after
10. KP matching: 知识点 must match provided KP
11. Chinese-English spacing: add half-width space between Chinese and English (e.g. "汇报 Jay 不配合")
12. Punctuation: English uses half-width (. , ! ? :), Chinese uses full-width (，。！？：), NEVER mix
13. Dialogue fill-in-blanks: the question sentence MUST contain ___ and be in English (NOT a Chinese description)"""

def llm_generate(prompt):
    """Call LLM to generate component config"""
    try:
        from openai import OpenAI
    except:
        os.system("pip install openai --break-system-packages -q")
        from openai import OpenAI
    
    client = OpenAI(base_url=LLM_BASE, api_key=LLM_KEY)
    resp = client.chat.completions.create(
        model=LLM_MODEL,
        messages=[
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": prompt}
        ],
        temperature=0.3,
        max_tokens=2000,
        extra_body={"thinking": {"type": "disabled"}}
    )
    return resp.choices[0].message.content.strip()

def build_prompt(cr):
    """Build LLM prompt for a component row"""
    prompt = f"""Generate component config for:
- Component Type: {cr['type']}
- Component ID: {cr['id']}
- Knowledge Points: {cr['kp']}

Context (dialogue before):
{chr(10).join(cr['ctx_before']) if cr['ctx_before'] else '(none)'}

Current row:
- Character: {cr['char']}
- Script (CN): {cr['script_cn']}
- Script (EN): {cr['script_en']}
- Plot: {cr['plot']}

Context (dialogue after):
{chr(10).join(cr['ctx_after']) if cr['ctx_after'] else '(none)'}

Generate the structured text component configuration for {cr['type']} type."""
    return prompt

def write_to_sheet(token, sheet_row, content):
    """Write content to column H (index 7) of a specific row"""
    col_letter = "H"
    range_str = f"{SHEET_ID}!{col_letter}{sheet_row}:{col_letter}{sheet_row}"
    url = f"https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/{SHEET_TOKEN}/values"
    
    r = requests.put(
        url,
        headers={
            "Authorization": f"Bearer {token}",
            "Content-Type": "application/json"
        },
        json={
            "valueRange": {
                "range": range_str,
                "values": [[content]]
            }
        }
    )
    result = r.json()
    if result.get("code") != 0:
        print(f"  ❌ Write error: {result}")
        return False
    return True

def main():
    print("📝 Script Component Production Pipeline")
    print(f"Sheet: {SHEET_TOKEN}/{SHEET_ID}")
    
    token = get_token()
    values = get_sheet_data(token)
    print(f"Total rows: {len(values)}")
    
    comp_rows = build_component_rows(values)
    print(f"Component rows: {len(comp_rows)}")
    
    # Save context
    os.makedirs(OUTPUT_DIR, exist_ok=True)
    with open(f"{OUTPUT_DIR}/comp_context.json", "w") as f:
        json.dump([{k: v for k, v in cr.items() if k not in ('index',)} for cr in comp_rows], f, ensure_ascii=False, indent=2)
    
    results = []
    for cr in comp_rows:
        sheet_row = cr["sheet_row"]
        print(f"\n[{cr['type']}] Row {sheet_row} — ID {cr['id']}")
        prompt = build_prompt(cr)
        
        try:
            content = llm_generate(prompt)
            print(f"  ✅ Generated {len(content)} chars")
            
            # ── 格式校验 ──
            from validate_format import validate, Severity
            violations = validate(content, cr['type'])
            errors = [v for v in violations if v.severity == Severity.ERROR]
            warns = [v for v in violations if v.severity == Severity.WARN]
            if errors:
                print(f"  ❌ Format validation failed ({len(errors)} errors):")
                for v in errors:
                    print(f"     - [{v.rule}] {v.detail}")
                results.append({"row": sheet_row, "id": cr["id"], "type": cr["type"],
                                "status": "validation_failed", "errors": [v.detail for v in errors]})
                continue  # Skip write-back
            if warns:
                for v in warns:
                    print(f"  ⚠️  [{v.rule}] {v.detail}")
            
            # Write back
            if write_to_sheet(token, sheet_row, content):
                print(f"  ✅ Written to H{sheet_row}")
                results.append({"row": sheet_row, "id": cr["id"], "type": cr["type"], "status": "ok"})
            else:
                print(f"  ⚠️ Generated but write failed, saved locally")
                results.append({"row": sheet_row, "id": cr["id"], "type": cr["type"], "status": "write_fail"})
        except Exception as e:
            print(f"  ❌ Error: {e}")
            results.append({"row": sheet_row, "id": cr["id"], "type": cr["type"], "status": f"error: {e}"})
        
        time.sleep(0.5)
    
    # Summary
    ok = sum(1 for r in results if r["status"] == "ok")
    print(f"\n{'='*40}")
    print(f"Done: {ok}/{len(results)} written, {len(results)-ok} failed")
    
    with open(f"{OUTPUT_DIR}/results.json", "w") as f:
        json.dump(results, f, ensure_ascii=False, indent=2)

if __name__ == "__main__":
    main()
