#!/usr/bin/env python3
"""
fsearch — Fractal Search CLI

Search files and directories using multi-scale φ-weighted indexing.

Usage:
    fsearch "query" [path]           # Search a directory
    fsearch "query" file.txt         # Search a single file
    fsearch "query" ~/Documents/     # Search recursively
    fsearch "query" . --depth 4      # Set search depth
    fsearch "query" . --json         # JSON output
    fsearch "query" . --ext .py .md  # Only index specific extensions
"""

import argparse
import json
import os
import sys
import time

from .engine import FractalSearchEngine, PHI


def main():
    parser = argparse.ArgumentParser(
        prog='fsearch',
        description='Fractal Search — Multi-scale document search with φ-weighted decay',
    )
    parser.add_argument('query', help='Search query')
    parser.add_argument('path', nargs='?', default='.', help='File or directory to search (default: current dir)')
    parser.add_argument('--depth', '-d', type=int, default=None, help='Search depth (1-4, default: auto)')
    parser.add_argument('--max', '-n', type=int, default=10, help='Max results (default: 10)')
    parser.add_argument('--ext', nargs='+', default=None, help='File extensions to index (default: .md .txt .py .sh .html .js)')
    parser.add_argument('--json', action='store_true', help='Output as JSON')
    parser.add_argument('--verbose', '-v', action='store_true', help='Show patterns and explanations')
    parser.add_argument('--stats', action='store_true', help='Show index statistics')
    parser.add_argument('--names', '-f', action='store_true', help='Search filenames and directory names only (no content)')
    parser.add_argument('--paths-only', '-p', action='store_true', help='(default behavior, kept for compat)')

    args = parser.parse_args()

    # Initialize engine
    engine = FractalSearchEngine()

    # Determine extensions
    extensions = tuple(args.ext) if args.ext else ('.md', '.txt', '.py', '.sh', '.html', '.js', '.jsx', '.json', '.tsx', '.css')

    # Index
    path = os.path.expanduser(args.path)
    t0 = time.time()

    if args.names:
        # Filename/dirname search mode — index names, not content
        if os.path.isdir(path):
            count = 0
            for root, dirs, files in os.walk(path):
                dirs[:] = [d for d in dirs if d not in {'.git', 'node_modules', '__pycache__', '.venv', 'target'}]
                depth = root.replace(path, '').count(os.sep)
                if depth > 6:
                    dirs.clear()
                    continue
                # Index directory names
                for d in dirs:
                    full = os.path.join(root, d)
                    name = d.replace('_', ' ').replace('-', ' ')
                    engine.add(full, name, name + ' ' + d)
                    count += 1
                # Index file names
                for f in files:
                    if f == '.DS_Store':
                        continue
                    full = os.path.join(root, f)
                    name = f.rsplit('.', 1)[0].replace('_', ' ').replace('-', ' ')
                    engine.add(full, name, name + ' ' + f)
                    count += 1
            if not args.json and not args.paths_only:
                elapsed = time.time() - t0
                print(f"\n  Indexed {count} names in {elapsed:.1f}s", file=sys.stderr)
        else:
            print(f"Error: --names requires a directory", file=sys.stderr)
            sys.exit(1)
    elif os.path.isfile(path):
        engine.add_file(path)
    elif os.path.isdir(path):
        count = engine.add_directory(path, extensions=extensions)
        if not args.json and not args.paths_only:
            elapsed = time.time() - t0
            print(f"\n  Indexed {count} files in {elapsed:.1f}s ({len(engine.index):,} terms)", file=sys.stderr)
    else:
        print(f"Error: {path} not found", file=sys.stderr)
        sys.exit(1)

    # Show stats if requested
    if args.stats:
        stats = engine.stats()
        print(f"\n  Documents: {stats['documents']}", file=sys.stderr)
        print(f"  Terms:     {stats['terms']:,}", file=sys.stderr)
        print(f"  φ:         {stats['phi']}", file=sys.stderr)
        print(f"  Max depth: {stats['max_depth']}", file=sys.stderr)

    # Search
    t1 = time.time()
    results = engine.search(args.query, max_results=args.max, depth=args.depth)
    search_time = time.time() - t1

    # Output
    if args.paths_only:
        for r in results:
            print(r.doc_id)
        return

    if args.json:
        output = {
            "query": args.query,
            "results": [
                {
                    "id": r.doc_id,
                    "title": r.title,
                    "score": round(r.score, 3),
                    "terms": r.matching_terms,
                    "depth": r.depth,
                    "snippet": r.snippet,
                    "patterns": r.patterns if args.verbose else [],
                }
                for r in results
            ],
            "search_time_ms": round(search_time * 1000, 1),
            "phi": PHI,
        }
        print(json.dumps(output, indent=2, ensure_ascii=False))
    else:
        if not results:
            print(f"No results for \"{args.query}\"", file=sys.stderr)
            return

        # Default: just print file paths (clean, pipeable)
        if not args.verbose:
            for r in results:
                print(r.doc_id)
            return

        # Verbose: full display
        print(f"\n  ◊ fsearch \"{args.query}\" — {len(results)} results ({search_time*1000:.0f}ms)\n")

        for i, r in enumerate(results, 1):
            max_score = results[0].score if results else 1
            bar_len = int((r.score / max_score) * 20)
            bar = '█' * bar_len + '░' * (20 - bar_len)

            print(f"  {i:>2}. {bar} {r.score:>8.1f}  {r.title[:60]}")
            print(f"      {r.doc_id}")

            if r.matching_terms:
                print(f"      terms: {', '.join(r.matching_terms[:8])}")

            if r.patterns:
                for p in r.patterns[:3]:
                    ptype = p.get('type', '?')
                    if ptype == 'co-occurrence':
                        print(f"      ↳ co-occurrence: {p['terms']} (+{p['boost']:.3f})")
                    elif ptype == 'self-similarity':
                        print(f"      ↳ self-similar at {p['scale']}[{p['index']}]: {p['similarity']}")

            if r.snippet:
                snip = r.snippet.replace('\n', ' ')[:100]
                print(f"      \"{snip}...\"")

            print()

        print(f"  φ = {PHI} · depth = {results[0].depth if results else '?'}")
        print()


if __name__ == '__main__':
    main()
