"""v1 tag 页 → pages.json v2 · graph 初始边。"""

from __future__ import annotations

import re
from datetime import datetime, timezone
from typing import Any
from uuid import uuid4


PAGES_VERSION = 2
GRAPH_VERSION = 1


def _now_iso() -> str:
    return datetime.now(timezone.utc).isoformat()


def normalize_entity_id(title: str) -> str:
    raw = str(title or "").strip()
    if raw.startswith("ent:"):
        return raw
    collapsed = re.sub(r"\s+", " ", raw)
    return f"ent:{collapsed}"


def is_v2_pages_document(raw: dict[str, Any]) -> bool:
    return isinstance(raw, dict) and raw.get("version") == PAGES_VERSION and isinstance(raw.get("pages"), dict)


def is_v1_pages_flat(raw: dict[str, Any]) -> bool:
    if not raw or is_v2_pages_document(raw):
        return False
    for key, val in raw.items():
        if key == "version":
            continue
        if isinstance(val, dict) and ("concept" in val or "summary" in val or "atom_ids" in val):
            return True
    return bool(raw)


def page_from_v1(key: str, page: dict[str, Any], *, namespace: str = "subject") -> dict[str, Any]:
    title = str(page.get("title") or page.get("concept") or key)
    page_id = normalize_entity_id(key if key.startswith("ent:") else title)
    links = [normalize_entity_id(x) for x in (page.get("links") or [])]
    return {
        "id": page_id,
        "title": title,
        "namespace": namespace,
        "summary": str(page.get("summary") or "")[:400],
        "body_md": page.get("body_md"),
        "atom_ids": list(page.get("atom_ids") or [])[-20:],
        "links": links,
        "meta": {
            "entity_type": "concept",
            "tier": "semantic",
            "confidence": float(page.get("confidence") or 0.6),
            "stale": False,
            "updated_at": _now_iso(),
            "provenance": "migrate_v1",
        },
    }


def migrate_pages_document(raw: dict[str, Any], *, namespace: str = "subject") -> dict[str, Any]:
    if is_v2_pages_document(raw):
        return raw
    pages: dict[str, Any] = {}
    if is_v1_pages_flat(raw):
        for key, page in raw.items():
            if not isinstance(page, dict):
                continue
            v2 = page_from_v1(str(key), page, namespace=namespace)
            pages[v2["id"]] = v2
    return {"version": PAGES_VERSION, "pages": pages}


def graph_node_from_page(page: dict[str, Any]) -> dict[str, Any]:
    page_id = str(page.get("id") or "")
    meta = page.get("meta") if isinstance(page.get("meta"), dict) else {}
    return {
        "page_id": page_id,
        "label": page.get("title") or page_id,
        "namespace": page.get("namespace") or "subject",
        "entity_type": meta.get("entity_type") or "concept",
        "degree": len(page.get("links") or []),
        "confidence": float(meta.get("confidence") or 0.6),
    }


def edges_from_page_links(page: dict[str, Any]) -> list[dict[str, Any]]:
    page_id = str(page.get("id") or "")
    edges: list[dict[str, Any]] = []
    atom_ids = list(page.get("atom_ids") or [])
    for target in page.get("links") or []:
        tid = normalize_entity_id(str(target))
        if tid == page_id:
            continue
        edges.append(
            {
                "id": f"e_{uuid4().hex[:8]}",
                "from": page_id,
                "to": tid,
                "type": "related-to",
                "weight": 0.6,
                "source_atom_ids": atom_ids[:5],
            }
        )
    return edges


def migrate_graph_from_pages(pages_doc: dict[str, Any]) -> dict[str, Any]:
    pages = pages_doc.get("pages") or {}
    nodes: dict[str, Any] = {}
    edges: list[dict[str, Any]] = []
    seen: set[tuple[str, str]] = set()
    for page in pages.values():
        if not isinstance(page, dict):
            continue
        pid = str(page.get("id") or "")
        if not pid:
            continue
        nodes[pid] = graph_node_from_page(page)
        for edge in edges_from_page_links(page):
            key = (edge["from"], edge["to"])
            if key in seen:
                continue
            seen.add(key)
            edges.append(edge)
    return {"version": GRAPH_VERSION, "nodes": nodes, "edges": edges}
