"""Fix ranking for autonomous debugging.

This module ranks validated fixes by quality to present
the best options to users.
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import List, Optional

from autodebug.autonomous.hypothesis import Hypothesis
from autodebug.autonomous.proof import ProofCertificate
from autodebug.autonomous.validator import ValidationResult


@dataclass
class RankedFix:
    """A validated fix with ranking information."""

    hypothesis: Hypothesis
    validation: ValidationResult
    certificate: Optional[ProofCertificate]
    rank: int
    score: float

    @property
    def is_proven(self) -> bool:
        """Check if this fix is proven to work."""
        return self.validation.fixes_failure

    @property
    def summary(self) -> str:
        """Get a one-line summary."""
        status = "PROVEN" if self.is_proven else self.validation.verdict
        return (
            f"#{self.rank} ({self.score:.2f}) {self.hypothesis.strategy}: "
            f"{self.hypothesis.root_cause[:60]}... [{status}]"
        )


def compute_fix_score(
    hypothesis: Hypothesis,
    validation: ValidationResult,
) -> float:
    """Compute a quality score for a fix.

    Higher is better. Considers:
    - Whether it fixes the failure (most important)
    - Confidence from LLM
    - Patch size (smaller is better)
    - Pass rate

    Args:
        hypothesis: The hypothesis
        validation: Validation result

    Returns:
        Score between 0 and 1
    """
    score = 0.0

    # Base: does it fix the failure?
    if validation.fixes_failure:
        score += 0.5
    elif validation.same_failure:
        score += 0.0
    elif validation.introduces_new_failure:
        score -= 0.2  # Penalty for new failures

    # Confidence from LLM (0-0.2)
    score += hypothesis.confidence * 0.2

    # Pass rate (0-0.15)
    score += validation.pass_rate * 0.15

    # Patch simplicity (0-0.15)
    # Smaller patches get higher scores
    line_count = hypothesis.patch.line_count
    if line_count <= 3:
        score += 0.15
    elif line_count <= 10:
        score += 0.10
    elif line_count <= 30:
        score += 0.05
    # else: no bonus

    # File count penalty
    # Single file changes preferred
    file_count = hypothesis.patch.file_count
    if file_count > 1:
        score -= 0.05 * (file_count - 1)

    return max(0.0, min(1.0, score))


def rank_fixes(
    validations: List[ValidationResult],
    certificates: Optional[List[ProofCertificate]] = None,
) -> List[RankedFix]:
    """Rank validated fixes by quality.

    Args:
        validations: List of validation results
        certificates: Optional list of proof certificates

    Returns:
        Sorted list of RankedFix objects (best first)
    """
    # Build certificate lookup
    cert_lookup = {}
    if certificates:
        for cert in certificates:
            cert_lookup[cert.hypothesis_id] = cert

    # Score each fix
    scored = []
    for validation in validations:
        hypothesis = validation.hypothesis
        score = compute_fix_score(hypothesis, validation)
        certificate = cert_lookup.get(hypothesis.id)

        scored.append((score, hypothesis, validation, certificate))

    # Sort by score (descending)
    scored.sort(key=lambda x: (-x[0], x[1].patch.line_count))

    # Create ranked fixes
    ranked = []
    for i, (score, hypothesis, validation, certificate) in enumerate(scored):
        ranked.append(RankedFix(
            hypothesis=hypothesis,
            validation=validation,
            certificate=certificate,
            rank=i + 1,
            score=score,
        ))

    return ranked


def filter_proven_fixes(ranked: List[RankedFix]) -> List[RankedFix]:
    """Filter to only proven fixes.

    Args:
        ranked: List of ranked fixes

    Returns:
        Filtered list with only proven fixes
    """
    return [r for r in ranked if r.is_proven]


def get_best_fix(ranked: List[RankedFix]) -> Optional[RankedFix]:
    """Get the best proven fix if any.

    Args:
        ranked: List of ranked fixes

    Returns:
        Best proven fix or None
    """
    proven = filter_proven_fixes(ranked)
    return proven[0] if proven else None


def should_auto_apply(
    fix: RankedFix,
    confidence_threshold: float = 0.95,
    max_lines: int = 10,
) -> bool:
    """Check if a fix should be auto-applied without human review.

    A fix can be auto-applied if:
    - It is proven to work
    - LLM confidence is very high
    - The patch is minimal

    Args:
        fix: The ranked fix
        confidence_threshold: Minimum confidence for auto-apply
        max_lines: Maximum patch lines for auto-apply

    Returns:
        True if safe to auto-apply
    """
    if not fix.is_proven:
        return False

    if fix.hypothesis.confidence < confidence_threshold:
        return False

    if fix.hypothesis.patch.line_count > max_lines:
        return False

    if fix.hypothesis.patch.file_count > 1:
        return False

    return True


def format_ranking_report(
    ranked: List[RankedFix],
    show_all: bool = False,
) -> str:
    """Format ranked fixes as human-readable report.

    Args:
        ranked: List of ranked fixes
        show_all: Show all fixes, not just proven ones

    Returns:
        Formatted report string
    """
    proven = filter_proven_fixes(ranked)
    other = [r for r in ranked if not r.is_proven]

    lines = [
        "",
        "Fix Ranking Results",
        "=" * 50,
        "",
        f"Total hypotheses: {len(ranked)}",
        f"Proven fixes: {len(proven)}",
        "",
    ]

    if proven:
        lines.append("### Proven Fixes (Ranked)")
        lines.append("")

        for fix in proven:
            confidence_pct = fix.hypothesis.confidence * 100
            lines.append(f"#### #{fix.rank}: {fix.hypothesis.strategy} ({confidence_pct:.0f}% confidence)")
            lines.append("")
            lines.append(f"**Score:** {fix.score:.2f}")
            lines.append(f"**Root Cause:** {fix.hypothesis.root_cause}")
            lines.append("")
            lines.append("**Patch:**")
            lines.append("```diff")
            diff = fix.hypothesis.patch.raw_diff
            lines.append(diff[:800])
            if len(diff) > 800:
                lines.append("... (truncated)")
            lines.append("```")
            lines.append("")
            lines.append(f"**Explanation:** {fix.hypothesis.explanation}")
            lines.append("")

            if fix.certificate:
                lines.append(f"**Proof:** Certificate {fix.certificate.certificate_id}")
                lines.append(f"**Validation:** {fix.validation.pass_rate:.0%} pass rate")
            lines.append("")

            # Auto-apply recommendation
            if should_auto_apply(fix):
                lines.append("> **Auto-apply eligible:** This fix is simple and high-confidence.")
            else:
                lines.append("> Recommend human review before applying.")

            lines.append("")
            lines.append("-" * 40)
            lines.append("")

    if not proven:
        lines.append("No proven fixes found.")
        lines.append("")
        lines.append("Suggestions:")
        lines.append("- Try generating more hypotheses")
        lines.append("- Review the failure analysis for additional context")
        lines.append("- Consider manual debugging")

    if show_all and other:
        lines.append("")
        lines.append("### Other Hypotheses (Not Proven)")
        lines.append("")
        for fix in other[:5]:
            lines.append(f"- {fix.summary}")

    return "\n".join(lines)
