"""階段 9-B 完整 regression(在 NAS 上跑)。

測試 Cooper 對你想要的「員工級執行者」流程的真實能力。

劇本(全部用 sim_line 偽造輸入,不打擾真實 LINE 群組):

  T1. Robert(老闆角色)派 3 個任務(用「老闆口氣」分別派給 阿綠 / Jonina / Robert):
      - 「阿綠 你週五前把客戶 A 的提案 v3 寄出去」
      - 「Jonina 明天 17:00 前把月底結算表整理好」
      - 「我自己晚上來盤一下後台 server 流量」
      → 期望:Cooper 自動建 3 個任務,assignee 對齊

  T2. 同仁口語回覆認領:
      - 阿綠 回:「OK 收到」      → 期望:任務 1 認領
      - Jonina 回:「收到了 沒問題」 → 期望:任務 2 認領
      - Robert 自己沒回           → 任務 3 維持「未認領」

  T3. @庫柏 列認領狀態:
      - Robert: 「@庫柏 那剛才派的任務誰接了誰沒接?」
      → 期望:Cooper 用 list_assignment_status 工具,回 2 已接 1 未接

  T4. 任務狀態變更:
      - 阿綠 私訊:「@庫柏 把客戶 A 那個提案任務改 in_progress」
      → 期望:Cooper update_task_status

  T5. 列任務:
      - Robert: 「@庫柏 列阿綠的任務」
      → 期望:Cooper list_tasks(assignee_name=神狙手阿綠)

  T6. KPI:
      - Robert: 「@庫柏 團隊本週完成率多少?」
      → 期望:Cooper get_team_completion_rate

每個 case 印:Cooper 是否回應、用什麼工具、回應內容、cost。

跑法:推到 NAS 後 SSH 跑
"""
from __future__ import annotations

import argparse
import asyncio
import os
import sys
import uuid
from datetime import datetime, timezone

# 載 .env
ENV = "/volume1/homes/robertsu/coba/app/.env"
if os.path.exists(ENV):
    for line in open(ENV):
        if "=" in line and not line.startswith("#"):
            k, v = line.strip().split("=", 1)
            os.environ.setdefault(k, v)

# 強制不推真 LINE,且把 quiet hours 縮到不影響(這是 NAS-side 模擬)
os.environ["LINE_NO_PUSH"] = "1"
os.environ["EXTRACTOR_LIVE"] = "true"
os.environ["PROACTIVE_QUIET_START"] = "3"
os.environ["PROACTIVE_QUIET_END"] = "4"

sys.path.insert(0, "/volume1/homes/robertsu/coba/app")

from sqlalchemy import select, func
from app.database import AsyncSessionLocal
from app.models.agent_activity import AgentActivity
from app.models.channel import Channel, Message
from app.models.task import Task
from app.models.user import User
from app.services import line_bridge


PASS, FAIL = [], []


def check(label, cond, detail=""):
    mark = "PASS" if cond else "FAIL"
    line = f"[{mark}] {label} {detail}"
    print(line)
    (PASS if cond else FAIL).append(line)
    return cond


async def get_user(name_substr):
    async with AsyncSessionLocal() as db:
        u = (await db.execute(
            select(User).where(User.display_name.contains(name_substr), User.deleted_at.is_(None)).limit(1)
        )).scalar_one_or_none()
    return u


async def get_group_channel():
    async with AsyncSessionLocal() as db:
        ch = (await db.execute(
            select(Channel).where(Channel.line_source_type == "group", Channel.deleted_at.is_(None)).limit(1)
        )).scalar_one_or_none()
    return ch


async def latest_coba_msg_id(channel_id):
    async with AsyncSessionLocal() as db:
        m = (await db.execute(
            select(Message)
            .where(Message.user_id.is_(None), Message.channel_id == channel_id, Message.deleted_at.is_(None))
            .order_by(Message.created_at.desc())
            .limit(1)
        )).scalars().first()
        return m.id if m else None


