#!/usr/bin/env python3
"""
LLM Self-Improvement Evaluation CLI.

Run pipeline iterations, evaluate against ground truth, track metrics.

Usage:
    # 1. Run baseline first (establishes benchmark)
    python scripts/run_eval.py --baseline --input data/input/torus/2025-12-10/256

    # 2. Create new iteration from baseline
    python scripts/run_eval.py --create v_0001 --from baseline

    # 3. LLM modifies code in src/chainswarm_analyzers_v_0001/

    # 4. Run evaluation (with optional build command)
    python scripts/run_eval.py --iteration v_0001 --input data/input/torus/2025-12-10/256
    python scripts/run_eval.py --iteration v_0001 --input data/input/torus/2025-12-10/256 \\
        --build-cmd "cargo build --release"

    # 5. View history
    python scripts/run_eval.py --history --input data/input/torus/2025-12-10/256

    # 6. Show specific iteration metrics
    python scripts/run_eval.py --show v_0001 --input data/input/torus/2025-12-10/256
"""

import argparse
import json
import os
import shutil
import sys
from pathlib import Path

# Add project root to path
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root / "src"))
os.chdir(str(project_root))

from loguru import logger


def setup_logging():
    """Configure logging."""
    logger.remove()
    logger.add(
        sys.stderr,
        format="<green>{time:HH:mm:ss}</green> | <level>{level: <8}</level> | {message}",
        level="INFO",
    )


def get_output_path(input_path: Path) -> Path:
    """Convert input path to output path."""
    # data/input/torus/2025-12-10/256 -> data/output/torus/2025-12-10/256
    parts = input_path.parts
    if 'input' in parts:
        idx = parts.index('input')
        new_parts = parts[:idx] + ('output',) + parts[idx + 1:]
        return Path(*new_parts)
    return input_path.parent / "output" / input_path.name


def run_baseline(args):
    """Run baseline evaluation."""
    from eval.runner import EvalRunner
    from eval.validator import FlowValidator
    from eval.scorer import EvalScorer
    from eval.metrics import MetricsStorage

    input_path = Path(args.input)
    output_path = get_output_path(input_path)

    logger.info(f"Running baseline evaluation")
    logger.info(f"Input: {input_path}")
    logger.info(f"Output: {output_path}")

    # Initialize components
    runner = EvalRunner(
        input_path=input_path,
        output_base_path=output_path,
        network=args.network or "torus",
    )

    # Run baseline
    build_cmd = args.build_cmd if hasattr(args, 'build_cmd') else None
    result = runner.run_iteration("baseline", build_command=build_cmd)

    if not result.success:
        logger.error(f"Baseline failed: {result.error}")
        return 1

    # Validate and score
    validator = FlowValidator()
    scorer = EvalScorer()
    storage = MetricsStorage(output_path)

    ground_truth = runner.load_ground_truth()
    transfers = runner.load_transfers()

    validation = validator.validate_patterns(
        patterns_df=result.patterns_df,
        transfers_df=transfers,
        ground_truth_df=ground_truth,
        debug=args.debug if hasattr(args, 'debug') else False,
    )

    score = scorer.calculate_score(
        validation_result=validation,
        total_time_seconds=result.total_seconds,
        detector_timing=result.detector_timing,
    )

    # Save metrics
    storage.save_metrics("baseline", score)
    storage.append_to_history("baseline", score)

    # Print summary
    print("\n" + "=" * 60)
    print("BASELINE EVALUATION COMPLETE")
    print("=" * 60)
    print(f"Final Score:       {score.final_score:.4f}")
    print(f"Recall Score:      {score.recall_score:.4f} ({score.synthetic_found}/{score.synthetic_expected})")
    print(f"Novelty Score:     {score.novelty_score:.4f} ({score.novelty_valid} valid)")
    print(f"Performance Score: {score.performance_score:.4f} ({score.total_seconds:.1f}s)")
    print(f"Total Patterns:    {score.total_patterns}")
    print("=" * 60)
    print(f"\nMetrics saved to: {output_path}/baseline/metrics.json")

    return 0


