#!/usr/bin/env python3
"""
Yggdrasil Context Builder
===========================
Lightweight architectural knowledge query tool inspired by Yggdrasil.
Reads the .yggdrasil/ directory and builds a focused context package
for a given module/node.

Usage:
    python3 yg-context.py <node-name>        # Get context for a specific node
    python3 yg-context.py --list              # List all nodes
    python3 yg-context.py --overview          # Show system overview
    python3 yg-context.py --deps <node-name>  # Show dependency chain

Example:
    python3 yg-context.py auth-system
    # Returns: node description, constraints, dependencies, relevant aspects, related flows
"""

import os
import re
import sys
from pathlib import Path

# Auto-detect project root
SCRIPT_DIR = Path(__file__).resolve().parent
# Try common locations: script is in project root, or in a scripts/ subdirectory
for candidate in [SCRIPT_DIR, SCRIPT_DIR.parent]:
    if (candidate / ".yggdrasil").exists():
        PROJECT_DIR = candidate
        break
else:
    PROJECT_DIR = Path(os.environ.get("PROJECT_DIR", SCRIPT_DIR))

YG_DIR = PROJECT_DIR / ".yggdrasil"
NODES_DIR = YG_DIR / "nodes"
ASPECTS_DIR = YG_DIR / "aspects"
FLOWS_DIR = YG_DIR / "flows"


def parse_frontmatter(filepath):
    """Parse YAML-like frontmatter from a markdown file."""
    content = filepath.read_text(encoding="utf-8")
    meta = {}
    body = content

    if content.startswith("---"):
        parts = content.split("---", 2)
        if len(parts) >= 3:
            for line in parts[1].strip().split("\n"):
                if ":" in line:
                    key, val = line.split(":", 1)
                    meta[key.strip()] = val.strip().strip('"').strip("'")
            body = parts[2].strip()

    return meta, body


def find_node(name):
    """Find a node file by name (fuzzy match)."""
    if not NODES_DIR.exists():
        return None

    # Exact match first
    for f in NODES_DIR.glob("*.md"):
        meta, _ = parse_frontmatter(f)
        if meta.get("name") == name:
            return f
    # Filename match
    for f in NODES_DIR.glob("*.md"):
        if f.stem == name or f.stem.replace("-", "_") == name.replace("-", "_"):
            return f
    # Partial match
    for f in NODES_DIR.glob("*.md"):
        if name.lower() in f.stem.lower():
            return f
    return None


def get_all_nodes():
    """Return list of all node names."""
    if not NODES_DIR.exists():
        return []
    nodes = []
    for f in sorted(NODES_DIR.glob("*.md")):
        meta, _ = parse_frontmatter(f)
        name = meta.get("name", f.stem)
        status = meta.get("status", "active")
        nodes.append((name, status, f))
    return nodes


def get_relevant_aspects(node_name):
    """Find aspects that apply to a given node."""
    if not ASPECTS_DIR.exists():
        return []
    relevant = []
    for f in sorted(ASPECTS_DIR.glob("*.md")):
        meta, body = parse_frontmatter(f)
        applies_to = meta.get("applies_to", "")
        if applies_to == "all" or node_name in applies_to:
            relevant.append((meta.get("name", f.stem), body))
        # Also check body for explicit mentions
        elif node_name in body:
            relevant.append((meta.get("name", f.stem), body))
    return relevant


def get_relevant_flows(node_name):
    """Find flows that involve a given node."""
    if not FLOWS_DIR.exists():
        return []
    relevant = []
    for f in sorted(FLOWS_DIR.glob("*.md")):
        meta, body = parse_frontmatter(f)
        if node_name in body.lower():
            relevant.append((meta.get("name", f.stem), body))
    return relevant


def extract_dependencies(body):
    """Extract depends_on and depended_on_by from node body."""
    deps = {"depends_on": [], "depended_on_by": []}
    current_section = None

    for line in body.split("\n"):
        stripped = line.strip()
        if "depends_on:" in stripped.lower() or "## Dependencies" in stripped:
            current_section = "depends_on"
            continue
        elif "depended_on_by:" in stripped.lower() or "## Dependents" in stripped:
            current_section = "depended_on_by"
            continue
        elif stripped.startswith("## "):
            current_section = None
            continue

        if current_section and stripped.startswith("- ") and "none" not in stripped.lower():
            dep = stripped.lstrip("- ").strip().split(" ")[0].strip("`")
            if dep:
                deps[current_section].append(dep)

    return deps


