"""
階段 3c WebSocket 即時對話自動化測試。

開兩個 Chromium(各自獨立 profile),模擬:
  - Robert(owner) 登入
  - Member(member-test@x.com)登入
  - 兩人都進「閒聊」頻道
  - Robert 發訊息 → 等 1 秒 → 確認 Member 那邊看到了
  - Member 發訊息 → 等 1 秒 → 確認 Robert 那邊看到了
  - 截圖兩人畫面

執行:
  ./.venv/Scripts/python.exe tools/web_scraper/test_chat_realtime.py
"""

import asyncio
import sys
import time
from pathlib import Path

try:
    sys.stdout.reconfigure(encoding="utf-8")
except (AttributeError, ValueError):
    pass

from playwright.async_api import async_playwright, Browser, BrowserContext, Page


HERE = Path(__file__).resolve().parent
PROFILE_DIR = HERE / "profile"
OUT_DIR = HERE / "out"
OUT_DIR.mkdir(exist_ok=True)

BASE_URL = "http://localhost:8000"

# 測試帳號
ROBERT = {"email": "ssbb30529@gmail.com", "password": "a183b729", "name": "Robert Su"}
MEMBER = {"email": "member-test@x.com", "password": "member123", "name": "Member Test"}


async def login_and_open_chat(context: BrowserContext, account: dict, label: str) -> Page:
    """登入後切到「💬 對話」section,點「💬 閒聊」頻道。"""
    page = await context.new_page()
    page.set_default_timeout(10000)

    print(f"[{label}] 開瀏覽器導向 {BASE_URL}")
    await page.goto(BASE_URL, wait_until="domcontentloaded")

    # 看看是不是已經自動登入(persistent profile)
    try:
        await page.wait_for_selector("#dashboard-view", state="visible", timeout=2000)
        print(f"[{label}] 偵測到已自動登入,跳過登入步驟")
    except Exception:
        # 沒登入 → 走登入流程
        await page.wait_for_selector("#login-view", state="visible")
        # 先清空再填(預設值是 Robert 的)
        await page.fill("#email", account["email"])
        await page.fill("#password", account["password"])
        await page.click("#login-btn")
        # 等 dashboard 出現
        await page.wait_for_selector("#dashboard-view", state="visible")
        print(f"[{label}] 登入成功:{account['email']}")

    # 切到對話 section
    await page.click('button.section-tab[data-section="chat"]')
    await asyncio.sleep(0.5)   # 等動畫

    # 階段 3e 後改成單一頻道,自動選中,等訊息列表載入即可
    await page.wait_for_function(
        "() => document.querySelectorAll('.chat-message, .chat-empty').length > 0",
        timeout=8000,
    )
    print(f"[{label}] 已進入團隊對話頻道")
    return page


async def get_message_count(page: Page) -> int:
    """目前頻道顯示了幾則訊息(不含 empty placeholder)。"""
    return await page.locator(".chat-message").count()


async def get_last_message_text(page: Page) -> str:
    """最後一則訊息的內容。"""
    locator = page.locator(".chat-message").last.locator(".chat-msg-bubble")
    return (await locator.inner_text()).strip()


async def get_read_avatar_count(page: Page) -> int:
    """最新訊息底下的「已讀大頭貼」個數(扣掉 +N 那顆 overflow)。"""
    return await page.locator(".chat-message.own").last.locator(".chat-msg-read-avatar").count()


async def send_message(page: Page, content: str) -> None:
    await page.fill("#chat-input", content)
    await page.press("#chat-input", "Enter")


