"""Background task logic extracted as pure async functions.

Each function accepts protocol-based stores and plain data -- no DB
sessions, no framework imports.  The application layer is responsible
for calling these inside its own transaction/session scope.

No sqlalchemy / fastapi / redis imports allowed in this file.
"""
from __future__ import annotations

import json
import logging
import re
from typing import Any, Callable, Awaitable

from kokoro_chat.attitude.state import AttitudeState
from kokoro_chat.storage.protocol import (
    ChatMessage,
    MemoStore,
    MemoryVectorStore,
)

logger = logging.getLogger(__name__)


# ---------------------------------------------------------------------------
# Lightweight pre-filter for _extract_user_info
# ---------------------------------------------------------------------------

PERSONAL_INFO_HINTS = [
    # name
    "我叫", "我的名字", "名字是", "my name", "i'm called",
    # location
    "我住", "住在", "搬到", "i live", "moved to",
    # pets
    "我養了", "我有一隻", "我的狗", "我的貓", "my pet", "my dog", "my cat",
    # age / birthday
    "歲", "生日", "birthday", "years old",
    # food
    "喜歡吃", "愛吃", "討厭吃", "不吃", "吃素", "過敏",
    # work
    "工作是", "我做", "上班", "公司", "職業", "my job", "i work",
    # hobbies
    "喜歡", "興趣", "愛好", "hobby", "i like", "i love", "i enjoy",
    # family
    "老婆", "老公", "女朋友", "男朋友", "爸", "媽", "兒子", "女兒",
    "哥", "姐", "弟", "妹", "家人", "family", "wife", "husband",
    # mood
    "壓力", "心情", "難過", "開心", "焦慮", "stress", "happy", "sad",
    # recent events
    "最近", "剛剛", "今天", "昨天", "recently", "just",
    # dislikes
    "討厭", "不喜歡", "受不了", "hate", "don't like",
    # media
    "聽", "玩", "看", "追劇",
]


def may_contain_personal_info(text: str) -> bool:
    """Return True when *text* contains at least one personal-info hint."""
    lower = text.lower()
    return any(hint in lower for hint in PERSONAL_INFO_HINTS)


# -- App-specific markup stripper ---------------------------------------------

_APP_MARKUP_RE = re.compile(
    r'<memo_user>.*?</memo_user>'
    r'|<memo_global>.*?</memo_global>'
    r'|<think>.*?</think>',
    re.DOTALL,
)


def strip_app_markup(text: str) -> str:
    """Remove app-specific XML tags from LLM output."""
    return _APP_MARKUP_RE.sub('', text).strip()


# -- Summarization (pure function) -------------------------------------------

# Type alias for the LLM call abstraction
LlmChatFn = Callable[
    [list[dict[str, str]], str, int],  # messages, model, max_tokens
    Awaitable[Any],  # returns object with .content and .usage
]


async def build_summarize_prompt(
    character_name: str,
    messages: list[ChatMessage],
    existing_summary: str,
) -> str:
    """Build the summarization prompt from messages.

    Takes the older messages (excluding the most recent 10) and formats
    them for compression.
    """
    if len(messages) < 10:
        return ""

    to_compress = messages[:-10]
    dialogue_text = "\n".join(
        f"{'用戶' if m.role == 'user' else character_name}：{m.content}"
        for m in to_compress[-20:]
    )

    return f"""請將以下對話歷史壓縮成一段簡潔的摘要（200字以內）。
保留重要的：事件、承諾、情感變化、關係進展、用戶提到的個人資訊。

{"之前的摘要：" + existing_summary if existing_summary else ""}

需要壓縮的對話：
{dialogue_text}

只輸出摘要，不要其他內容。"""


# -- User info extraction (pure function) ------------------------------------

