"""Frequency Threshold Experiment — Test different min_coverage values.

The current pipeline uses min_coverage=0.2 fixed. This tests what happens
at 0.01, 0.05, 0.10, 0.15, 0.20 on all 4 codebases.
"""

import json
import time
from collections import Counter, defaultdict
from pathlib import Path

from bex.tag_preprocessor.analyze import (
    _preprocess_files, _file_to_package, scan_directory, frequency_filter
)
from bex.tag_preprocessor.code import _extract_call_tokens, _extract_coarsened_tokens
from bex.twotinf import build_soa
from bex.rwr0 import rwr0


RESULTS_DIR = Path(__file__).parent / "results"


CODEBASES = {
    "ragsak": {"name": "RAGSAK", "path": "/home/tobi/Desktop/kesai/RAGSAK", "ext": ".kt"},
    "flask": {"name": "Flask", "path": "/home/tobi/Desktop/dervish/external_refs/flask", "ext": ".py"},
    "coroutines": {"name": "Kotlin Coroutines", "path": "/home/tobi/Desktop/dervish/projects/grammar-inference-engine/external_refs/kotlinx.coroutines", "ext": ".kt"},
    "fastapi": {"name": "FastAPI", "path": "/home/tobi/Desktop/dervish/projects/grammar-inference-engine/external_refs/fastapi", "ext": ".py"},
}

THRESHOLDS = [0.0, 0.01, 0.02, 0.05, 0.10, 0.15, 0.20]


def load_data(base, ext):
    groups = scan_directory(base)
    files = groups.get(ext, [])
    sequences, seq_files = _preprocess_files(files)
    raw_seqs = [_extract_call_tokens(seq) for seq in sequences]
    packages = [_file_to_package(fp, base) for fp in seq_files]
    return raw_seqs, packages, seq_files


def measure_at_threshold(raw_seqs, packages, threshold):
    """Measure what happens at a given frequency threshold."""
    # Count symbol frequency
    sym_file = Counter()
    for seq in raw_seqs:
        for sym in set(seq):
            sym_file[sym] += 1

    total = len(raw_seqs)
    n_keep = sum(1 for c in sym_file.values() if c >= max(1, int(total * threshold)))
    keep_syms = {s for s, c in sym_file.items() if c >= max(1, int(total * threshold))}
    surviving = sum(1 for seq in raw_seqs if any(s in keep_syms for s in seq))

    # Filter sequences
    wrapped = [[(j, s, 0) for j, s in enumerate(seq)] for seq in raw_seqs]
    filtered = frequency_filter(wrapped, min_coverage=threshold)
    clean = [[text for _, text, _ in r] for r in filtered]
    clean = [s for s in clean if s]

    # Group by first 3 symbols for inference
    contexts = defaultdict(list)
    for i, seq in enumerate(clean):
        if not seq:
            contexts[("_eps",)].append(seq)
        else:
            ctx = tuple(seq[:3])
            contexts[ctx].append(seq)

    sore_successes = 0
    methods_in_good = 0
    total_methods = 0
    top_patterns = []

    for ctx, seqs in sorted(contexts.items(), key=lambda x: -len(x[1])):
        n = len(seqs)
        total_methods += n
        if n < 3:
            continue
        unique = len(set(tuple(s) for s in seqs))
        if unique / n > 0.9:
            continue
        alphabet = set()
        for s in seqs:
            alphabet.update(s)
        if len(alphabet) > 20:
            continue
        try:
            soa = build_soa(seqs)
            sore = rwr0(soa)
            if sore != "∅":
                sore_successes += 1
                methods_in_good += n
                top_patterns.append({
                    "context": str(ctx),
                    "methods": n,
                    "unique": unique,
                    "sore": sore[:150],
                })
        except Exception:
            pass

    return {
        "threshold": threshold,
        "symbols_total": len(sym_file),
        "symbols_kept": n_keep,
        "seqs_total": total,
        "seqs_surviving": surviving,
        "contexts": len(contexts),
        "sore_successes": sore_successes,
        "total_methods": total_methods,
        "methods_in_good": methods_in_good,
        "coverage": round(methods_in_good / total_methods * 100, 1) if total_methods else 0,
        "top_patterns": top_patterns[:5],
    }


def run_codebase(key):
    cfg = CODEBASES[key]
    print(f"\n{'=' * 60}")
    print(f"  {cfg['name']}")
    print(f"{'=' * 60}")

    raw_seqs, packages, seq_files = load_data(cfg["path"], cfg["ext"])
    print(f"  {len(raw_seqs)} methods from {len(set(packages))} packages")

    results = []
    for thresh in THRESHOLDS:
        t0 = time.time()
        r = measure_at_threshold(raw_seqs, packages, thresh)
        elapsed = time.time() - t0
        r["elapsed"] = round(elapsed, 2)
        results.append(r)
        print(f"  thresh={thresh:.2f}  syms={r['symbols_kept']:4d}  seqs={r['seqs_surviving']:5d}  "
              f"ctxs={r['contexts']:4d}  SOREs={r['sore_successes']:3d}  cov={r['coverage']:5.1f}%  "
              f"({elapsed:.1f}s)")

    out_path = RESULTS_DIR / f"freq_{key}.json"
    with open(out_path, "w") as f:
        json.dump({"codebase": cfg["name"], "results": results}, f, indent=2)
    print(f"  Saved to {out_path}")

    return results


def main():
    all_results = {}
    for key in CODEBASES:
        all_results[key] = run_codebase(key)

    # Summary table
    print(f"\n{'=' * 80}")
    print("  SUMMARY")
    print(f"{'=' * 80}")
    print(f"  {'Codebase':20s} {'Thresh':7s} {'Syms':5s} {'Seqs':6s} {'SOREs':6s} {'Cov':7s}")
    print(f"  {'-'*55}")
    for key, results in all_results.items():
        name = CODEBASES[key]["name"]
        for r in results:
            print(f"  {name:20s} {r['threshold']:7.2f} {r['symbols_kept']:5d} "
                  f"{r['seqs_surviving']:6d} {r['sore_successes']:6d} {r['coverage']:6.1f}%")

    return all_results


if __name__ == "__main__":
    main()