async def main():
    async with async_playwright() as pw:
        # 兩個獨立 persistent context(分別模擬兩台不同電腦)
        print("啟動 Robert Chromium...")
        ctx_robert = await pw.chromium.launch_persistent_context(
            user_data_dir=str(PROFILE_DIR / "robert"),
            headless=False,
            args=["--disable-blink-features=AutomationControlled"],
            viewport={"width": 1280, "height": 800},
        )
        print("啟動 Member Chromium...")
        ctx_member = await pw.chromium.launch_persistent_context(
            user_data_dir=str(PROFILE_DIR / "member"),
            headless=False,
            args=["--disable-blink-features=AutomationControlled"],
            viewport={"width": 1280, "height": 800},
        )

        results = {"passes": [], "fails": []}

        try:
            # 兩邊同時登入
            page_robert, page_member = await asyncio.gather(
                login_and_open_chat(ctx_robert, ROBERT, "Robert"),
                login_and_open_chat(ctx_member, MEMBER, "Member"),
            )

            # ─────────── 測試 1:Robert 發 → Member 即時看到 ───────────
            stamp = int(time.time())
            msg_from_robert = f"[E2E test {stamp}] Robert 發的訊息"
            print(f"\n--- TEST 1:Robert 發訊息 ---")
            count_before = await get_message_count(page_member)
            await send_message(page_robert, msg_from_robert)

            # 等 Member 那邊出現新訊息(WebSocket 應該 < 1 秒)
            t0 = time.time()
            try:
                await page_member.wait_for_function(
                    f"document.querySelectorAll('.chat-message').length > {count_before}",
                    timeout=3000,
                )
                elapsed = (time.time() - t0) * 1000
                last = await get_last_message_text(page_member)
                if last == msg_from_robert:
                    results["passes"].append(f"TEST 1: Robert 發訊息 → Member 在 {elapsed:.0f}ms 內收到 ✓")
                else:
                    results["fails"].append(f"TEST 1: Member 收到的訊息內容不對。期望:{msg_from_robert!r} 收到:{last!r}")
            except Exception as e:
                results["fails"].append(f"TEST 1: Member 沒在 3 秒內收到 Robert 的訊息 ({e})")

            # ─────────── 測試 2:Member 發 → Robert 即時看到 ───────────
            stamp = int(time.time())
            msg_from_member = f"[E2E test {stamp}] Member 回的訊息"
            print(f"\n--- TEST 2:Member 發訊息 ---")
            count_before = await get_message_count(page_robert)
            await send_message(page_member, msg_from_member)
            t0 = time.time()
            try:
                await page_robert.wait_for_function(
                    f"document.querySelectorAll('.chat-message').length > {count_before}",
                    timeout=3000,
                )
                elapsed = (time.time() - t0) * 1000
                last = await get_last_message_text(page_robert)
                if last == msg_from_member:
                    results["passes"].append(f"TEST 2: Member 發訊息 → Robert 在 {elapsed:.0f}ms 內收到 ✓")
                else:
                    results["fails"].append(f"TEST 2: Robert 收到的訊息內容不對。期望:{msg_from_member!r} 收到:{last!r}")
            except Exception as e:
                results["fails"].append(f"TEST 2: Robert 沒在 3 秒內收到 Member 的訊息 ({e})")

            # ─────────── 測試 3:Robert 自己訊息底下出現 Member 已讀頭像 ───────────
            print(f"\n--- TEST 3:Robert 訊息底下應出現 Member 的已讀頭像 ---")
            await asyncio.sleep(0.5)  # 等 read_receipt event 抵達 Robert

            # 找 Robert 視角下,自己發的最新訊息
            try:
                avatar_count = await page_robert.locator(".chat-message.own").last.locator(".chat-msg-read-avatar").count()
                if avatar_count >= 1:
                    results["passes"].append(f"TEST 3: Robert 訊息出現 {avatar_count} 個已讀頭像 ✓")
                else:
                    results["fails"].append(f"TEST 3: Robert 訊息底下沒有已讀頭像(預期至少 1 個)")
            except Exception as e:
                results["fails"].append(f"TEST 3: 找不到 Robert 自己的訊息 ({e})")

            # ─────────── 截圖 ───────────
            print("\n--- 截圖兩邊畫面 ---")
            shot_robert = OUT_DIR / "chat_robert.png"
            shot_member = OUT_DIR / "chat_member.png"
            await page_robert.screenshot(path=str(shot_robert), full_page=False)
            await page_member.screenshot(path=str(shot_member), full_page=False)
            print(f"Robert 截圖:{shot_robert}")
            print(f"Member 截圖:{shot_member}")

        finally:
            await ctx_robert.close()
            await ctx_member.close()

        # ─────────── 結果 ───────────
        print("\n" + "=" * 60)
        print(" 測試結果")
        print("=" * 60)
        for p in results["passes"]:
            print(f"[PASS] {p}")
        for f in results["fails"]:
            print(f"[FAIL] {f}")
        print("=" * 60)
        return 0 if not results["fails"] else 1


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