def run_iteration(args):
    """Run iteration evaluation."""
    from eval.runner import EvalRunner
    from eval.validator import FlowValidator
    from eval.scorer import EvalScorer
    from eval.metrics import MetricsStorage

    input_path = Path(args.input)
    output_path = get_output_path(input_path)
    iteration = args.iteration

    logger.info(f"Running iteration: {iteration}")
    logger.info(f"Input: {input_path}")
    logger.info(f"Output: {output_path}")

    # Initialize components
    runner = EvalRunner(
        input_path=input_path,
        output_base_path=output_path,
        network=args.network or "torus",
    )

    # Load baseline for comparison
    storage = MetricsStorage(output_path)
    baseline_score = storage.load_baseline_score()

    if baseline_score is None:
        logger.warning("No baseline found. Run --baseline first for comparison metrics.")

    # Run iteration with optional build command
    result = runner.run_iteration(
        iteration=iteration,
        build_command=args.build_cmd,
        build_timeout=args.build_timeout,
    )

    if not result.success:
        logger.error(f"Iteration {iteration} failed: {result.error}")
        return 1

    # Validate and score
    validator = FlowValidator()
    scorer = EvalScorer()

    ground_truth = runner.load_ground_truth()
    transfers = runner.load_transfers()

    validation = validator.validate_patterns(
        patterns_df=result.patterns_df,
        transfers_df=transfers,
        ground_truth_df=ground_truth,
        debug=args.debug if hasattr(args, 'debug') else False,
    )

    score = scorer.calculate_score(
        validation_result=validation,
        total_time_seconds=result.total_seconds,
        detector_timing=result.detector_timing,
        baseline_score=baseline_score,
    )

    # Save metrics
    build_duration = result.build_result.duration_seconds if result.build_result else None
    storage.save_metrics(iteration, score, args.build_cmd, build_duration)
    storage.append_to_history(iteration, score)

    # Print summary
    print("\n" + "=" * 60)
    print(f"ITERATION {iteration} EVALUATION COMPLETE")
    print("=" * 60)
    print(f"Final Score:       {score.final_score:.4f}", end="")
    if score.baseline_comparison:
        delta = score.baseline_comparison['final_score_delta']
        print(f" ({'+' if delta >= 0 else ''}{delta:.4f} vs baseline)")
    else:
        print()
    print(f"Recall Score:      {score.recall_score:.4f} ({score.synthetic_found}/{score.synthetic_expected})")
    print(f"Novelty Score:     {score.novelty_score:.4f} ({score.novelty_valid} valid)")
    print(f"Performance Score: {score.performance_score:.4f} ({score.total_seconds:.1f}s)")
    print(f"Total Patterns:    {score.total_patterns}")
    print("=" * 60)
    print(f"\nMetrics saved to: {output_path}/{iteration}/metrics.json")

    return 0


def create_iteration(args):
    """Create new iteration from source."""
    source = args.source or "baseline"
    iteration = args.create

    src_dir = project_root / "src"

    # Determine source module
    if source == "baseline":
        source_dir = src_dir / "chainswarm_analyzers_baseline"
    else:
        source_dir = src_dir / f"chainswarm_analyzers_{source}"

    if not source_dir.exists():
        logger.error(f"Source directory not found: {source_dir}")
        return 1

    # Create new iteration directory
    target_dir = src_dir / f"chainswarm_analyzers_{iteration}"

    if target_dir.exists():
        if args.force:
            logger.warning(f"Removing existing {target_dir}")
            shutil.rmtree(target_dir)
        else:
            logger.error(f"Target directory already exists: {target_dir}")
            logger.info("Use --force to overwrite")
            return 1

    # Copy source to target
    logger.info(f"Creating {iteration} from {source}")
    shutil.copytree(source_dir, target_dir)

    # Update module references in __init__.py if needed
    init_file = target_dir / "__init__.py"
    if init_file.exists():
        content = init_file.read_text()
        old_module = f"chainswarm_analyzers_{source}" if source != "baseline" else "chainswarm_analyzers_baseline"
        new_module = f"chainswarm_analyzers_{iteration}"
        content = content.replace(old_module, new_module)
        init_file.write_text(content)

    print(f"\nCreated iteration: {iteration}")
    print(f"Location: {target_dir}")
    print(f"\nNext steps:")
    print(f"  1. Modify code in {target_dir}/")
    print(f"  2. Run: python scripts/run_eval.py --iteration {iteration} --input <input_path>")

    return 0