def build_context(node_name):
    """Build a complete context package for a node."""
    node_file = find_node(node_name)
    if not node_file:
        print(f"Node '{node_name}' not found.")
        print(f"Available nodes: {', '.join(n[0] for n in get_all_nodes())}")
        return

    meta, body = parse_frontmatter(node_file)
    deps = extract_dependencies(body)

    print(f"{'='*60}")
    print(f"YGGDRASIL CONTEXT: {meta.get('name', node_name)}")
    print(f"{'='*60}")
    print()

    # Node content
    print(f"--- NODE ---")
    print(body)
    print()

    # Dependencies context
    if deps["depends_on"]:
        print(f"--- UPSTREAM DEPENDENCIES ---")
        for dep_name in deps["depends_on"]:
            dep_file = find_node(dep_name)
            if dep_file:
                dep_meta, dep_body = parse_frontmatter(dep_file)
                # Extract just purpose and constraints
                for section in ["## Purpose", "## Constraints", "## Public Interface"]:
                    if section in dep_body:
                        start = dep_body.index(section)
                        # Find next section
                        next_section = dep_body.find("\n## ", start + len(section))
                        end = next_section if next_section > 0 else len(dep_body)
                        print(f"\n{dep_name} {section}:")
                        print(dep_body[start + len(section):end].strip())
            else:
                print(f"  {dep_name}: (node file not found)")
        print()

    # Relevant aspects
    aspects = get_relevant_aspects(node_name)
    if aspects:
        print(f"--- APPLICABLE ASPECTS ---")
        for aspect_name, aspect_body in aspects:
            print(f"\n[{aspect_name}]")
            # Extract just the rules section
            if "## Rules" in aspect_body:
                start = aspect_body.index("## Rules")
                next_section = aspect_body.find("\n## ", start + 8)
                end = next_section if next_section > 0 else len(aspect_body)
                print(aspect_body[start:end].strip())
            else:
                # Show first 500 chars
                print(aspect_body[:500])
        print()

    # Relevant flows
    flows = get_relevant_flows(node_name)
    if flows:
        print(f"--- RELATED FLOWS ---")
        for flow_name, flow_body in flows:
            print(f"\n[{flow_name}]")
            print(flow_body[:500])
        print()

    print(f"{'='*60}")
    print(f"End of context for: {meta.get('name', node_name)}")
    print(f"{'='*60}")


def list_nodes():
    """List all available nodes."""
    nodes = get_all_nodes()
    if not nodes:
        print("No nodes found. Create nodes in .yggdrasil/nodes/")
        return

    print("Available nodes:")
    for name, status, filepath in nodes:
        indicator = " (inactive)" if status != "active" else ""
        print(f"  {name}{indicator}")


def show_overview():
    """Show system overview from root.md."""
    root_file = YG_DIR / "root.md"
    if root_file.exists():
        _, body = parse_frontmatter(root_file)
        print(body)
    else:
        print("No root.md found. Create .yggdrasil/root.md with your system overview.")


def show_deps(node_name):
    """Show the dependency chain for a node."""
    node_file = find_node(node_name)
    if not node_file:
        print(f"Node '{node_name}' not found.")
        return

    _, body = parse_frontmatter(node_file)
    deps = extract_dependencies(body)

    print(f"Dependencies for: {node_name}")
    print(f"\n  Depends on:")
    for d in deps["depends_on"] or ["(none)"]:
        print(f"    <- {d}")
    print(f"\n  Depended on by:")
    for d in deps["depended_on_by"] or ["(none)"]:
        print(f"    -> {d}")


def main():
    if not YG_DIR.exists():
        print(f"No .yggdrasil/ directory found at {PROJECT_DIR}")
        print("Run the project setup or create .yggdrasil/ manually.")
        sys.exit(1)

    if len(sys.argv) < 2:
        print(__doc__)
        sys.exit(0)

    arg = sys.argv[1]

    if arg == "--list":
        list_nodes()
    elif arg == "--overview":
        show_overview()
    elif arg == "--deps" and len(sys.argv) >= 3:
        show_deps(sys.argv[2])
    else:
        build_context(arg)


if __name__ == "__main__":
    main()
