import json
from sqlalchemy.orm import Session
from app.models.knowledge_graph import KGNode, KGEdge

TAG_EXTRACTION_PROMPT = """你是一个知识分类专家。从给定内容中提取标签。

要求：
1. 标签采用层级路径格式，如 "编程语言/Python"、"后端/框架/FastAPI"
2. 层级深度 1-4 层，避免过深
3. 每个内容提取 3-8 个标签
4. 标签应覆盖：主题、技术栈、领域、任务类型等维度

输出格式（JSON数组）：
[
  {"path": "编程语言/Python", "description": "Python编程语言相关"},
  {"path": "后端/框架/FastAPI", "description": "FastAPI框架相关"}
]

内容：
{content}
"""

TAG_RELATION_PROMPT = """分析以下标签之间的关系，输出 JSON 数组：

关系类型：
- parent_of: 父子关系（上级包含下级）
- related_to: 语义相关（但不是父子）
- synonym_of: 同义词

标签列表：
{tag_paths}

输出格式：
[
  {"source": "标签1", "target": "标签2", "relation": "related_to", "weight": 0.8},
  {"source": "标签1", "target": "标签3", "relation": "parent_of", "weight": 1.0}
]
"""


class TagService:
    def __init__(self, db: Session, llm_client):
        self.db = db
        self.llm_client = llm_client

    def extract_tags_from_content(self, content: str, user_id: str) -> list[dict]:
        """从内容中提取标签"""
        response = self.llm_client.chat.completions.create(
            model="gpt-4o",
            messages=[
                {"role": "system", "content": "你是一个知识分类专家。"},
                {"role": "user", "content": TAG_EXTRACTION_PROMPT.format(content=content)}
            ],
            response_format={"type": "json_object"}
        )
        result = json.loads(response.choices[0].message.content)
        return result.get("tags", [])

    def parse_tag_path(self, path: str) -> tuple[str, int, str | None]:
        """解析标签路径，返回 (short_name, level, parent_path)"""
        parts = path.strip("/").split("/")
        short_name = parts[-1]
        level = len(parts)
        parent_path = "/".join(parts[:-1]) if level > 1 else None
        return short_name, level, parent_path

    def get_or_create_tag_node(self, tag_info: dict, user_id: str) -> KGNode:
        """获取或创建标签节点"""
        path = tag_info["path"]
        existing = self.db.query(KGNode).filter(
            KGNode.user_id == user_id,
            KGNode.properties_["tag_path"].astext == path
        ).first()

        if existing:
            return existing

        short_name, level, parent_path = self.parse_tag_path(path)

        node = KGNode(
            user_id=user_id,
            name=short_name,
            entity_type="tag",
            description=tag_info.get("description"),
            properties_={
                "tag_path": path,
                "short_name": short_name,
                "level": level,
                "parent_path": parent_path,
                "description": tag_info.get("description"),
                "color": tag_info.get("color"),
            },
            importance=0.5
        )
        self.db.add(node)
        self.db.flush()
        return node

    def ensure_parent_tags(self, path: str, user_id: str) -> list[KGNode]:
        """确保父路径标签存在"""
        parts = path.strip("/").split("/")
        nodes = []
        for i in range(1, len(parts)):
            parent_path = "/".join(parts[:i])
            tag_info = {"path": parent_path, "description": None}
            node = self.get_or_create_tag_node(tag_info, user_id)
            nodes.append(node)
        return nodes

    def create_tag_relations(self, tag_paths: list[str], user_id: str) -> list[KGEdge]:
        """分析并创建标签之间的关系边"""
        path_to_node = {}
        for path in tag_paths:
            node = self.db.query(KGNode).filter(
                KGNode.user_id == user_id,
                KGNode.properties_["tag_path"].astext == path,
                KGNode.entity_type == "tag"
            ).first()
            if node:
                path_to_node[path] = node

        response = self.llm_client.chat.completions.create(
            model="gpt-4o",
            messages=[
                {"role": "system", "content": "你是一个知识图谱专家。"},
                {"role": "user", "content": TAG_RELATION_PROMPT.format(tag_paths=json.dumps(tag_paths))}
            ],
            response_format={"type": "json_object"}
        )
        result = json.loads(response.choices[0].message.content)
        relations = result.get("relations", [])

        edges = []
        for rel in relations:
            source_node = path_to_node.get(rel["source"])
            target_node = path_to_node.get(rel["target"])
            if source_node and target_node:
                existing = self.db.query(KGEdge).filter(
                    KGEdge.source_id == source_node.id,
                    KGEdge.target_id == target_node.id
                ).first()
                if not existing:
                    edge = KGEdge(
                        source_id=source_node.id,
                        target_id=target_node.id,
                        relation_type=rel["relation"],
                        weight=rel.get("weight", 0.5)
                    )
                    self.db.add(edge)
                    edges.append(edge)

        self.db.flush()
        return edges

    def tag_content(self, content: str, user_id: str, content_node: KGNode) -> list[KGNode]:
        """为内容节点打标签"""
        tag_infos = self.extract_tags_from_content(content, user_id)
        tag_paths = [t["path"] for t in tag_infos]

        tag_nodes = []
        for tag_info in tag_infos:
            node = self.get_or_create_tag_node(tag_info, user_id)
            tag_nodes.append(node)
            self.ensure_parent_tags(tag_info["path"], user_id)

        # 创建 has_tag 边
        for tag_node in tag_nodes:
            existing_edge = self.db.query(KGEdge).filter(
                KGEdge.source_id == content_node.id,
                KGEdge.target_id == tag_node.id,
                KGEdge.relation_type == "has_tag"
            ).first()
            if not existing_edge:
                edge = KGEdge(
                    source_id=content_node.id,
                    target_id=tag_node.id,
                    relation_type="has_tag",
                    weight=1.0
                )
                self.db.add(edge)

        tag_node_ids = [n.id for n in tag_nodes]
        current_tag_ids = content_node.properties_.get("tag_node_ids", []) if content_node.properties_ else []
        content_node.properties_["tag_node_ids"] = list(set(current_tag_ids + tag_node_ids))

        if len(tag_paths) >= 2:
            self.create_tag_relations(tag_paths, user_id)

        self.db.commit()
        return tag_nodes

    def tag_incremental_content(self, user_id: str, days: int = 1) -> dict:
        """
        增量打标签 - 只对最近新增/更新的内容节点打标签
        """
        from datetime import UTC, datetime, timedelta

        cutoff_date = datetime.now(UTC) - timedelta(days=days)

        content_nodes = self.db.query(KGNode).filter(
            KGNode.user_id == user_id,
            KGNode.entity_type.in_(["conversation", "document", "chunk"]),
            KGNode.updated_at >= cutoff_date
        ).all()

        untagged = [
            n for n in content_nodes
            if not n.properties_.get("tag_node_ids")
        ]

        tagged_count = 0
        for node in untagged:
            content = node.description or ""
            try:
                self.tag_content(content, user_id, node)
                tagged_count += 1
            except Exception as e:
                pass

        return {"total": len(untagged), "tagged": tagged_count}

    def get_related_content(self, tag_node_ids: list[str], user_id: str, limit: int = 10) -> list[tuple[KGNode, float]]:
        """通过标签找相关内容"""
        edges = self.db.query(KGEdge).filter(
            KGEdge.target_id.in_(tag_node_ids),
            KGEdge.relation_type == "has_tag"
        ).all()

        content_weights: dict[str, float] = {}
        for edge in edges:
            content_weights[edge.source_id] = content_weights.get(edge.source_id, 0) + edge.weight

        content_ids = list(content_weights.keys())
        content_nodes = self.db.query(KGNode).filter(
            KGNode.id.in_(content_ids),
            KGNode.entity_type.in_(["conversation", "document", "chunk"])
        ).all()

        return [(node, content_weights[node.id]) for node in content_nodes]
