"""
Coba 自動標籤服務(階段 3e)。

每則新訊息都會被丟給 Haiku 4.5 做分類,產生 1-3 個 tag。

設計原則:
  - 用 Haiku(便宜、快、足夠準)
  - 標準 tag 集合(讓 filter pill 穩定)+ 可動態加 entity tag(人名 / 專案名)
  - 庫柏自己的訊息不重新標(它生成時就會帶 tag)
  - 失敗時靜默忽略(tag 不是核心功能,不影響對話)
"""

import json
import logging
import re
from uuid import UUID

import anthropic
from sqlalchemy import select

from app.config import settings
from app.core.ws_manager import manager as ws_manager
from app.database import AsyncSessionLocal
from app.models.channel import Message


logger = logging.getLogger("coba.tagger")


# 標準 type tag(filter pill 用這幾個)— Coba 必須從這裡選一個
STANDARD_TYPE_TAGS = [
    "📢 公告",      # 重要訊息、決策、佈達
    "🆘 求助",      # 卡關、問題、求支援
    "💡 靈感",      # 想法、分享、新點子
    "🤝 客戶",      # 客戶相關討論
    "📌 決策",      # 已做的決定
    "🚀 進度",      # 工作進度更新
    "💬 閒聊",      # 閒談、社交
]

TAG_SYSTEM_PROMPT = f"""你是分類助手。讀使用者訊息,產出 JSON 陣列形式的標籤。

【規則】
1. 從以下「類型標籤」**只選 1 個**(就算同時像兩種,也只選最主要的):
{chr(10).join(f"   - {t}" for t in STANDARD_TYPE_TAGS)}

2. 額外可以加 0-2 個「實體標籤」(人名、客戶名、專案名、技術名、產品名等)。
   實體標籤不要 emoji,純文字。例如:客戶A、React、Q4預算、阿明

【輸出格式】嚴格 JSON 陣列,不要 markdown,不要解釋。範例:

訊息「客戶 A 剛回信說合約要修第三條」
回:["📢 公告", "客戶A", "合約"]

訊息「我 useEffect 噴 warning 怎解」
回:["🆘 求助", "React"]

訊息「今天好累哈哈」
回:["💬 閒聊"]

訊息「我覺得我們的定位應該調整成 B2B」
回:["💡 靈感", "定位"]

只回陣列,不要其他字。
"""


def _parse_tags(raw: str) -> list[str]:
    """容錯地把 Haiku 回傳的字串解析成 tag list。"""
    raw = raw.strip()
    # 抓第一段 JSON 陣列
    m = re.search(r"\[.*?\]", raw, flags=re.DOTALL)
    if not m:
        return []
    try:
        result = json.loads(m.group(0))
    except json.JSONDecodeError:
        return []
    if not isinstance(result, list):
        return []
    # 過濾:每個元素必須是非空字串、長度 < 30
    cleaned = []
    for item in result:
        if isinstance(item, str) and item.strip() and len(item) <= 30:
            cleaned.append(item.strip())
    # 去重
    seen = set()
    out = []
    for t in cleaned:
        if t not in seen:
            seen.add(t)
            out.append(t)
    return out[:5]   # 最多 5 個


async def tag_message(message_id: UUID) -> None:
    """背景任務:把 Haiku 標好的 tags 寫回訊息 + 廣播。"""
    if not settings.ANTHROPIC_API_KEY:
        return

    async with AsyncSessionLocal() as db:
        msg = (await db.execute(
            select(Message).where(Message.id == message_id)
        )).scalar_one_or_none()
        if msg is None or msg.tags:   # 已有 tag 就不重新標
            return
        # 庫柏自己的訊息不需要分類(他自己知道在說什麼)
        if msg.user_id is None:
            return
        # 太短的訊息(例如「好」「ok」)直接給「💬 閒聊」省 token
        if len(msg.content.strip()) <= 3:
            msg.tags = ["💬 閒聊"]
            await db.commit()
            await _broadcast_tags(msg)
            return

        try:
            client = anthropic.AsyncAnthropic(api_key=settings.ANTHROPIC_API_KEY)
            response = await client.messages.create(
                model=settings.CLAUDE_MODEL_HAIKU,
                max_tokens=128,
                system=TAG_SYSTEM_PROMPT,
                messages=[{"role": "user", "content": msg.content}],
            )
            raw = "".join(b.text for b in response.content if b.type == "text")
            tags = _parse_tags(raw)
            if not tags:
                tags = ["💬 閒聊"]   # fallback
            msg.tags = tags
            await db.commit()
            await _broadcast_tags(msg)

            usage = response.usage
            cost = (usage.input_tokens / 1_000_000) * 1.0 + (usage.output_tokens / 1_000_000) * 5.0
            logger.info(
                "tag msg=%s tags=%s tokens=%d/%d cost=US$%.6f",
                str(message_id)[:8], tags, usage.input_tokens, usage.output_tokens, cost,
            )
            # 階段 7-A:寫 AgentActivity
            try:
                from app.models.agent_activity import AgentActivity
                async with AsyncSessionLocal() as _adb:
                    _adb.add(AgentActivity(
                        activity_type="auto_tag",
                        user_id=msg.user_id,
                        summary=f"標籤:{', '.join(tags)}",
                        success=True,
                        extra={"message_id": str(message_id), "tags": tags},
                        cost_usd=cost,
                    ))
                    await _adb.commit()
            except Exception:
                logger.exception("寫 AgentActivity (auto_tag) 失敗")
        except Exception as e:
            logger.warning("tag failed: %s", e)


async def _broadcast_tags(msg: Message) -> None:
    """廣播 message_tagged event(前端依此即時更新 tag 顯示)。"""
    await ws_manager.broadcast({
        "type": "message_tagged",
        "channel_id": str(msg.channel_id),
        "message_id": str(msg.id),
        "tags": msg.tags or [],
    })


def trigger_tagging(message_id: UUID) -> None:
    """同步入口,起背景任務不阻塞 HTTP。"""
    import asyncio
    asyncio.create_task(tag_message(message_id))
