"""Test execution and reporting logic."""

from pathlib import Path
from typing import Dict, List, Optional, Tuple

import click

from guardrail.locator import FunctionInfo
from guardrail.report import FixPromptGenerator
from guardrail.runner import TestRunner


def run_and_report_tests(
    test_dir: Path,
    root: Path,
    function_test_map: Dict[FunctionInfo, Tuple[Path, str]],
    execute_command: bool,
    function_to_file: Optional[Dict[FunctionInfo, Path]] = None,
    regenerate_on_failure: bool = True,
    max_regenerations: int = 1,
) -> bool:
    """
    Run tests and report results. Optionally regenerate tests on failure.
    
    Args:
        test_dir: Directory containing test files.
        root: Project root directory.
        function_test_map: Map of functions to their test file paths and code.
        execute_command: Whether to execute user command after tests pass.
        regenerate_on_failure: Whether to regenerate tests when they fail.
        max_regenerations: Maximum number of regeneration attempts.
    
    Returns:
        True if tests passed, False if they failed.
    """
    click.echo(f"\n🧪 Running generated tests...")
    test_runner = TestRunner(test_dir, root)
    passed, test_output, test_error = test_runner.run_tests()

    if passed:
        click.echo("✅ All tests passed!")
        return True

    # Tests failed - try regeneration if enabled
    regeneration_attempt = 0
    while regeneration_attempt < max_regenerations and regenerate_on_failure:
        click.echo(f"\n❌ Tests failed (attempt {regeneration_attempt + 1})...")
        
        # Check if we should regenerate (import here to avoid circular imports)
        from guardrail.cli.test_generation import regenerate_failing_tests
        
        if function_to_file is None:
            # Fallback: try to get function_to_file from detection
            from guardrail.cli.detection import detect_and_analyze_changes
            _, _, _, function_to_file = detect_and_analyze_changes(root)
        
        regenerated = regenerate_failing_tests(
            function_test_map, 
            test_output or test_error or "", 
            test_dir, 
            root,
            function_to_file or {},
        )
        
        if regenerated:
            regeneration_attempt += 1
            click.echo(f"🔄 Regenerated tests, running again...")
            passed, test_output, test_error = test_runner.run_tests()
            if passed:
                click.echo("✅ All tests passed after regeneration!")
                return True
        else:
            break  # No regeneration happened, exit loop

    # Tests still failed - generate fix prompts
    click.echo("❌ Tests failed!")
    click.echo("\n" + "=" * 80)

    failing_tests = test_runner.get_failing_tests(test_output or "")
    fix_prompts = generate_fix_prompts(
        function_test_map, failing_tests, test_output, test_error
    )

    if fix_prompts:
        click.echo("\n".join(fix_prompts))
    else:
        click.echo("\nTest Output:")
        click.echo(test_output or test_error or "No output available")

    click.echo("\n" + "=" * 80)
    if execute_command:
        click.echo("\n🚫 Command execution blocked due to test failures.")
        click.echo("Please fix the failing tests and try again.")
    else:
        click.echo("\n⚠️  Tests failed. Please fix the issues before running your command.")

    return False


def generate_fix_prompts(
    function_test_map: Dict[FunctionInfo, Tuple[Path, str]],
    failing_tests: List[str],
    test_output: str,
    test_error: Optional[str],
) -> List[str]:
    """Generate fix prompts for failing tests."""
    fix_prompts = []

    for func, (test_path, test_code) in function_test_map.items():
        test_name = test_path.stem
        if any(test_name in ft for ft in failing_tests):
            failure_output = test_output or test_error or "Test execution failed"
            prompt = FixPromptGenerator.generate_fix_prompt(
                function=func,
                test_code=test_code,
                failure_output=failure_output[:1000],
                test_file_path=test_path,
            )
            fix_prompts.append(prompt)

    return fix_prompts

