"""RootCauseAnalyzer — group test failures by probable root cause using AI.

Instead of filing 20 separate bugs for failures that share a common cause,
this agent clusters related failures and produces grouped bug reports.
"""

from __future__ import annotations

import json
import logging
from typing import Any

from agno.agent import Agent
from pydantic import BaseModel, Field

from antiqa.config.models import resolve_model
from antiqa.config.schema import AntiQAConfig
from antiqa.flows.runner import FlowResult

log = logging.getLogger("antiqa.agents")

CLUSTER_PROMPT = """\
You are a QA root cause analysis expert. Given multiple test failures from an automated test run,
group them by their probable root cause.

Rules:
1. Failures with the same error type on the same page/API are likely the same root cause.
2. Failures caused by a broken API endpoint should be grouped together.
3. Selector failures on the same page suggest a UI refactor — group them.
4. Timeout failures on different pages might indicate a server performance issue.
5. Be conservative — only group failures you're confident share a root cause.

Return ONLY valid JSON:
```json
{
  "clusters": [
    {
      "root_cause": "Brief description of the probable root cause",
      "severity": "critical" | "major" | "minor",
      "affected_area": "e.g. Login API, Product Form CSS",
      "failure_indices": [0, 3, 7],
      "suggested_fix": "Brief suggestion for fixing this class of failures",
      "count": 3
    }
  ],
  "unclustered": [2, 5]
}
```

`failure_indices` refer to the 0-based index of each failure in the input list.
`unclustered` lists indices of failures that don't clearly belong to any group.
"""


class FailureCluster(BaseModel):
    root_cause: str = ""
    severity: str = "major"
    affected_area: str = ""
    failure_indices: list[int] = Field(default_factory=list)
    suggested_fix: str = ""
    count: int = 0


class ClusterResult(BaseModel):
    clusters: list[FailureCluster] = Field(default_factory=list)
    unclustered: list[int] = Field(default_factory=list)
    total_failures: int = 0


class RootCauseAnalyzer:
    """Cluster test failures by root cause using AI."""

    def __init__(self, config: AntiQAConfig) -> None:
        self.config = config
        self.model = resolve_model(config.llm)

    def analyze(self, flow_results: list[FlowResult]) -> ClusterResult:
        """Analyze all failures across flow results and cluster them."""
        failures = self._collect_failures(flow_results)
        if not failures:
            return ClusterResult(total_failures=0)

        if len(failures) == 1:
            return ClusterResult(
                clusters=[FailureCluster(
                    root_cause=failures[0].get("error", "Unknown"),
                    severity="major",
                    affected_area=failures[0].get("flow", ""),
                    failure_indices=[0],
                    count=1,
                )],
                total_failures=1,
            )

        user_prompt = self._build_prompt(failures)

        from agno.utils.log import logger as agno_logger
        prev_handlers = agno_logger.handlers[:]
        prev_propagate = agno_logger.propagate
        agno_logger.handlers = [logging.NullHandler()]
        agno_logger.propagate = False
        try:
            agent = Agent(
                model=self.model,
                instructions=[CLUSTER_PROMPT],
                markdown=False,
            )
            response = agent.run(user_prompt)
            content = _clean(response.content or "{}")
            data = json.loads(content)
        except Exception as exc:
            log.warning("Root cause analysis failed: %s", exc)
            return self._fallback_cluster(failures)
        finally:
            agno_logger.handlers = prev_handlers
            agno_logger.propagate = prev_propagate

        clusters = [
            FailureCluster(
                root_cause=c.get("root_cause", "Unknown"),
                severity=c.get("severity", "major"),
                affected_area=c.get("affected_area", ""),
                failure_indices=c.get("failure_indices", []),
                suggested_fix=c.get("suggested_fix", ""),
                count=c.get("count", len(c.get("failure_indices", []))),
            )
            for c in data.get("clusters", [])
        ]

        return ClusterResult(
            clusters=clusters,
            unclustered=data.get("unclustered", []),
            total_failures=len(failures),
        )

    def _collect_failures(self, results: list[FlowResult]) -> list[dict[str, Any]]:
        """Flatten all failures across all flow results."""
        failures: list[dict[str, Any]] = []
        for fr in results:
            for step in fr.failed_steps:
                failures.append({
                    "index": len(failures),
                    "flow": fr.flow_name,
                    "step_number": step.step_number,
                    "action": step.action,
                    "description": step.description,
                    "error": step.action_result.error or "Unknown error",
                    "duration_ms": step.action_result.duration_ms,
                })
            for issue in fr.critical_health_issues:
                failures.append({
                    "index": len(failures),
                    "flow": fr.flow_name,
                    "step_number": 0,
                    "action": f"health:{issue.category.value}",
                    "description": issue.message,
                    "error": issue.message,
                    "url": issue.url,
                })
        return failures

    def _build_prompt(self, failures: list[dict[str, Any]]) -> str:
        parts = [f"## Test Failures ({len(failures)} total)\n"]
        for f in failures:
            parts.append(
                f"[{f['index']}] Flow: {f['flow']} | Step: {f.get('step_number', '?')} | "
                f"Action: {f['action']} | Error: {f['error'][:100]}"
            )
        return "\n".join(parts)

    def _fallback_cluster(self, failures: list[dict[str, Any]]) -> ClusterResult:
        """Group failures by error message similarity when AI is unavailable."""
        groups: dict[str, list[int]] = {}
        for f in failures:
            key = f.get("error", "unknown")[:50]
            groups.setdefault(key, []).append(f["index"])

        clusters = [
            FailureCluster(
                root_cause=key,
                severity="major",
                failure_indices=indices,
                count=len(indices),
            )
            for key, indices in groups.items()
        ]

        return ClusterResult(clusters=clusters, total_failures=len(failures))


def _clean(content: str) -> str:
    content = content.strip()
    if content.startswith("```"):
        first_newline = content.index("\n") if "\n" in content else 3
        content = content[first_newline + 1:]
    if content.endswith("```"):
        content = content[:-3]
    return content.strip()
