"""
generate_sidebar_tree.py

Generates sidebar-tree.json from your org-roam directory.
Groups notes by #+category: property.
Notes without a category go into "uncategorised".

Output shape:
{
  "groups": [
    {
      "label": "java",
      "files": [
        { "title": "Java MOC", "url": "/20250402185735-java_moc.html" },
        ...
      ]
    },
    ...
    {
      "label": "uncategorised",
      "files": [ ... ]
    }
  ]
}
"""

import json
import re
from pathlib import Path
from collections import defaultdict


def get_org_metadata(file_path: Path) -> dict:
    """Extract title, category (and optionally id) from an Org file."""
    title_pattern    = re.compile(r"^#\+title:\s*(.+)",    re.IGNORECASE | re.MULTILINE)
    category_pattern = re.compile(r"^#\+category:\s*(.+)", re.IGNORECASE | re.MULTILINE)
    id_pattern       = re.compile(r"^:ID:\s*(.+)",         re.MULTILINE)

    try:
        content = file_path.read_text(encoding="utf-8", errors="ignore")
    except OSError:
        return {}

    title_match    = title_pattern.search(content)
    category_match = category_pattern.search(content)
    id_match       = id_pattern.search(content)

    return {
        "title":    title_match.group(1).strip()    if title_match    else "Untitled",
        "category": category_match.group(1).strip() if category_match else None,
        "id":       id_match.group(1).strip()       if id_match       else None,
    }


def generate_sidebar_tree(org_dir: Path, out_file: Path) -> None:
    """Generate sidebar-tree.json grouped by #+category:."""
    print(" Generating sidebar-tree.json...")

    org_dir  = org_dir.expanduser().resolve()
    out_file = out_file.expanduser()

    # Collect all .org files, skip lock/temp files
    org_files = [
        f for f in org_dir.rglob("*.org")
        if not re.search(r"/\.#[^/]+\.org$", str(f))
    ]

    groups: dict[str, list[dict]] = defaultdict(list)

    for file_path in org_files:
        meta = get_org_metadata(file_path)
        if not meta:
            continue

        # Build the URL the same way as search-index.json
        try:
            relative = file_path.relative_to(org_dir).with_suffix("")
        except ValueError:
            continue

        entry = {
            "title": meta["title"],
            "url":   f"/{relative}.html",
        }

        category = (meta["category"] or "").strip().lower() or "uncategorised"
        groups[category].append(entry)

    # Sort files within each group alphabetically by title
    for cat in groups:
        groups[cat].sort(key=lambda e: e["title"].lower())

    # Sort groups alphabetically, but always put "uncategorised" last
    sorted_labels = sorted(
        groups.keys(),
        key=lambda k: (k == "uncategorised", k.lower())
    )
    output = {
        "tree": build_tree(org_dir)
    }
    out_file.parent.mkdir(parents=True, exist_ok=True)
    with open(out_file, "w", encoding="utf-8") as f:
        json.dump(output, f, ensure_ascii=False, indent=2)

            
    files_count, folder_count = count_nodes(output["tree"])

    print(f" sidebar-tree.json generated ({folder_count} folders, {files_count} files)")
    print(f"   → {out_file}")



def count_nodes(nodes):
    total_files = 0
    total_folders = 0

    if not nodes:
        return 0, 0   # ✅ IMPORTANT BASE CASE

    for node in nodes:
        total_folders += 1
        total_files += len(node.get("files", []))

        child_files, child_folders = count_nodes(node.get("children", []))
        total_files += child_files
        total_folders += child_folders

    return total_files, total_folders    
        
def build_tree(org_dir: Path) -> list:
    """Build nested folder tree based on filesystem structure."""

    def insert_node(nodes, parts, entry):
        if not parts:
            return

        label = parts[0]

        # Find or create folder
        node = next((n for n in nodes if n["label"] == label), None)
        if not node:
            node = {"label": label, "children": [], "files": []}
            nodes.append(node)

        if len(parts) == 1:
            node["files"].append(entry)
        else:
            insert_node(node["children"], parts[1:], entry)

    tree = []

    org_files = [
        f for f in org_dir.rglob("*.org")
        if not re.search(r"/\.#[^/]+\.org$", str(f))
    ]

    for file_path in org_files:
        meta = get_org_metadata(file_path)
        if not meta:
            continue

        try:
            relative = file_path.relative_to(org_dir)
        except ValueError:
            continue

        entry = {
            "title": meta["title"],
            "url": f"/{relative.with_suffix('')}.html",
        }

        folder_parts = relative.parts[:-1]  # ignore filename
        insert_node(tree, list(folder_parts), entry)

    # Sort everything
    def sort_tree(nodes):
        nodes.sort(key=lambda n: n["label"].lower())
        for node in nodes:
            node["files"].sort(key=lambda f: f["title"].lower())
            sort_tree(node["children"])

    sort_tree(tree)

    return tree

        
def main():
    base_dir     = Path.home() / "master-folder" / "org_files" / "org_roam"
    sidebar_tree = base_dir / "output" / "assets" / "sidebar-tree.json"

    generate_sidebar_tree(base_dir, sidebar_tree)


if __name__ == "__main__":
    main()
