# Familiar - Self-Hosted AI Companion Platform
# Copyright (c) 2026 George Scott Foley
# ORCID: 0009-0006-4957-0540
# Email: Georgescottfoley@proton.me
# Licensed under the MIT License - see LICENSE file for details

"""
Markdown Memories Skill

Wiki-style markdown memories with YAML frontmatter and provenance
tracking.  Six tool handlers following the knowledge_base pattern.

Dependencies: pyyaml (base dep)
"""

import hashlib
import json
import logging
from datetime import datetime, timezone
from pathlib import Path

logger = logging.getLogger(__name__)


def _get_data_dir():
    """Get data directory (tenant-scoped in Reflection, default in standalone)."""
    try:
        from familiar.core import paths

        return paths.DATA_DIR
    except ImportError:
        return Path.home() / ".familiar" / "data"


def _get_store(user_id: str = "default"):
    """Create a MarkdownMemoryStore for the given user."""
    from familiar.core.markdown_memory import MarkdownMemoryStore

    try:
        from familiar.core.paths import MEMORIES_DIR
    except ImportError:
        MEMORIES_DIR = Path.home() / ".familiar" / "data" / "memories"

    return MarkdownMemoryStore(MEMORIES_DIR, user_id)


# === Tool Handlers ===


def save_memory(data):
    """Save or append content to a markdown memory topic."""
    topic = data.get("topic", "").strip()
    content = data.get("content", "").strip()

    if not topic:
        return "Please provide a topic name."
    if not content:
        return "Please provide content to save."

    tags = data.get("tags", [])
    if isinstance(tags, str):
        tags = [t.strip() for t in tags.split(",") if t.strip()]

    heading = data.get("heading", "").strip() or None
    aliases = data.get("aliases", [])
    if isinstance(aliases, str):
        aliases = [a.strip() for a in aliases.split(",") if a.strip()]

    user_id = data.get("_user_id", "default")
    store = _get_store(user_id)

    slug, revision = store.save_memory(
        topic=topic,
        content=content,
        tags=tags,
        heading=heading,
        aliases=aliases,
    )

    tag_str = f" [{', '.join(tags)}]" if tags else ""
    return f"Saved to memory: {slug} (revision {revision}){tag_str}"


def read_memory(data):
    """Read the full content of a markdown memory topic."""
    topic = data.get("topic", "").strip()
    if not topic:
        return "Please provide a topic name."

    user_id = data.get("_user_id", "default")
    store = _get_store(user_id)
    result = store.read_memory(topic)

    if result is None:
        return f"Memory topic not found: {topic}"

    meta, body = result
    if meta is None:
        return body

    lines = [f"# {meta.topic}"]
    lines.append(f"Tags: {', '.join(meta.tags) if meta.tags else 'none'}")
    lines.append(f"Revision: {meta.revision}")
    lines.append(f"Updated: {meta.updated}")
    if meta.aliases:
        lines.append(f"Aliases: {', '.join(meta.aliases)}")
    lines.append("")
    lines.append(body)

    return "\n".join(lines)


def search_memories(data):
    """Search across markdown memory topics by keyword."""
    query = data.get("query", "").strip()
    if not query:
        return "Please provide a search query."

    tags = data.get("tags", [])
    if isinstance(tags, str):
        tags = [t.strip() for t in tags.split(",") if t.strip()]

    limit = min(data.get("limit", 10), 50)
    user_id = data.get("_user_id", "default")
    store = _get_store(user_id)

    results = store.search(query, limit=limit)

    if tags:
        # Post-filter by tags
        filtered = []
        for slug, score, preview in results:
            result = store.read_memory(slug)
            if result and result[0]:
                if any(t in result[0].tags for t in tags):
                    filtered.append((slug, score, preview))
        results = filtered

    if not results:
        return f"No memories found matching: {query}"

    lines = [f"Found {len(results)} memory topic(s):\n"]
    for slug, score, preview in results:
        lines.append(f"  {slug} (score: {score:.1f})")
        lines.append(f"    {preview[:120]}")
    return "\n".join(lines)


def list_memories(data):
    """List all markdown memory topics."""
    tags = data.get("tags", [])
    if isinstance(tags, str):
        tags = [t.strip() for t in tags.split(",") if t.strip()]

    sort_by = data.get("sort", "recent")
    limit = min(data.get("limit", 30), 100)
    user_id = data.get("_user_id", "default")
    store = _get_store(user_id)

    topics = store.list_topics(tags=tags or None, limit=limit)

    if not topics:
        return "No memory topics found."

    if sort_by == "alpha":
        topics.sort(key=lambda t: t["topic"])
    elif sort_by == "revision":
        topics.sort(key=lambda t: t["revision"], reverse=True)
    # default "recent" is already sorted by mtime from list_topics

    lines = [f"Memory topics ({len(topics)}):\n"]
    for t in topics:
        tag_str = f" [{', '.join(t['tags'])}]" if t["tags"] else ""
        lines.append(f"  {t['topic']} (rev {t['revision']}){tag_str}")
        lines.append(f"    Updated: {t['updated']}")
    return "\n".join(lines)


