"""JiraAgent — wrapper around Agno JiraTools for AntiQA.

Reads credentials from antiqa.json config (env var names) and
environment variables, then initialises the Agno JiraTools toolkit.

Also provides direct access to the underlying `jira.JIRA` client
for operations Agno doesn't expose (like downloading attachments).
"""

from __future__ import annotations

import json
import logging
import os
from typing import Any

from agno.tools.jira import JiraTools
from jira import JIRA

from antiqa.config.schema import AntiQAConfig, JiraConfig

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


class JiraAgent:
    """Thin facade over Agno JiraTools + raw JIRA client."""

    def __init__(self, config: AntiQAConfig) -> None:
        self.config = config
        jira_cfg = config.jira

        server_url = jira_cfg.base_url
        username = jira_cfg.email
        token = jira_cfg.api_token or os.environ.get(jira_cfg.api_key_env, "")

        if not token:
            raise RuntimeError(
                "Jira API token not found. Run 'antiqa init' to set it up."
            )

        self.tools = JiraTools(
            server_url=server_url,
            username=username,
            token=token,
            enable_get_issue=True,
            enable_create_issue=True,
            enable_search_issues=True,
            enable_add_comment=True,
        )

        self.client: JIRA = self.tools.jira
        self.server_url = server_url
        self.project_key = jira_cfg.project_key
        self._issue_types: dict[str, str] | None = None

        log.info("Jira connected to %s (project %s)", server_url, jira_cfg.project_key)

    def _get_issue_types(self) -> dict[str, str]:
        """Fetch available issue types for the project. Cached after first call."""
        if self._issue_types is not None:
            return self._issue_types
        try:
            meta = self.client.createmeta(
                projectKeys=self.project_key,
                expand="projects.issuetypes",
            )
            types: dict[str, str] = {}
            for proj in meta.get("projects", []):
                for it in proj.get("issuetypes", []):
                    types[it["name"].lower()] = it["name"]
            self._issue_types = types
            log.debug("Available issue types: %s", list(types.values()))
        except Exception as exc:
            log.debug("Could not fetch issue types: %s", exc)
            self._issue_types = {}
        return self._issue_types

    def _resolve_issue_type(self, requested: str) -> str:
        """Find the best matching issue type name for this project."""
        types = self._get_issue_types()
        if not types:
            return requested

        lower = requested.lower()
        if lower in types:
            return types[lower]

        # Common aliases
        aliases = {
            "bug": ["bug", "defect", "fehler"],
            "sub-task": ["sub-task", "subtask", "sub task"],
            "task": ["task", "aufgabe"],
            "story": ["story", "user story"],
        }
        for canonical, names in aliases.items():
            if lower in names:
                for name in names:
                    if name in types:
                        return types[name]

        # Last resort: pick the first available type
        if types:
            fallback = list(types.values())[0]
            log.warning("Issue type '%s' not found, using '%s'", requested, fallback)
            return fallback

        return requested

    # ── High-level helpers ─────────────────────────────

    def get_issue(self, issue_key: str) -> dict[str, Any]:
        """Fetch a Jira issue and return parsed dict."""
        try:
            raw = self.tools.get_issue(issue_key)
            return json.loads(raw)
        except Exception as exc:
            error_str = str(exc)
            if "404" in error_str:
                if "AUTHENTICATED_FAILED" in error_str:
                    return {"error": f"Authentication failed. Check your Jira email and API token in antiqa.json."}
                return {"error": f"Ticket '{issue_key}' not found. Check the issue key and your permissions."}
            if "401" in error_str or "403" in error_str:
                return {"error": "Jira authentication failed. Run 'antiqa init' to update your credentials."}
            return {"error": f"Jira error: {error_str[:200]}"}

    def get_issue_full(self, issue_key: str) -> Any:
        """Fetch the full JIRA Issue object (includes attachments, comments, etc)."""
        return self.client.issue(issue_key)

    def add_comment(self, issue_key: str, comment: str) -> dict[str, Any]:
        """Post a comment on a Jira issue."""
        raw = self.tools.add_comment(issue_key, comment)
        return json.loads(raw)

    def create_issue(
        self,
        summary: str,
        description: str,
        issuetype: str = "Bug",
        parent_key: str | None = None,
        labels: list[str] | None = None,
        priority: str | None = None,
    ) -> dict[str, Any]:
        """Create a Jira issue (bug, sub-task, etc).

        For sub-tasks, pass *parent_key* to link to the parent issue.
        """
        resolved_type = self._resolve_issue_type(issuetype)
        fields: dict[str, Any] = {
            "project": {"key": self.project_key},
            "summary": summary,
            "description": description,
            "issuetype": {"name": resolved_type},
        }
        if parent_key:
            subtask_type = self._resolve_issue_type("Sub-task")
            fields["parent"] = {"key": parent_key}
            fields["issuetype"] = {"name": subtask_type}
        if labels:
            fields["labels"] = labels
        if priority:
            fields["priority"] = {"name": priority}

        try:
            new_issue = self.client.create_issue(fields=fields)
            issue_url = f"{self.server_url}/browse/{new_issue.key}"
            log.info("Created issue %s: %s", new_issue.key, summary)
            return {"key": new_issue.key, "url": issue_url}
        except Exception as exc:
            error_text = str(exc)
            # Extract clean error message from Jira response
            if "response text" in error_text:
                try:
                    import re
                    match = re.search(r'response text = ({.*})', error_text)
                    if match:
                        err_data = json.loads(match.group(1))
                        msgs = err_data.get("errorMessages", [])
                        errs = err_data.get("errors", {})
                        error_text = "; ".join(msgs + [f"{k}: {v}" for k, v in errs.items()])
                except Exception:
                    error_text = error_text[:200]
            log.error("Failed to create issue: %s", error_text)
            return {"error": error_text}

    def attach_file(self, issue_key: str, file_path: str, filename: str | None = None) -> bool:
        """Attach a file (screenshot, log, etc) to a Jira issue."""
        try:
            self.client.add_attachment(
                issue=issue_key,
                attachment=file_path,
                filename=filename,
            )
            log.info("Attached %s to %s", filename or file_path, issue_key)
            return True
        except Exception as exc:
            log.error("Failed to attach file to %s: %s", issue_key, exc)
            return False

    def get_attachments(self, issue_key: str) -> list[dict[str, str]]:
        """Return metadata for all attachments on an issue."""
        issue = self.get_issue_full(issue_key)
        attachments = []
        for att in issue.fields.attachment:
            attachments.append({
                "id": att.id,
                "filename": att.filename,
                "content_url": att.content,
                "mime_type": getattr(att, "mimeType", ""),
                "size": str(getattr(att, "size", 0)),
            })
        return attachments
