"""Memory ↔ Wiki bridge.

Bidirectional sync between the memory system and the knowledge base wiki.

Memory → Wiki: conversation insights, decisions, and facts that relate to
wiki entities/concepts get filed as wiki updates.

Wiki → Memory: key synthesis insights and structural decisions get distilled
into memory-friendly format for MEMORY.md.
"""

from __future__ import annotations

import logging
import re
from datetime import UTC, datetime
from typing import TYPE_CHECKING

from agntspace.infra.timezone import local_today

if TYPE_CHECKING:
    from agntspace.kb.service import KnowledgeBaseService
    from agntspace.llm.base import LLMProvider
    from agntspace.vault.reader import VaultReader
    from agntspace.vault.writer import VaultWriter

log = logging.getLogger(__name__)


class MemoryWikiBridge:
    """Bridges the memory system and the wiki knowledge base."""

    def __init__(
        self,
        llm: LLMProvider,
        vault_reader: VaultReader,
        vault_writer: VaultWriter,
        kb_service: KnowledgeBaseService,
    ) -> None:
        self._llm = llm
        self._reader = vault_reader
        self._writer = vault_writer
        self._kb = kb_service
        self._activity: object | None = None  # injected post-hoc
        self._decisions: object | None = None  # injected post-hoc
        self._contract: object | None = None  # injected post-hoc

    async def memory_to_wiki(self, date: str | None = None) -> dict:
        """Push relevant memory facts into wiki articles.

        Reads the daily memory file, extracts facts that mention wiki entities,
        and appends them as updates to the corresponding wiki articles.

        Wrapped in ``scoped_operation`` (Rule #17 Tier B) so every wiki
        append fires inside the same ``memory-bridge-*`` correlation chain.
        """
        if not date:
            date = local_today()

        year, month = date.split("-")[:2]
        memory_path = f"memory/{year}/{month}/memory_{date}.md"

        if not self._reader.exists(memory_path):
            return {"date": date, "status": "no_memory_file"}

        from agntspace.activity.scoped import scoped_operation

        async with scoped_operation(
            writer=self._writer,
            contract=self._contract,
            decisions=self._decisions,
            activity=self._activity,
            actor="main",
            action_type="memory_to_wiki",
            action_target=memory_path,
            reason="memory_bridge",
            scope_prefix="memory-bridge",
            rationale=f"Memory→Wiki bridge for {date}",
            caller="daily_cycle",
            skill_context=["memory-consolidate"],
        ) as _scope:
            return await self._do_memory_to_wiki(date, memory_path)

    async def _do_memory_to_wiki(self, date: str, memory_path: str) -> dict:
        """Internal memory→wiki bridge body — caller owns the scope."""
        if not self._reader.exists(memory_path):
            return {"date": date, "status": "no_memory_file"}

        doc = self._reader.read(memory_path)
        content = doc.content

        # Find all [[wiki-links]] in the memory
        wiki_refs = re.findall(r"\[\[([^\]]+)\]\]", content)
        if not wiki_refs:
            return {"date": date, "status": "no_wiki_references", "refs": 0}

        # For each referenced entity, extract the relevant sentences
        updates_filed = 0
        kbs = self._kb.list_kbs()

        for ref in set(wiki_refs):
            slug = ref.lower().replace(" ", "-").split("|")[0]

            # Find sentences mentioning this entity
            sentences = []
            for line in content.split("\n"):
                if f"[[{ref}]]" in line or slug in line.lower():
                    clean = line.strip().lstrip("- ")
                    if clean and len(clean) > 20:
                        sentences.append(clean)

            if not sentences:
                continue

            # Find the article across KBs
            for kb in kbs:
                kb_slug = kb["slug"]
                article_path = None
                for subdir in ("entities", "concepts", "synthesis"):
                    path = f"knowledge/{kb_slug}/wiki/{subdir}/{slug}.md"
                    if self._reader.exists(path):
                        article_path = path
                        break
                # Also check projects
                if not article_path:
                    try:
                        projects_dir = self._reader._resolve(f"knowledge/{kb_slug}/wiki/projects")
                        if projects_dir.is_dir():
                            for proj in projects_dir.iterdir():
                                if proj.is_dir():
                                    for subdir in ("entities", "concepts"):
                                        path = f"knowledge/{kb_slug}/wiki/projects/{proj.name}/{subdir}/{slug}.md"
                                        if self._reader.exists(path):
                                            article_path = path
                                            break
                    except Exception:
                        pass

                if article_path:
                    update_text = (
                        f"\n\n---\n\n"
                        f"> **From memory ({date}):**\n>\n"
                        + "\n".join(f"> - {s}" for s in sentences[:5])
                        + "\n"
                    )
                    self._writer.append(article_path, update_text)
                    updates_filed += 1
                    log.info("Memory→Wiki: updated %s with %d facts from %s", slug, len(sentences), date)
                    break

        return {"date": date, "refs": len(wiki_refs), "updates_filed": updates_filed}

    async def wiki_to_memory(self) -> dict:
        """Distill wiki insights into memory-friendly format.

        Reads recent synthesis pages and decision records, extracts key facts,
        and appends them as pending memory updates.

        Wrapped in ``scoped_operation`` (Rule #17 Tier B).
        """
        from agntspace.activity.scoped import scoped_operation

        _pending_target = f"memory/pending/wiki-insights_{datetime.now(UTC).strftime('%Y-%m-%d')}.md"
        async with scoped_operation(
            writer=self._writer,
            contract=self._contract,
            decisions=self._decisions,
            activity=self._activity,
            actor="wiki-supervisor",
            action_type="wiki_to_memory",
            action_target=_pending_target,
            reason="memory_bridge",
            scope_prefix="wiki-to-memory",
            rationale="Distill wiki insights into pending memory facts",
            caller="daily_cycle",
            skill_context=["memory-consolidate"],
        ) as _scope:
            return await self._do_wiki_to_memory()

    async def _do_wiki_to_memory(self) -> dict:
        """Internal wiki→memory body — caller owns the scope."""
        from agntspace.llm.base import Message

        # Gather recent synthesis and decision pages across all KBs
        insights = []
        kbs = self._kb.list_kbs()

        for kb in kbs:
            kb_slug = kb["slug"]
            # Recent synthesis
            for subdir in ("synthesis", "decisions"):
                try:
                    for f in self._reader.list_files(f"knowledge/{kb_slug}/wiki/{subdir}", "*.md"):
                        doc = self._reader.read(f"knowledge/{kb_slug}/wiki/{subdir}/{f.name}")
                        # Only include recent (check date in frontmatter)
                        created = doc.metadata.get("date_created", doc.metadata.get("date", ""))
                        if created:
                            insights.append(f"[{subdir}/{f.stem}] {doc.content[:500]}")
                except Exception:
                    continue

        if not insights:
            return {"status": "no_insights", "count": 0}

        # LLM distills into memory-ready facts
        try:
            response = await self._llm.complete(
                messages=[Message(role="user", content=(
                    f"Distill these {len(insights)} wiki insights into concise memory facts.\n\n"
                    + "\n\n".join(insights[:10])
                ))],
                system=(
                    "You are a memory consolidation assistant. Read wiki synthesis and decision pages "
                    "and extract the KEY FACTS that should be in the agent's long-term memory.\n\n"
                    "Output format:\n"
                    "```pending_memory\n"
                    "- Add: [concise fact from wiki] [Source: wiki/synthesis/slug]\n"
                    "- Add: [another fact] [Source: wiki/decisions/slug]\n"
                    "```\n\n"
                    "Only include genuinely important facts — skip trivial details. "
                    "Max 5 items. If nothing is memory-worthy, output: no updates."
                ),
                max_tokens=500,
                temperature=0.2,
            )
        except Exception as exc:
            log.warning("Memory bridge: LLM failed: %s — will retry on next cycle", exc)
            return {"status": "llm_unavailable", "error": str(exc), "count": 0}

        # Write as pending memory (user approves before MEMORY.md)
        pending_text = response.content
        if "no updates" in pending_text.lower():
            return {"status": "no_updates", "count": 0}

        date = local_today()
        pending_path = f"memory/pending/wiki-insights_{date}.md"
        self._writer.write(pending_path, (
            f"---\ntitle: \"Wiki Insights — {date}\"\ntype: pending_memory\n"
            f"source: wiki_synthesis\nauthor: agent\nagent: wiki-supervisor\n---\n\n"
            f"{pending_text}"
        ))

        # Count items
        items = pending_text.count("- Add:")
        log.info("Wiki→Memory: %d pending facts from wiki insights", items)

        return {"status": "pending", "count": items, "path": pending_path}
