"""用户快捷指令(触发词 → 完整指令)。渠道无关,入口层确定性展开。

存储:`workspace/users/<user_id>/.memory/shortcuts.md` —— 蹭 memory 的 per-user 存储壳
(同一 workspace 内按 user_id 隔离,agent 已有该目录写权限),但**与 memory 是两种机制**:

- memory 是注进 system prompt、给模型**参考**的软上下文(概率召回)。
- 快捷指令**不进上下文**:展开发生在入口层、模型跑之前 —— 每条入站消息先经 `expand()`
  查表,整条精确命中触发词就把文本替换成完整指令再跑 agent。所以存再多条,平时上下文也是 0;
  触发时进上下文的就是那条完整指令本身(= 用户本来要打的字),无额外 token。

维护(agent 自管,同 memory):用户在对话里说"记个快捷词:X → Y",模型往 shortcuts.md 写一行
(memory 契约里加了一句告诉它格式);触发不靠模型,靠本模块解析,确定、零歧义。

格式(markdown 两列表,容错解析;表头/分隔行自动跳过):

    | 触发词 | 指令 |
    |---|---|
    | 简报 | 给我输出一份昨日的 AI 新闻简报 |

匹配语义:整条消息 `strip()` + `casefold()` 后与某触发词**精确相等**才展开;
"帮我出个简报" 不命中(当普通消息走)。与「新话题」魔法命令同风格,零误伤。
（触发词含 `|` 会破坏表格解析 —— 约定触发词不含竖线;指令正文含竖线也会被截断,同样避免。）
"""
from __future__ import annotations

import re
from pathlib import Path
from typing import Dict, Optional, Tuple
from uuid import UUID

# 表头行的触发词(解析时跳过,避免把表头当成一条快捷词)
_HEADER_TRIGGERS = {"触发词", "触发", "快捷词", "快捷指令", "命令", "trigger", "shortcut"}
# markdown 表格分隔行的单元格:`---` / `:--` / `:-:` 之类
_SEP_RE = re.compile(r"^:?-+:?$")


def _shortcuts_file(workspace_dir: Path, user_id: UUID) -> Path:
    return workspace_dir / "users" / str(user_id) / ".memory" / "shortcuts.md"


def _normalize(s: str) -> str:
    return s.strip().casefold()


def _is_separator(cell: str) -> bool:
    return bool(_SEP_RE.match(cell.replace(" ", "")))


def parse_shortcuts(text: str) -> Dict[str, str]:
    """解析 shortcuts.md 文本 → {归一化触发词: 完整指令}。纯函数,可测。

    容错:只认以 `|` 起头的表格行;跳过分隔行、表头行、空单元格行;
    触发词重复时**先出现者赢**(首行优先,和人读顺序一致)。
    """
    mapping: Dict[str, str] = {}
    for raw in text.splitlines():
        line = raw.strip()
        if not line.startswith("|"):
            continue
        cells = [c.strip() for c in line.strip("|").split("|")]
        if len(cells) < 2:
            continue
        trigger, prompt = cells[0], cells[1]
        if not trigger or not prompt:
            continue
        if _is_separator(trigger) and _is_separator(prompt):
            continue  # 分隔行 |---|---|
        key = _normalize(trigger)
        if not key or key in _HEADER_TRIGGERS:
            continue  # 空或表头
        mapping.setdefault(key, prompt)  # 首行优先
    return mapping


def load_shortcuts(workspace_dir: Path, user_id: UUID) -> Dict[str, str]:
    """读该用户 shortcuts.md 并解析;文件不存在 / 读失败 → 空表(不抛,不挡入站)。"""
    p = _shortcuts_file(workspace_dir, user_id)
    if not p.is_file():
        return {}
    try:
        return parse_shortcuts(p.read_text(encoding="utf-8"))
    except (OSError, UnicodeDecodeError):
        return {}


def expand(
    workspace_dir: Path, user_id: UUID, text: str
) -> Tuple[str, Optional[str]]:
    """入口层展开:整条 `text` 精确命中某触发词 → 返回 (完整指令, 命中的触发词原文);
    未命中 → 返回 (text 原样, None)。空文本直接原样返回。

    调用点:渠道核心 `_run_channel_conversation` + 网页 `post_message`,共用此函数,
    保证任何入口打同一个触发词行为一致。
    """
    if not text or not text.strip():
        return text, None
    mapping = load_shortcuts(workspace_dir, user_id)
    if not mapping:
        return text, None
    prompt = mapping.get(_normalize(text))
    if prompt is None:
        return text, None
    return prompt, text.strip()
