"""Healer — full healing pipeline."""

import time
from typing import Any
from uuid import UUID

from xyglue.plan.models import Step
from xyglue.execute.models import ExecutionResult
from xyglue.heal.models import Failure, Fix, HealResult
from xyglue.heal.detector import FailureDetector
from xyglue.heal.diagnoser import Diagnoser
from xyglue.learn.patterns import PatternLibrary
from xyglue.learn.models import Pattern


class Healer:
    """
    Full healing pipeline.
    Priority: Proven patterns first (fast), AI diagnosis second (slower, creative).
    """

    def __init__(
        self,
        pattern_library: PatternLibrary,
        ai_client: Any = None,
        max_heals_per_step: int = 3,
    ) -> None:
        self.detector = FailureDetector()
        self.diagnoser = Diagnoser(ai_client=ai_client)
        self.patterns = pattern_library
        self.max_heals_per_step = max_heals_per_step
        self.heal_count = 0

    async def heal(
        self, run_id: UUID, step: Step, result: ExecutionResult
    ) -> HealResult:
        start = time.time()

        # Detect and classify
        failure = self.detector.detect(run_id, step, result)
        if failure is None:
            return HealResult.failed("No failure detected")

        # Try pattern library first (fast path)
        pattern_fix = self._find_pattern_fix(failure)
        if pattern_fix:
            self.heal_count += 1
            return HealResult(
                success=True,
                fix_applied=pattern_fix,
                message=f"Pattern fix: {pattern_fix.fix_type}",
                duration_ms=int((time.time() - start) * 1000),
            )

        # AI diagnosis fallback (slow path)
        diagnosis = await self.diagnoser.diagnose(failure)
        fix = Fix(
            failure=failure,
            fix_type=diagnosis.get("fix_type", "retry"),
            fix_params=diagnosis.get("fix_params", {}),
            diagnosis=diagnosis.get("diagnosis", ""),
            source="ai",
            confidence=diagnosis.get("confidence", 0.5),
        )
        self.heal_count += 1
        return HealResult(
            success=True,
            fix_applied=fix,
            message=f"AI fix: {fix.fix_type} (confidence: {fix.confidence:.2f})",
            duration_ms=int((time.time() - start) * 1000),
        )

    def _find_pattern_fix(self, failure: Failure) -> Fix | None:
        signatures = failure.get_hierarchical_signatures()
        for level in ["exact", "domain", "action", "generic"]:
            sig = signatures.get(level, "")
            if not sig:
                continue
            pattern = self.patterns.find_match(sig, min_confidence=0.5)
            if pattern:
                return Fix(
                    failure=failure,
                    fix_type=pattern.fix_type,
                    fix_params=pattern.fix_params,
                    diagnosis=pattern.description,
                    source="pattern",
                    pattern_id=pattern.id,
                    confidence=pattern.confidence,
                )
        return None

    def record_fix_outcome(self, fix: Fix, success: bool) -> None:
        if fix.source == "pattern" and fix.pattern_id:
            self.patterns.record_use(fix.pattern_id, success)
        if fix.source == "ai" and success and fix.confidence >= 0.7:
            new_pattern = Pattern(
                name=f"learned_{fix.fix_type}_{fix.failure.error_type}",
                signature=fix.failure.signature,
                category=fix.failure.error_type,
                description=fix.diagnosis,
                fix_type=fix.fix_type,
                fix_params=fix.fix_params,
                confidence=fix.confidence,
                is_seeded=False,
            )
            new_pattern.record_use(True)
            self.patterns.add(new_pattern)

    def can_heal(self) -> bool:
        return self.heal_count < self.max_heals_per_step

    def reset_for_step(self) -> None:
        self.heal_count = 0
