"""
用户记忆服务：对话偏好提取
记忆文件只存储用户偏好段，基本信息实时从 users 表读取
"""
import logging
import os
from pathlib import Path

import httpx

logger = logging.getLogger(__name__)


def _get_memories_dir() -> Path:
    base = os.getenv("MEMORIES_DIR", "")
    if base:
        return Path(base)
    return Path("./data/memories")


def _build_initial_memory_content() -> str:
    """构建初始用户记忆文件内容（空偏好段）"""
    return "## 用户偏好\n\n（暂无，将在对话中自动提取）"


def _parse_preferences(content: str) -> list[str]:
    """从记忆内容中解析出偏好列表"""
    prefs = []
    in_pref_section = False
    for line in content.split("\n"):
        if "## 用户偏好" in line:
            in_pref_section = True
            continue
        if in_pref_section and line.startswith("## "):
            break
        if in_pref_section and line.strip().startswith("- "):
            prefs.append(line.strip()[2:].strip())
    return prefs


def _rebuild_memory_content(preferences: list[str]) -> str:
    """重新组装记忆内容（只有偏好段）"""
    parts = ["## 用户偏好", ""]

    if preferences:
        for p in preferences[:10]:
            parts.append(f"- {p}")
    else:
        parts.append("（暂无）")

    return "\n".join(parts)


async def ensure_user_memory(user_id: str, db) -> None:
    """
    确保用户记忆存在：若 user_memories 表无记录，则创建空偏好记录。
    在用户首次对话时调用。
    """
    conn, cur = db
    try:
        # 检查是否已有记忆记录
        await cur.execute(
            "SELECT id FROM user_memories WHERE user_id = %s AND memory_type = 'user' AND is_archived = 0 LIMIT 1",
            (user_id,),
        )
        if await cur.fetchone():
            return  # 已有记录，无需创建

        content = _build_initial_memory_content()

        # 写入数据库
        await cur.execute(
            "INSERT INTO user_memories (user_id, memory_type, content, created_at, updated_at) "
            "VALUES (%s, 'user', %s, NOW(), NOW())",
            (user_id, content),
        )

        # 同时写入文件
        file_path = _get_memories_dir() / "users" / f"{user_id}.md"
        try:
            file_path.parent.mkdir(parents=True, exist_ok=True)
            file_path.write_text(content, encoding="utf-8")
        except OSError as e:
            logger.warning(f"Failed to write user memory file for {user_id}: {e}")

        logger.info(f"Created initial user memory for {user_id}")

    except Exception as e:
        logger.error(f"Failed to ensure user memory for {user_id}: {e}")


async def extract_and_update_preferences(
    user_id: str,
    conversation_content: str,
    db,
) -> None:
    """
    从对话内容中提取用户偏好，合并到已有记忆中。
    在对话摘要生成后异步调用。
    """
    if not conversation_content or len(conversation_content.strip()) < 50:
        return

    conn, cur = db
    try:
        # 读取当前记忆
        await cur.execute(
            "SELECT id, content FROM user_memories "
            "WHERE user_id = %s AND memory_type = 'user' AND is_archived = 0 "
            "ORDER BY updated_at DESC LIMIT 1",
            (user_id,),
        )
        row = await cur.fetchone()
        if not row:
            # 无记忆记录，先创建
            await ensure_user_memory(user_id, db)
            await cur.execute(
                "SELECT id, content FROM user_memories "
                "WHERE user_id = %s AND memory_type = 'user' AND is_archived = 0 "
                "ORDER BY updated_at DESC LIMIT 1",
                (user_id,),
            )
            row = await cur.fetchone()
            if not row:
                return

        r = dict(row)
        memory_id = r.get("id") or r[0]
        current_content = r.get("content") or r[1] or ""

        # 解析已有偏好
        existing_prefs = _parse_preferences(current_content)
        existing_prefs_text = "\n".join(f"- {p}" for p in existing_prefs) if existing_prefs else "（暂无）"

        # 调用 LLM 提取新偏好
        new_prefs = await _call_llm_extract_preferences(
            existing_prefs=existing_prefs_text,
            conversation_content=conversation_content[:3000],
        )

        if not new_prefs:
            return

        # 合并去重
        merged = existing_prefs.copy()
        for p in new_prefs:
            if p and p not in merged:
                merged.append(p)

        # 保留最近10条
        merged = merged[-10:]

        # 重建记忆内容（只有偏好段）
        new_content = _rebuild_memory_content(merged)

        # 更新数据库
        await cur.execute(
            "UPDATE user_memories SET content = %s, updated_at = NOW() WHERE id = %s",
            (new_content, memory_id),
        )

        # 更新文件
        file_path = _get_memories_dir() / "users" / f"{user_id}.md"
        try:
            file_path.parent.mkdir(parents=True, exist_ok=True)
            file_path.write_text(new_content, encoding="utf-8")
        except OSError:
            pass

        logger.info(f"Updated user preferences for {user_id}: +{len(new_prefs)} new, total {len(merged)}")

    except Exception as e:
        logger.error(f"Failed to extract preferences for {user_id}: {e}")


async def _call_llm_extract_preferences(
    existing_prefs: str,
    conversation_content: str,
) -> list[str]:
    """调用 LLM 从对话中提取用户偏好"""
    from ..core.model_resolver import get_scene_model_config, _decrypt_api_key

    try:
        scene_config = await get_scene_model_config("summary_compress")
        if not scene_config:
            logger.warning("No model config for summary_compress, skip preference extraction")
            return []

        api_key = _decrypt_api_key(scene_config["api_key"])
        api_base = scene_config["api_base"]
        model_name = scene_config["model_name"]

    except Exception as e:
        logger.warning(f"Failed to get model config for preference extraction: {e}")
        return []

    prompt = f"""从以下对话中提取用户的偏好和工作习惯。只提取与执法工作相关的偏好，忽略打招呼、闲聊、确认等无效信息。
输出格式：每条偏好一行，以"- "开头，不超过30字。如果没有有价值的偏好，输出"无"。

已有偏好：
{existing_prefs}

最近对话：
{conversation_content}

请输出新增的偏好（不重复已有偏好）："""

    try:
        async with httpx.AsyncClient(timeout=30.0) as client:
            resp = await client.post(
                f"{api_base}/chat/completions",
                headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
                json={
                    "model": model_name,
                    "messages": [{"role": "user", "content": prompt}],
                    "max_tokens": 256,
                    "temperature": 0.3,
                },
            )
            resp.raise_for_status()
            data = resp.json()
            text = data.get("choices", [{}])[0].get("message", {}).get("content", "").strip()

            if not text or text == "无":
                return []

            # 解析 "- xxx" 格式
            prefs = []
            for line in text.split("\n"):
                line = line.strip()
                if line.startswith("- "):
                    pref = line[2:].strip()
                    if pref and len(pref) <= 50:
                        prefs.append(pref)
                elif line and not line.startswith("#") and len(line) <= 50:
                    prefs.append(line)

            return prefs

    except Exception as e:
        logger.warning(f"LLM preference extraction failed: {e}")
        return []
