#!/usr/bin/env python3
"""Generate nexus-docs.html from template.html + ALL docs/ files.

Usage: python build_html.py
Output: docs/nexus-docs.html (single self-contained HTML file)

Collects: .md .json .js .py .sh .txt .yaml .yml .toml .cfg .ini .conf .env .sql
Excludes: build_html.py, template.html, nexus-docs.html, binary files
"""

import os
import json

DOCS_DIR = os.path.dirname(os.path.abspath(__file__))
TEMPLATE = os.path.join(DOCS_DIR, "template.html")
OUTPUT = os.path.join(DOCS_DIR, "nexus-docs.html")

# File extensions to include (text/documentation files)
INCLUDE_EXT = {
    '.md', '.markdown',
    '.json', '.jsonl',
    '.js', '.ts', '.mjs',
    '.py', '.sh', '.bash',
    '.txt', '.csv',
    '.yaml', '.yml',
    '.toml', '.cfg', '.ini', '.conf',
    '.env', '.example',
    '.sql',
    '.xml', '.html', '.css',
    '.gitignore',
}

# Files to always exclude (by basename)
EXCLUDE_FILES = {
    'build_html.py',
    'template.html',
    'nexus-docs.html',
}

# Directories to skip
SKIP_DIRS = {'.git', '__pycache__', 'node_modules', '.venv', 'venv'}

# Group config: (directory_match, display_name, icon)
GROUP_CONFIG = [
    ("project",   "Project",   "📋"),
    ("design",    "Design",    "🎨"),
    ("changelog", "Changelog", "📝"),
    ("reports",   "Reports",   "📊"),
    ("team",      "Team",      "👥"),
    ("memory",    "Memory",    "🧠"),
    ("research",  "Research",  "🔬"),
    ("skills",    "Skills",    "⚡"),
]

# Fallback for unmatched dirs
FALLBACK_ICON = "📄"


def collect_all_files():
    """Walk docs/ and return list of (relative_path, full_path) sorted. Includes all text doc types."""
    results = []
    for root, dirs, files in os.walk(DOCS_DIR):
        dirs[:] = sorted(d for d in dirs if d not in SKIP_DIRS)
        for f in sorted(files):
            if f in EXCLUDE_FILES:
                continue
            ext = os.path.splitext(f)[1].lower()
            if ext in INCLUDE_EXT:
                full = os.path.join(root, f)
                rel = os.path.relpath(full, DOCS_DIR).replace('\\', '/')
                results.append((rel, full))
    return results


def group_files(files):
    """Group files by top-level directory using GROUP_CONFIG."""
    grouped = {}

    for rel, full in files:
        top = rel.split('/')[0] if '/' in rel else "Root"

        # Match against config
        matched = False
        for dir_key, display_name, icon in GROUP_CONFIG:
            if top == dir_key or top.startswith(dir_key):
                key = display_name
                if key not in grouped:
                    grouped[key] = {"icon": icon, "files": []}
                grouped[key]["files"].append((rel, full))
                matched = True
                break

        if not matched:
            if top not in grouped:
                # Find icon from config if partial match
                icon = FALLBACK_ICON
                for dir_key, _, ic in GROUP_CONFIG:
                    if dir_key in top:
                        icon = ic
                        break
                grouped[top] = {"icon": icon, "files": []}
            grouped[top]["files"].append((rel, full))

    # Sort by GROUP_CONFIG order, then alphabetically for rest
    ordered = []
    for _, display_name, _ in GROUP_CONFIG:
        if display_name in grouped:
            ordered.append((display_name, grouped.pop(display_name)))
    for k in sorted(grouped.keys()):
        ordered.append((k, grouped[k]))

    return ordered


def read_file(path):
    try:
        with open(path, 'r', encoding='utf-8') as f:
            return f.read()
    except Exception:
        return "*Unable to read file*"


def build_data(groups):
    """Build NAV_GROUPS and CONTENT_MAP JS objects."""
    nav_groups = []
    content_map = {}

    for name, info in groups:
        files = []
        for rel, full in info["files"]:
            # Show full filename as label (with extension for non-md files)
            label = rel.split('/')[-1]
            # Remove .md extension only for markdown files
            if label.endswith('.md'):
                label = label[:-3]
            files.append({"path": rel, "label": label})
            # For non-md files, wrap content in a code block for proper rendering
            raw = read_file(full)
            ext = os.path.splitext(rel)[1].lower()
            if ext == '.md':
                content_map[rel] = raw
            else:
                lang = ext.lstrip('.')
                content_map[rel] = f"# `{rel}`\n\n```{lang}\n{raw}\n```"

        nav_groups.append({
            "name": name,
            "icon": info["icon"],
            "collapsed": name == "Skills",  # Skills collapsed by default
            "files": files,
        })

    return nav_groups, content_map


def generate(nav_groups, content_map):
    """Read template, inject data, return final HTML."""
    with open(TEMPLATE, 'r', encoding='utf-8') as f:
        template = f.read()

    nav_json = json.dumps(nav_groups, ensure_ascii=False, indent=None)
    # content_map can be huge — compact serialization
    content_json = json.dumps(content_map, ensure_ascii=False, indent=None)

    # Inject data before the docApp() function
    data_script = f"""<script>
// Auto-generated data — do not edit
const NAV_GROUPS = {nav_json};
const CONTENT_MAP = {content_json};
</script>
"""

    # Insert data script right before the docApp function script
    placeholder = "// __DATA_PLACEHOLDER__ will be replaced by build_html.py"
    if placeholder in template:
        html = template.replace(placeholder, f"// Data injected by build_html.py\nconst NAV_GROUPS = {nav_json};\nconst CONTENT_MAP = {content_json};")
    else:
        # Fallback: insert before closing </body>
        html = template.replace("</body>", data_script + "</body>")

    return html


def main():
    import sys
    sys.stdout.reconfigure(encoding='utf-8')

    print("Scanning docs/...")
    files = collect_all_files()
    md_count = sum(1 for f in files if f[0].endswith('.md'))
    other_count = len(files) - md_count
    print(f"  Found {md_count} markdown + {other_count} other = {len(files)} total files")

    print("Grouping...")
    groups = group_files(files)
    for name, info in groups:
        print(f"  {info['icon']} {name}: {len(info['files'])} files")

    print("Reading content & building data...")
    nav_groups, content_map = build_data(groups)

    print("Generating HTML...")
    html = generate(nav_groups, content_map)

    with open(OUTPUT, 'w', encoding='utf-8') as f:
        f.write(html)

    size_mb = os.path.getsize(OUTPUT) / 1024 / 1024
    print(f"\nDone: {OUTPUT}")
    print(f"Size: {size_mb:.1f} MB")


if __name__ == '__main__':
    main()
