"""Pattern mining and root cause analysis."""
from __future__ import annotations

from collections import Counter, defaultdict
from datetime import datetime, timedelta, UTC
from typing import Any
from uuid import uuid4

from .models import FailurePattern, RootCauseHypothesis, RootCauseReport


class PatternMiner:
    """Mine recurring failure patterns from learning data."""

    def __init__(self, store: Any) -> None:
        """Initialize pattern miner.

        Args:
            store: LearningStore instance
        """
        self.store = store

    def mine_patterns(
        self,
        *,
        workspace_id: str | None = None,
        agent_name: str | None = None,
        limit: int = 500,
        min_occurrences: int = 3,
    ) -> list[FailurePattern]:
        """Mine failure patterns from runs."""
        runs = self.store.list_runs(
            workspace_id=workspace_id,
            agent_name=agent_name,
            limit=limit,
        )

        failure_by_type: dict[str, list[str]] = defaultdict(list)
        failure_by_run: dict[str, list[str]] = defaultdict(list)

        for run in runs:
            feedback = self.store.list_feedback(run_id=run.id)
            for item in feedback:
                for failure_type in item.failure_types or []:
                    failure_by_type[failure_type].append(run.id)
                    failure_by_run[run.id].append(failure_type)

        patterns = []
        for failure_type, run_ids in failure_by_type.items():
            if len(run_ids) >= min_occurrences:
                pattern = self._create_pattern(failure_type, run_ids, failure_by_run)
                patterns.append(pattern)

        return sorted(patterns, key=lambda p: p.occurrence_count, reverse=True)

    def save_lessons(
        self,
        reflection_memory: Any,
        *,
        workspace_id: str | None = None,
        agent_name: str | None = None,
        min_occurrences: int = 2,
    ) -> list[Any]:
        """Mine patterns and save as lessons to reflection memory.
        
        Args:
            reflection_memory: ReflectionMemory instance
            workspace_id: Optional workspace filter
            agent_name: Optional agent filter
            min_occurrences: Minimum occurrences to create lesson
            
        Returns:
            List of saved ReflectionLesson objects
        """
        from .models import ReflectionLesson
        
        patterns = self.mine_patterns(
            workspace_id=workspace_id,
            agent_name=agent_name,
            min_occurrences=min_occurrences,
        )
        
        lessons = []
        for pattern in patterns:
            lesson = ReflectionLesson(
                id=uuid4().hex,
                workspace_id=workspace_id,
                agent_name=agent_name,
                category=pattern.suggested_fix_type,  # Use suggested_fix_type as category
                lesson=pattern.description,
                rationale=f"Observed in {pattern.occurrence_count} runs",
                suggested_action=pattern.suggested_fix or "Review and fix",
                confidence=pattern.confidence,
                source_run_ids=pattern.affected_runs[:10],
                metadata={
                    "pattern_type": pattern.pattern_type,
                    "failure_types": pattern.failure_types,
                    "signature": pattern.signature,  # Add signature to metadata
                },
            )
            reflection_memory.save_lesson(lesson)
            lessons.append(lesson)
        
        return lessons

    def _create_pattern(
        self,
        failure_type: str,
        run_ids: list[str],
        failure_by_run: dict[str, list[str]],
    ) -> FailurePattern:
        """Create failure pattern from data."""
        co_occurring = Counter()
        for run_id in run_ids:
            for ft in failure_by_run.get(run_id, []):
                if ft != failure_type:
                    co_occurring[ft] += 1
        
        description = f"Failure type '{failure_type}' occurs in {len(run_ids)} runs"
        if co_occurring:
            most_common = co_occurring.most_common(1)[0]
            description += f", often with '{most_common[0]}' ({most_common[1]} times)"
        
        suggested_fix = self._suggest_fix(failure_type, co_occurring)
        
        # Determine suggested fix type based on failure type
        fix_type_map = {
            "bad_retrieval": "retrieval_policy",
            "wrong_tool": "tool_path",
            "hallucination": "prompt",
            "policy_violation": "guardrail",
            "timeout": "routing_policy",
            "format_error": "prompt",
        }
        suggested_fix_type = fix_type_map.get(failure_type, "prompt")
        
        signature = failure_type  # Just the failure type for test compatibility

        return FailurePattern(
            id=uuid4().hex,
            pattern_type="recurring_failure",
            description=description,
            failure_types=[failure_type] + [ft for ft, _ in co_occurring.most_common(3)],
            occurrence_count=len(run_ids),
            affected_runs=run_ids[:20],
            suggested_fix=suggested_fix,
            suggested_fix_type=suggested_fix_type,
            confidence=min(1.0, len(run_ids) / 50.0),
            signature=signature,
        )

    def _suggest_fix(self, failure_type: str, co_occurring: Counter) -> str | None:
        """Suggest fix based on failure type."""
        fixes = {
            "bad_retrieval": "Improve retrieval quality or increase retrieval limit",
            "hallucination": "Add grounding requirements to prompt or use RAG",
            "policy_violation": "Review and update safety guidelines in prompt",
            "wrong_tool": "Improve tool selection logic or add tool descriptions",
            "timeout": "Increase timeout or optimize prompt length",
            "format_error": "Add output format examples to prompt",
        }
        return fixes.get(failure_type)


