#!/usr/bin/env python3
"""Generate a plugins-only audit report with usage classification
Classifies each plugin file under src/plugins as:
 - runtime: referenced in non-test non-doc source files
 - tests-only: referenced only in test modules or tests/ folder
 - docs-only: referenced only in docs/ or markdown files
 - mixed: references span multiple categories
Also reports whether the plugin is registered (register_plugin_builder!/register_exec_plugin_builder! or forced in factory Lazy::force).
Outputs to docs/PLUGINS_AUDIT_PLUGINS.md
"""
import re
from pathlib import Path
import os
ROOT = Path(__file__).resolve().parents[1]
SRC = ROOT / 'src'
DOCS = ROOT / 'docs'
OUT = DOCS / 'PLUGINS_AUDIT_PLUGINS.md'

# gather all files content
files = list(SRC.rglob('*.rs')) + list(DOCS.rglob('*'))
content = {}
for p in files:
    try:
        content[str(p.relative_to(ROOT))] = p.read_text(encoding='utf-8', errors='ignore')
    except Exception:
        content[str(p.relative_to(ROOT))] = ''

# plugin files under src/plugins
plugin_files = sorted((SRC / 'plugins').rglob('*.rs'))

pub_re = re.compile(r"pub\s+(?:struct|enum)\s+(\w+)")
register_macro_re = re.compile(r"register_(?:plugin_builder|exec_plugin_builder)!\(")
factory_force_re = re.compile(r"Lazy::force\(&crate::plugins::([\w:]+)::([A-Z0-9_]+)\)")

# helper to classify file path string -> category
def classify_path(rel_path, file_text):
    # docs
    if rel_path.startswith('docs/') or rel_path.lower().endswith('.md'):
        return 'doc'
    # tests directory
    if '/tests/' in rel_path or rel_path.startswith('tests/'):
        return 'test'
    # source file containing test module
    if '#[cfg(test)]' in file_text or 'mod tests' in file_text:
        return 'test'
    # otherwise runtime
    return 'runtime'

# build an index of all text for searching
all_text = '\n'.join(content.values())

rows = []
for pf in plugin_files:
    rel = str(pf.relative_to(ROOT))
    text = content.get(rel, '')
    exports = pub_re.findall(text)

    # registered? scan for register macro in file or factory Lazy::force referring this module
    registered = False
    if register_macro_re.search(text):
        registered = True
    # check factory lazy force
    module_path = str(pf.relative_to(SRC)).replace('.rs','').replace(os.sep, '::')
    for m in factory_force_re.findall(all_text):
        if module_path in m[0]:
            registered = True
            break

    # find references for each export
    runtime_refs = set()
    test_refs = set()
    doc_refs = set()
    total_refs = 0
    # if no exports, use filename stem as symbol
    symbols = exports if exports else [pf.stem]
    for sym in symbols:
        # search for word occurrences across files
        wre = re.compile(r"\b" + re.escape(sym) + r"\b")
        for rel2, txt in content.items():
            count = len(wre.findall(txt))
            if count == 0:
                continue
            total_refs += count
            cat = classify_path(rel2, txt)
            if cat == 'runtime':
                runtime_refs.add(rel2)
            elif cat == 'test':
                test_refs.add(rel2)
            elif cat == 'doc':
                doc_refs.add(rel2)

    # classification
    cats = set()
    if runtime_refs: cats.add('runtime')
    if test_refs: cats.add('tests')
    if doc_refs: cats.add('docs')
    if not cats:
        classification = 'unused'
    elif len(cats) == 1:
        classification = next(iter(cats))
    else:
        classification = 'mixed'

    rows.append({
        'file': rel,
        'exports': ', '.join(exports) if exports else '-',
        'registered': 'yes' if registered else 'no',
        'runtime_refs': len(runtime_refs),
        'test_refs': len(test_refs),
        'doc_refs': len(doc_refs),
        'total_refs': total_refs,
        'classification': classification,
    })

# write markdown
lines = []
lines.append('# Plugins Audit (plugins/ only)')
lines.append('Generated by scripts/generate_plugins_audit_plugins.py')
lines.append('')
lines.append('| File | Exports | Registered? | Runtime refs | Test refs | Doc refs | Total refs | Classification |')
lines.append('|------|---------|-------------:|-------------:|---------:|---------:|-----------:|----------------:|')
for r in rows:
    lines.append(f"| `{r['file']}` | {r['exports']} | {r['registered']} | {r['runtime_refs']} | {r['test_refs']} | {r['doc_refs']} | {r['total_refs']} | {r['classification']} |")

OUT.write_text('\n'.join(lines), encoding='utf-8')
print('Wrote', OUT)