async def feed(user, channel, text, dm=False):
    """以該 user 身份模擬一則訊息進系統(走 line_bridge.handle_event)。
    回傳:Cooper 回應(text 或 None),Cooper 用的 tool_calls,activity 增量
    """
    line_msg_id = "rgs_" + uuid.uuid4().hex[:10]
    src = (
        {"type": "user", "userId": user.line_user_id or f"U_{user.id.hex[:6]}"}
        if dm else
        {"type": "group", "groupId": channel.line_source_id, "userId": user.line_user_id or f"U_{user.id.hex[:6]}"}
    )
    event = {
        "type": "message",
        "replyToken": "rt_" + uuid.uuid4().hex[:8],
        "source": src,
        "message": {"type": "text", "id": line_msg_id, "text": text},
    }
    before_act_count = await _count_acts()
    before_coba = await latest_coba_msg_id(channel.id)
    await line_bridge.handle_event(event)

    # 等 Cooper 回應(只在他需要時才回 — 沒 @ 就不回)
    response_text = None
    tool_calls = []
    for _ in range(45):
        await asyncio.sleep(1)
        latest = await latest_coba_msg_id(channel.id)
        if latest and latest != before_coba:
            async with AsyncSessionLocal() as db:
                m = (await db.execute(select(Message).where(Message.id == latest))).scalar_one_or_none()
                response_text = m.content if m else None
            break
    # 多等 5 秒讓 activity / acceptance / extractor 全部寫完
    await asyncio.sleep(6)

    # 拿剛才這段時間新加的 activities
    async with AsyncSessionLocal() as db:
        new_acts = (await db.execute(
            select(AgentActivity)
            .order_by(AgentActivity.created_at.desc())
            .limit(20)
        )).scalars().all()
    # 抓 Cooper 用過哪些 tool(從 line_response activity 的 extra.tool_calls)
    for a in new_acts:
        if a.activity_type == "line_response" and a.extra and a.extra.get("tool_calls"):
            for tc in a.extra["tool_calls"]:
                tool_calls.append(tc.get("name"))
            break
    return {
        "response_text": response_text,
        "tool_calls": tool_calls,
        "new_acts": new_acts[:10],
    }


async def _count_acts():
    async with AsyncSessionLocal() as db:
        return (await db.execute(select(func.count()).select_from(AgentActivity))).scalar() or 0