class RootCauseAnalyzer:
    """Analyze root causes of failures."""

    def __init__(self, store: Any) -> None:
        """Initialize root cause analyzer.
        
        Args:
            store: LearningStore instance
        """
        self.store = store

    def analyze(
        self,
        *,
        workspace_id: str | None = None,
        agent_name: str | None = None,
        window_days: int = 7,
        limit: int = 1000,
        min_occurrences: int = 1,
    ) -> RootCauseReport:
        """Analyze root causes of failures in time window.
        
        Args:
            workspace_id: Optional workspace filter
            agent_name: Optional agent filter
            window_days: Analysis window in days
            limit: Maximum runs to analyze
            min_occurrences: Minimum occurrences for classification
            
        Returns:
            RootCauseReport with hypotheses and recommendations
        """
        
        anchor = datetime.now(UTC)
        window_start = anchor - timedelta(days=window_days)

        runs = self.store.list_runs(
            workspace_id=workspace_id,
            agent_name=agent_name,
            limit=limit,
        )

        failed_runs = []
        failure_type_counts: Counter = Counter()
        all_hypotheses: list[RootCauseHypothesis] = []
        failure_summary: dict[str, int] = {}
        correlations: dict[str, list[dict[str, Any]]] = {
            "prompt_version": [],
            "tool_path": [],
            "retrieval_profile": [],
        }

        for run in runs:
            run_time = self._parse_timestamp(run.created_at)
            if run_time and run_time < window_start:
                continue

            feedback = self.store.list_feedback(run_id=run.id)
            has_failure = any(item.failure_types for item in feedback)

            if has_failure:
                failed_runs.append(run)
                for item in feedback:
                    for ft in item.failure_types or []:
                        failure_type_counts[ft] += 1
                        failure_summary[ft] = failure_summary.get(ft, 0) + 1

        # Build correlations from failed runs
        for run in failed_runs[:10]:
            if run.prompt_version:
                correlations["prompt_version"].append({"value": run.prompt_version, "count": 1})
            if hasattr(run, 'tool_path') and run.tool_path:
                correlations["tool_path"].append({"value": run.tool_path, "count": 1})
            if hasattr(run, 'retrieval_profile') and run.retrieval_profile:
                correlations["retrieval_profile"].append({"value": run.retrieval_profile, "count": 1})

        if failed_runs:
            all_hypotheses = self._generate_hypotheses(failed_runs, failure_type_counts)

        top_failure_types = [
            {"type": ft, "count": count}
            for ft, count in failure_type_counts.most_common(10)
        ]

        recommendations = self._generate_recommendations(all_hypotheses, top_failure_types)

        return RootCauseReport(
            workspace_id=workspace_id,
            agent_name=agent_name,
            window_days=window_days,
            total_failures=len(failed_runs),
            sample_size=len(failed_runs),
            automatic_classification_count=len([ft for ft, c in failure_type_counts.items() if c >= min_occurrences]),
            hypotheses=all_hypotheses,
            top_failure_types=top_failure_types,
            failure_summary=failure_summary,
            correlations=correlations,
            recommendations=recommendations,
        )

    def _parse_timestamp(self, value: str | None) -> datetime | None:
        """Parse ISO timestamp."""
        if not value:
            return None
        try:
            return datetime.fromisoformat(value)
        except ValueError:
            return None

    def _generate_hypotheses(
        self,
        failed_runs: list[Any],
        failure_type_counts: Counter,
    ) -> list[RootCauseHypothesis]:
        """Generate root cause hypotheses."""
        hypotheses = []
        
        if failure_type_counts.get("bad_retrieval", 0) > 5:
            hypotheses.append(RootCauseHypothesis(
                id=uuid4().hex,
                description="Retrieval quality issues causing incorrect answers",
                evidence=["Multiple runs with bad_retrieval failure type"],
                confidence=min(1.0, failure_type_counts["bad_retrieval"] / 20.0),
                affected_runs=[run.id for run in failed_runs[:10]],
                suggested_actions=[
                    "Increase retrieval limit",
                    "Improve query formulation",
                    "Add better document chunking",
                ],
            ))
        
        if failure_type_counts.get("hallucination", 0) > 3:
            hypotheses.append(RootCauseHypothesis(
                id=uuid4().hex,
                description="Model generating ungrounded information",
                evidence=["Multiple runs with hallucination failure type"],
                confidence=min(1.0, failure_type_counts["hallucination"] / 15.0),
                affected_runs=[run.id for run in failed_runs[:10]],
                suggested_actions=[
                    "Add grounding requirements to prompt",
                    "Use RAG with strict citation requirements",
                    "Add fact-checking step",
                ],
            ))
        
        return hypotheses

    def _generate_recommendations(
        self,
        hypotheses: list[RootCauseHypothesis],
        top_failure_types: list[dict[str, Any]],
    ) -> list[str]:
        """Generate recommendations based on analysis."""
        recommendations = []
        
        if not hypotheses and not top_failure_types:
            return ["No significant issues detected in this time window"]
        
        for hypothesis in hypotheses[:3]:
            recommendations.extend(hypothesis.suggested_actions)
        
        if top_failure_types:
            most_common = top_failure_types[0]["type"]
            recommendations.append(f"Prioritize fixing '{most_common}' failures ({top_failure_types[0]['count']} occurrences)")
        
        return recommendations


__all__ = ["PatternMiner", "RootCauseAnalyzer"]