def delete_memory(data):
    """Delete a markdown memory topic."""
    topic = data.get("topic", "").strip()
    if not topic:
        return "Please provide a topic name to delete."

    user_id = data.get("_user_id", "default")
    store = _get_store(user_id)

    if store.delete_memory(topic):
        return f"Deleted memory topic: {topic}"
    return f"Memory topic not found: {topic}"


def export_memories_for_training(data):
    """Export markdown memories as training data with provenance."""
    min_quality = data.get("min_quality", 0.5)
    user_id = data.get("_user_id", "default")
    store = _get_store(user_id)

    records = store.export_for_training(min_revision=0)

    if not records:
        return "No memory topics to export."

    # Filter by quality score
    records = [r for r in records if r.get("quality_score", 0) >= min_quality]

    if not records:
        return f"No records meet quality threshold {min_quality}."

    # Write to curated directory
    curated_dir = _get_data_dir() / "training" / "curated"
    curated_dir.mkdir(parents=True, exist_ok=True)
    output_path = curated_dir / "memories.jsonl"

    with open(output_path, "w", encoding="utf-8") as f:
        for rec in records:
            f.write(json.dumps(rec, ensure_ascii=False) + "\n")

    # Compute export fingerprint
    content = output_path.read_bytes()
    fingerprint = hashlib.sha256(content).hexdigest()

    # Anchor export to genesis chain
    try:
        from familiar.skills.training.bridge import anchor_export

        anchor_export(
            {
                "action": "export_memories",
                "total_records": len(records),
                "min_quality": min_quality,
                "fingerprint": fingerprint,
                "exported_at": datetime.now(timezone.utc).isoformat(),
            }
        )
    except Exception:
        logger.debug("Chain anchor skipped for memories export", exc_info=True)

    return (
        f"Exported {len(records)} memory sections to {output_path.name}\n"
        f"Quality threshold: {min_quality}\n"
        f"Fingerprint: {fingerprint[:16]}..."
    )


# === Tool Definitions ===

TOOLS = [
    {
        "name": "save_memory",
        "description": (
            "Save or append content to a wiki-style markdown memory topic. "
            "Each save appends a new section with a timestamp. Topics accumulate "
            "over time and are anchored to the genesis provenance chain. "
            "Use this for long-lived knowledge that grows over time."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "topic": {
                    "type": "string",
                    "description": "Topic name (will be slugified for the filename)",
                },
                "content": {
                    "type": "string",
                    "description": "Markdown content to append",
                },
                "tags": {
                    "type": "array",
                    "items": {"type": "string"},
                    "description": "Tags for organization",
                },
                "heading": {
                    "type": "string",
                    "description": "Section heading (defaults to topic title)",
                },
                "aliases": {
                    "type": "array",
                    "items": {"type": "string"},
                    "description": "Alternate names for this topic",
                },
            },
            "required": ["topic", "content"],
        },
        "handler": save_memory,
        "category": "memories",
    },
    {
        "name": "read_memory",
        "description": (
            "Read the full content of a markdown memory topic, including all "
            "accumulated sections, tags, revision count, and metadata."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "topic": {
                    "type": "string",
                    "description": "Topic name or alias",
                },
            },
            "required": ["topic"],
        },
        "handler": read_memory,
        "category": "memories",
    },
    {
        "name": "search_memories",
        "description": (
            "Search across all markdown memory topics by keyword. "
            "Returns topics ranked by relevance with preview snippets. "
            "Supports optional tag filtering."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "Search terms",
                },
                "tags": {
                    "type": "array",
                    "items": {"type": "string"},
                    "description": "Filter results by tags",
                },
                "limit": {
                    "type": "integer",
                    "default": 10,
                    "description": "Maximum results to return",
                },
            },
            "required": ["query"],
        },
        "handler": search_memories,
        "category": "memories",
    },
    {
        "name": "list_memories",
        "description": (
            "List all markdown memory topics with metadata. "
            "Supports filtering by tags and sorting by recency, "
            "alphabetical order, or revision count."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "tags": {
                    "type": "array",
                    "items": {"type": "string"},
                    "description": "Filter by tags",
                },
                "sort": {
                    "type": "string",
                    "enum": ["recent", "alpha", "revision"],
                    "default": "recent",
                    "description": "Sort order",
                },
                "limit": {
                    "type": "integer",
                    "default": 30,
                    "description": "Maximum topics to return",
                },
            },
        },
        "handler": list_memories,
        "category": "memories",
    },
    {
        "name": "delete_memory",
        "description": (
            "Delete a markdown memory topic permanently. "
            "This removes the file and all accumulated sections."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "topic": {
                    "type": "string",
                    "description": "Topic name or alias to delete",
                },
            },
            "required": ["topic"],
        },
        "handler": delete_memory,
        "category": "memories",
    },
    {
        "name": "export_memories_for_training",
        "description": (
            "Export markdown memories as structured training data with "
            "provenance metadata. Writes to training/curated/memories.jsonl "
            "and anchors the export to the genesis block chain."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "min_quality": {
                    "type": "number",
                    "default": 0.5,
                    "description": "Minimum quality score threshold",
                },
                "format": {
                    "type": "string",
                    "enum": ["jsonl"],
                    "default": "jsonl",
                    "description": "Export format",
                },
            },
        },
        "handler": export_memories_for_training,
        "category": "memories",
    },
]
