"""
Test script to verify fixes for previously failing mutations.

Run with: python -m tests.test_failed_mutations
Or: pytest tests/test_failed_mutations.py -v
"""

import csv
import sys
import time
from pathlib import Path
from typing import Tuple

# Add parent to path for imports
sys.path.insert(0, str(Path(__file__).parent.parent))

from geney.pipelines import oncosplice_pipeline


def load_failed_mutations(filepath: str = "docs/failures.txt") -> list[Tuple[str, str, str]]:
    """Load mutations from failures.txt, returns list of (mut_id, original_error, chunk)."""
    mutations = []
    with open(filepath, 'r') as f:
        reader = csv.DictReader(f)
        for row in reader:
            mut_id = row['mutation_id'].strip('"')
            error = row['error'].strip('"')
            chunk = row['chunk'].strip('"')
            mutations.append((mut_id, error, chunk))
    return mutations


def categorize_error(error: str) -> str:
    """Categorize error type for reporting."""
    if "outside sequence bounds" in error:
        return "bounds"
    elif "cons_vector" in error:
        return "cons_vector"
    elif "concatenate DataFrames" in error:
        return "concat"
    elif "mismatch" in error.lower():
        return "ref_mismatch"
    elif "memory" in error.lower():
        return "oom"
    else:
        return "other"


def check_mutation(mut_id: str, timeout_seconds: int = 300) -> Tuple[bool, str]:
    """
    Test a single mutation through the pipeline.
    
    Returns: (success: bool, message: str)
    """
    try:
        result = oncosplice_pipeline(mut_id)
        
        if result.empty:
            return True, "OK (no transcript covers mutation - null case)"
        else:
            return True, f"OK ({len(result)} isoforms)"
            
    except MemoryError:
        return False, "MemoryError (expected for large genes)"
    except Exception as e:
        return False, f"ERROR: {type(e).__name__}: {str(e)[:100]}"


def run_tests(mutations: list[Tuple[str, str, str]], 
              max_mutations: int = None,
              skip_oom: bool = True) -> dict:
    """
    Run all mutations through the pipeline and collect results.
    
    Args:
        mutations: List of (mut_id, original_error, chunk)
        max_mutations: Optional limit on number to test
        skip_oom: Skip mutations that originally failed with OOM
    """
    results = {
        "total": 0,
        "passed": 0,
        "failed": 0,
        "skipped": 0,
        "by_original_error": {},
        "failures": [],
        "successes": [],
    }
    
    if max_mutations:
        mutations = mutations[:max_mutations]
    
    for i, (mut_id, original_error, chunk) in enumerate(mutations):
        error_type = categorize_error(original_error)
        
        # Skip OOM mutations if requested
        if skip_oom and error_type == "oom":
            results["skipped"] += 1
            print(f"[{i+1}/{len(mutations)}] SKIP (OOM): {mut_id}")
            continue
        
        results["total"] += 1
        
        print(f"[{i+1}/{len(mutations)}] Testing: {mut_id} (was: {error_type})...", end=" ", flush=True)
        
        start = time.time()
        success, message = check_mutation(mut_id)
        elapsed = time.time() - start
        
        # Track by original error type
        if error_type not in results["by_original_error"]:
            results["by_original_error"][error_type] = {"passed": 0, "failed": 0}
        
        if success:
            results["passed"] += 1
            results["by_original_error"][error_type]["passed"] += 1
            results["successes"].append((mut_id, error_type, message))
            print(f"{message} ({elapsed:.1f}s)")
        else:
            results["failed"] += 1
            results["by_original_error"][error_type]["failed"] += 1
            results["failures"].append((mut_id, error_type, original_error, message))
            print(f"FAILED: {message} ({elapsed:.1f}s)")
    
    return results


def print_summary(results: dict):
    """Print test summary."""
    print("\n" + "="*80)
    print("TEST SUMMARY")
    print("="*80)
    
    print(f"\nTotal tested: {results['total']}")
    print(f"Passed: {results['passed']} ({100*results['passed']/max(1,results['total']):.1f}%)")
    print(f"Failed: {results['failed']} ({100*results['failed']/max(1,results['total']):.1f}%)")
    print(f"Skipped: {results['skipped']}")
    
    print("\n--- Results by Original Error Type ---")
    for error_type, counts in sorted(results["by_original_error"].items()):
        total = counts["passed"] + counts["failed"]
        pct = 100 * counts["passed"] / max(1, total)
        print(f"  {error_type:15} : {counts['passed']:3}/{total:3} passed ({pct:.0f}%)")
    
    if results["failures"]:
        print("\n--- Remaining Failures ---")
        for mut_id, orig_type, orig_error, new_error in results["failures"][:20]:
            print(f"  {mut_id}")
            print(f"    Original: {orig_type} - {orig_error[:60]}...")
            print(f"    New:      {new_error[:80]}")
        if len(results["failures"]) > 20:
            print(f"  ... and {len(results['failures'])-20} more")


def main():
    """Main test runner."""
    import argparse
    
    parser = argparse.ArgumentParser(description="Test previously failed mutations")
    parser.add_argument("--max", type=int, default=None, 
                        help="Maximum number of mutations to test")
    parser.add_argument("--include-oom", action="store_true",
                        help="Include OOM mutations (will likely fail)")
    parser.add_argument("--failures-file", default="docs/failures.txt",
                        help="Path to failures.txt")
    parser.add_argument("--error-type", type=str, default=None,
                        help="Only test specific error type: bounds, cons_vector, concat, ref_mismatch")
    args = parser.parse_args()
    
    print("Loading failed mutations...")
    mutations = load_failed_mutations(args.failures_file)
    print(f"Loaded {len(mutations)} mutations")
    
    # Filter by error type if specified
    if args.error_type:
        mutations = [(m, e, c) for m, e, c in mutations 
                     if categorize_error(e) == args.error_type]
        print(f"Filtered to {len(mutations)} mutations with error type: {args.error_type}")
    
    print("\nRunning tests...")
    print("-" * 80)
    
    results = run_tests(
        mutations, 
        max_mutations=args.max,
        skip_oom=not args.include_oom
    )
    
    print_summary(results)
    
    # Exit with error code if any failures
    sys.exit(1 if results["failed"] > 0 else 0)


if __name__ == "__main__":
    main()