async def main():
    print("=" * 70)
    print(" 階段 9-B 完整 regression(劇本演 6 個情境)")
    print("=" * 70)

    robert = await get_user("Robert")
    aliu = await get_user("阿綠") or await get_user("綠")
    jonina = await get_user("Jonina")
    ch = await get_group_channel()

    if not all([robert, aliu, jonina, ch]):
        print(f"❌ 缺前置:robert={robert}, 阿綠={aliu}, jonina={jonina}, channel={ch}")
        sys.exit(1)
    print(f"  Robert={robert.display_name} 阿綠={aliu.display_name} Jonina={jonina.display_name} ch={ch.name}")

    # === T1:派工 3 個任務 ===
    print("\n" + "─" * 70)
    print(" T1: Robert 派工 3 個任務(extractor 自動建)")
    print("─" * 70)
    base_task_count = await _count_tasks_alive()
    base_create_acts = await _count_create_acts()

    r1 = await feed(robert, ch, f"阿綠 你週五前把客戶 A 的提案 v3 寄出去 [rgs1-{int(datetime.now().timestamp())}]")
    print(f"  → Cooper 回應:{(r1['response_text'] or '(沉默)')[:80]}")
    r2 = await feed(robert, ch, f"Jonina 明天 17:00 前把月底結算表整理好 [rgs2-{int(datetime.now().timestamp())}]")
    print(f"  → Cooper 回應:{(r2['response_text'] or '(沉默)')[:80]}")
    r3 = await feed(robert, ch, f"我自己晚上來盤一下後台 server 流量 [rgs3-{int(datetime.now().timestamp())}]")
    print(f"  → Cooper 回應:{(r3['response_text'] or '(沉默)')[:80]}")

    after_create = await _count_create_acts()
    new_creates = after_create - base_create_acts
    check(f"T1.1 task_extractor 建 ≥ 2 任務(實際 {new_creates})",
          new_creates >= 2)

    # 找剛建的任務看 assignee 對不對
    tasks = await _latest_n_tasks(5)
    titles = [t.title for t in tasks]
    print(f"  最新任務 titles: {titles}")
    aliu_task = next((t for t in tasks if "客戶" in t.title or "提案" in t.title), None)
    jonina_task = next((t for t in tasks if "結算" in t.title or "月底" in t.title), None)
    check("T1.2 阿綠的「客戶提案」任務有建", aliu_task is not None)
    check("T1.3 Jonina 的「月底結算」任務有建", jonina_task is not None)
    if aliu_task:
        check("T1.4 客戶提案 → 指派阿綠", aliu_task.assignee_id == aliu.id,
              f"actual={aliu_task.assignee_id}")
    if jonina_task:
        check("T1.5 結算表 → 指派 Jonina", jonina_task.assignee_id == jonina.id,
              f"actual={jonina_task.assignee_id}")

    # === T2:同仁認領 ===
    print("\n" + "─" * 70)
    print(" T2: 同仁口語回覆認領")
    print("─" * 70)
    base_accept = await _count_accept_acts()
    await feed(aliu, ch, "OK 收到")
    await feed(jonina, ch, "收到了 沒問題")
    # Robert 不回(不模擬),測未認領
    after_accept = await _count_accept_acts()
    new_accepts = after_accept - base_accept
    check(f"T2.1 task_accepted activity ≥ 2(實際 {new_accepts})",
          new_accepts >= 2)

    # === T3:@庫柏 列認領狀態 ===
    print("\n" + "─" * 70)
    print(" T3: @庫柏 列認領狀態")
    print("─" * 70)
    r = await feed(robert, ch, "@庫柏 那剛才派的任務誰接了誰沒接?")
    print(f"  → Cooper 回應(摘):{(r['response_text'] or '(沉默)')[:200]}")
    print(f"  → tool_calls:{r['tool_calls']}")
    check("T3.1 Cooper 用 list_assignment_status 工具",
          "list_assignment_status" in r["tool_calls"])
    if r["response_text"]:
        check("T3.2 回應提到「接」/「未」/任一名字",
              any(k in r["response_text"] for k in ("接", "未", "阿綠", "Jonina", "已收")),
              r["response_text"][:60])

    # === T4:狀態變更(阿綠私訊改 in_progress)===
    print("\n" + "─" * 70)
    print(" T4: 阿綠私訊「@庫柏 把客戶 A 那個提案任務改 in_progress」")
    print("─" * 70)
    # 找 1對1 channel
    async with AsyncSessionLocal() as db:
        dm_ch = (await db.execute(
            select(Channel).where(Channel.line_source_type == "user", Channel.deleted_at.is_(None)).limit(1)
        )).scalar_one_or_none()
    if dm_ch:
        # 在 DM channel 用阿綠身分發
        r = await feed(aliu, dm_ch, "@庫柏 把客戶 A 那個提案任務改 in_progress", dm=True)
        print(f"  → Cooper 回應(摘):{(r['response_text'] or '(沉默)')[:150]}")
        print(f"  → tool_calls:{r['tool_calls']}")
        # 重抓 task,看 status
        if aliu_task:
            async with AsyncSessionLocal() as db:
                t = (await db.execute(select(Task).where(Task.id == aliu_task.id))).scalar_one_or_none()
            if t:
                check("T4.1 客戶提案任務狀態 = in_progress",
                      t.status == "in_progress",
                      f"actual={t.status}")
    else:
        print("  (沒 1對1 channel,跳過)")

    # === T5:列任務 ===
    print("\n" + "─" * 70)
    print(" T5: @庫柏 列阿綠的任務")
    print("─" * 70)
    r = await feed(robert, ch, "@庫柏 列阿綠的任務")
    print(f"  → Cooper 回應(摘):{(r['response_text'] or '(沉默)')[:200]}")
    print(f"  → tool_calls:{r['tool_calls']}")
    check("T5.1 Cooper 用 list_tasks 工具", "list_tasks" in r["tool_calls"])

    # === T6:團隊 KPI ===
    print("\n" + "─" * 70)
    print(" T6: @庫柏 團隊本週完成率多少?")
    print("─" * 70)
    r = await feed(robert, ch, "@庫柏 團隊本週的完成率是多少?")
    print(f"  → Cooper 回應(摘):{(r['response_text'] or '(沉默)')[:200]}")
    print(f"  → tool_calls:{r['tool_calls']}")
    check("T6.1 Cooper 用 get_team_completion_rate 工具",
          "get_team_completion_rate" in r["tool_calls"])

    # === 結算 ===
    print("\n" + "=" * 70)
    print(f" 結果:PASS={len(PASS)} FAIL={len(FAIL)}")
    print("=" * 70)
    if FAIL:
        print("\n以下 FAIL:")
        for f in FAIL:
            print(f"  {f}")
    sys.exit(0 if not FAIL else 1)


async def _count_tasks_alive():
    async with AsyncSessionLocal() as db:
        return (await db.execute(select(func.count()).select_from(Task).where(Task.deleted_at.is_(None)))).scalar() or 0


async def _count_create_acts():
    async with AsyncSessionLocal() as db:
        return (await db.execute(
            select(func.count()).select_from(AgentActivity).where(AgentActivity.activity_type == "task_create_auto")
        )).scalar() or 0


async def _count_accept_acts():
    async with AsyncSessionLocal() as db:
        return (await db.execute(
            select(func.count()).select_from(AgentActivity).where(AgentActivity.activity_type == "task_accepted")
        )).scalar() or 0


async def _latest_n_tasks(n):
    async with AsyncSessionLocal() as db:
        return (await db.execute(
            select(Task).where(Task.deleted_at.is_(None)).order_by(Task.created_at.desc()).limit(n)
        )).scalars().all()


if __name__ == "__main__":
    asyncio.run(main())