def show_history(args):
    """Show iteration history."""
    from eval.metrics import MetricsStorage

    input_path = Path(args.input)
    output_path = get_output_path(input_path)

    storage = MetricsStorage(output_path)
    print(storage.print_history_table())

    return 0


def show_metrics(args):
    """Show metrics for specific iteration."""
    from eval.metrics import MetricsStorage

    input_path = Path(args.input)
    output_path = get_output_path(input_path)

    storage = MetricsStorage(output_path)
    metrics = storage.load_metrics(args.show)

    if metrics is None:
        logger.error(f"No metrics found for iteration: {args.show}")
        return 1

    print(json.dumps(metrics, indent=2))
    return 0


def list_iterations(args):
    """List all iterations."""
    from eval.metrics import MetricsStorage

    input_path = Path(args.input)
    output_path = get_output_path(input_path)

    storage = MetricsStorage(output_path)
    iterations = storage.list_iterations()

    if not iterations:
        print("No iterations found.")
        return 0

    print("Available iterations:")
    for it in iterations:
        print(f"  - {it}")

    return 0


def main():
    setup_logging()

    parser = argparse.ArgumentParser(
        description="LLM Self-Improvement Evaluation CLI",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog=__doc__,
    )

    # Mode flags (mutually exclusive)
    mode_group = parser.add_mutually_exclusive_group(required=True)
    mode_group.add_argument(
        "--baseline",
        action="store_true",
        help="Run baseline evaluation (iteration 0)",
    )
    mode_group.add_argument(
        "--iteration",
        type=str,
        metavar="NAME",
        help="Run specific iteration (e.g., v_0001)",
    )
    mode_group.add_argument(
        "--create",
        type=str,
        metavar="NAME",
        help="Create new iteration folder",
    )
    mode_group.add_argument(
        "--history",
        action="store_true",
        help="Show iteration history",
    )
    mode_group.add_argument(
        "--show",
        type=str,
        metavar="NAME",
        help="Show metrics for specific iteration",
    )
    mode_group.add_argument(
        "--list",
        action="store_true",
        help="List all iterations",
    )

    # Input/output paths
    parser.add_argument(
        "--input",
        type=Path,
        help="Input directory (e.g., data/input/torus/2025-12-10/256)",
    )

    # Create options
    parser.add_argument(
        "--from", "--source",
        dest="source",
        type=str,
        default="baseline",
        help="Source iteration to copy from (default: baseline)",
    )
    parser.add_argument(
        "--force",
        action="store_true",
        help="Force overwrite existing iteration",
    )

    # Build hook options
    parser.add_argument(
        "--build-cmd",
        type=str,
        help="Pre-build command to run (e.g., 'cargo build --release')",
    )
    parser.add_argument(
        "--build-timeout",
        type=int,
        default=300,
        help="Build command timeout in seconds (default: 300)",
    )

    # Other options
    parser.add_argument(
        "--network",
        type=str,
        default="torus",
        help="Network name (default: torus)",
    )
    parser.add_argument(
        "--debug",
        action="store_true",
        help="Enable debug logging",
    )

    args = parser.parse_args()

    # Validate input requirement
    if (args.baseline or args.iteration or args.history or args.show or args.list) and not args.input:
        parser.error("--input is required for this operation")

    # Route to appropriate handler
    if args.baseline:
        return run_baseline(args)
    elif args.iteration:
        return run_iteration(args)
    elif args.create:
        return create_iteration(args)
    elif args.history:
        return show_history(args)
    elif args.show:
        return show_metrics(args)
    elif args.list:
        return list_iterations(args)

    return 0


if __name__ == "__main__":
    sys.exit(main())
