"""FailureAnalyzerAgent — AI-powered bug report generation from test failures.

Analyses test execution results and produces structured bug reports
ready to be filed as Jira sub-tasks via the BugReporter.
"""

from __future__ import annotations

import json
import logging
from pathlib import Path
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, StepResult

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

_PROMPT_PATH = Path(__file__).parent / "prompts" / "analyze_failure.md"


class BugReport(BaseModel):
    """Structured bug report generated by AI analysis."""
    summary: str = ""
    description: str = ""
    severity: str = "major"
    steps_to_reproduce: list[str] = Field(default_factory=list)
    expected_result: str = ""
    actual_result: str = ""
    affected_area: str = ""
    probable_fix: str = ""
    user_impact: str = ""
    regression_test: str = ""
    environment: str = ""


class FailureAnalyzerAgent:
    """Analyse test failures and generate structured bug reports."""

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

    def analyze_step_failure(
        self,
        flow_name: str,
        failed_step: StepResult,
        all_steps: list[StepResult],
        page_url: str = "",
    ) -> BugReport:
        """Generate a BugReport for a single failed step."""
        user_prompt = self._build_step_prompt(flow_name, failed_step, all_steps, page_url)
        return self._run(user_prompt, flow_name)

    def analyze_flow_result(self, flow_result: FlowResult) -> list[BugReport]:
        """Generate BugReports for all failed steps in a FlowResult."""
        reports: list[BugReport] = []

        for failed_step in flow_result.failed_steps:
            try:
                report = self.analyze_step_failure(
                    flow_name=flow_result.flow_name,
                    failed_step=failed_step,
                    all_steps=flow_result.steps,
                )
                reports.append(report)
                log.info("Bug report generated for step %d of %s", failed_step.step_number, flow_result.flow_name)
            except Exception as exc:
                log.error("Failed to analyse step %d: %s", failed_step.step_number, exc)
                reports.append(self._fallback_report(flow_result.flow_name, failed_step))

        return reports

    def severity_to_priority(self, severity: str) -> str:
        """Map AI severity to Jira priority."""
        mapping = {
            "critical": "Highest",
            "major": "High",
            "minor": "Medium",
        }
        return mapping.get(severity.lower(), "Medium")

    # ── Internal ───────────────────────────────────────

    def _run(self, user_prompt: str, context_name: str) -> BugReport:
        agent = Agent(
            model=self.model,
            instructions=[self._system_prompt],
            markdown=False,
        )

        response = agent.run(user_prompt)
        content = self._clean(response.content or "{}")

        try:
            data = json.loads(content)
            return BugReport(
                summary=data.get("summary", ""),
                description=data.get("description", ""),
                severity=data.get("severity", "major"),
                steps_to_reproduce=data.get("steps_to_reproduce", []),
                expected_result=data.get("expected_result", ""),
                actual_result=data.get("actual_result", ""),
                affected_area=data.get("affected_area", ""),
                probable_fix=data.get("probable_fix", ""),
                user_impact=data.get("user_impact", ""),
                regression_test=data.get("regression_test", ""),
                environment=data.get("environment", ""),
            )
        except (json.JSONDecodeError, Exception) as exc:
            log.warning("AI analysis failed for %s: %s", context_name, exc)
            return BugReport(summary=f"Test failure in {context_name}", severity="major")

    def _build_step_prompt(
        self,
        flow_name: str,
        failed_step: StepResult,
        all_steps: list[StepResult],
        page_url: str,
    ) -> str:
        ar = failed_step.action_result

        preceding_steps = [
            f"  {s.step_number}. [{s.action}] {s.description} — {'PASS' if s.passed else 'FAIL'}"
            for s in all_steps
            if s.step_number <= failed_step.step_number
        ]

        parts = [
            f"## Test Failure Details\n",
            f"**Flow:** {flow_name}",
            f"**Failed Step:** #{failed_step.step_number} — {failed_step.description}",
            f"**Action:** {failed_step.action}",
            f"**Error:** {ar.error or 'Unknown error'}",
            f"**Duration:** {ar.duration_ms:.0f}ms",
        ]

        if page_url:
            parts.append(f"**Page URL:** {page_url}")

        if ar.details:
            parts.append(f"**Action details:** {json.dumps(ar.details)}")

        if ar.screenshot_path:
            parts.append(f"**Screenshot:** {ar.screenshot_path}")

        parts.append(f"\n**Execution history:**")
        parts.extend(preceding_steps)

        parts.append(f"\n**App URL:** {self.config.app_url}")

        return "\n".join(parts)

    @staticmethod
    def _fallback_report(flow_name: str, failed_step: StepResult) -> BugReport:
        ar = failed_step.action_result
        return BugReport(
            summary=f"[{flow_name}] Step {failed_step.step_number} failed: {failed_step.action}",
            description=f"Action '{failed_step.action}' failed with error: {ar.error}",
            severity="major",
            steps_to_reproduce=[f"Run flow '{flow_name}' and observe step {failed_step.step_number}"],
            expected_result=f"Step {failed_step.step_number} ({failed_step.description}) should succeed",
            actual_result=ar.error or "Step failed",
            affected_area=flow_name,
        )

    @staticmethod
    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()