def build_extract_user_info_prompt(
    user_msg: str,
    assistant_msg: str,
    character_name: str,
) -> str:
    """Build the prompt for extracting user personal information."""
    return f"""分析以下對話，提取用戶透露的個人資訊。

目標資訊類型（請用以下 key）：
基本資料：user_name, user_age, user_birthday, user_occupation, user_location
喜好：likes_food, dislikes_food, likes_music, likes_game, hobbies
重要他人：has_pet（格式：「名字/種類」）, family_members, relationship_status
當前狀態（有時效性）：recent_event（最近發生的大事）, current_stress（壓力來源）, current_mood（當前情緒）

用戶：{user_msg}
{character_name}：{assistant_msg}

如果有發現個人資訊，請以 JSON 陣列回覆，例如：
[{{"key": "user_name", "value": "Wade"}}, {{"key": "has_pet", "value": "Mochi/貓"}}]

如果沒有發現任何個人資訊，回覆空陣列：[]

只輸出 JSON，不要其他內容。"""


def parse_extracted_user_info(raw_text: str) -> list[dict[str, str]]:
    """Parse the LLM response for user info extraction.

    Returns a list of {key, value} dicts.  Returns empty list on
    parse failure.
    """
    try:
        clean = re.sub(r"```(?:json)?\s*|\s*```", "", raw_text).strip()
        items = json.loads(clean)
        if not isinstance(items, list):
            return []
        return [
            {"key": item["key"].strip(), "value": item["value"].strip()}
            for item in items
            if isinstance(item, dict)
            and item.get("key", "").strip()
            and item.get("value", "").strip()
        ]
    except (json.JSONDecodeError, KeyError, TypeError):
        return []


# -- Memory processing (pure function) ---------------------------------------

def build_process_memory_prompt(
    user_msg: str,
    assistant_msg: str,
    character_name: str,
) -> str:
    """Build the prompt for creating a memory summary."""
    return f"""請摘要以下對話的重點（不超過 50 字），並評估這段記憶的重要度（0.0-1.0）。
重要的記憶包含：個人資訊、重要事件、情感轉折、承諾、秘密。
普通的日常寒暄重要度較低。

用戶：{user_msg}
{character_name}：{assistant_msg}

請以 JSON 回覆，不要輸出其他內容：
{{"summary": "...", "importance": 0.0-1.0}}"""


def parse_memory_summary(raw_text: str) -> tuple[str, float]:
    """Parse the LLM response for memory summarization.

    Returns (summary_text, importance).  Falls back to
    (raw_text, 0.5) on parse failure.
    """
    try:
        clean = re.sub(r"```(?:json)?\s*|\s*```", "", raw_text).strip()
        parsed = json.loads(clean)
        summary = parsed["summary"]
        importance = max(0.0, min(1.0, float(parsed["importance"])))
        return summary, importance
    except (json.JSONDecodeError, KeyError, ValueError):
        return raw_text, 0.5


# -- Event analysis (pure function) ------------------------------------------

def build_event_analysis_prompt(
    character_name: str,
    personality: str,
    affection: int,
    trust: int,
    mood: str,
    event_description: str,
) -> str:
    """Build the prompt for analysing an event's attitude impact."""
    return f"""你是一個角色情感分析器。

角色「{character_name}」的性格：{personality}
當前好感度：{affection}/100
當前信任度：{trust}/100
當前心情：{mood}

發生了以下事件：
{event_description}

請分析這個事件對角色態度的影響，以 JSON 格式回覆：
{{
    "affection_delta": 數字（-30 到 +30），
    "trust_delta": 數字（-30 到 +30），
    "new_mood": "心情描述（一兩個詞）",
    "attitude_shift": "態度變化描述（一句話，描述角色接下來會怎麼對用戶）",
    "duration": 數字（影響持續幾輪對話，1-10）
}}

只輸出 JSON，不要其他內容。"""


def parse_event_impact(raw_text: str) -> dict[str, Any] | None:
    """Parse the LLM response for event impact analysis.

    Returns parsed dict or None on failure.
    """
    if not raw_text:
        return None
    try:
        return json.loads(raw_text)
    except json.JSONDecodeError:
        match = re.search(
            r'\{[^{}]*"affection_delta"[^{}]*\}', raw_text, re.DOTALL,
        )
        if match:
            try:
                return json.loads(match.group())
            except json.JSONDecodeError:
                pass
    return None
