"""Jira data agent — A2A server using pydantic-ai with live JQL queries."""
import os
from functools import lru_cache
from pathlib import Path

from jira import JIRA, JIRAError
from loguru import logger as _log
from pydantic_ai import RunContext

from backend.chanakya.a2a_context import RequestContext, make_metadata_aware_app
from backend.chanakya.base import EnterpriseAgent
from backend.chanakya.config import AGENT_REGISTRY
from pylogue.shell import app_factory as create_core_app

_cfg = AGENT_REGISTRY["jira"]


@lru_cache(maxsize=1)
def _jira_client() -> JIRA:
    """Create and cache a JIRA client using env credentials."""
    return JIRA(
        server=os.environ["JIRA_SERVER"],
        basic_auth=(os.environ["JIRA_EMAIL"], os.environ["JIRA_TOKEN"]),
    )


def _fmt_issue(issue) -> str:
    f = issue.fields
    return (
        f"[{issue.key}] {f.status.name} | {f.priority.name if f.priority else 'None'} | {f.summary}\n"
        f"Type: {f.issuetype.name} | "
        f"Assignee: {getattr(f.assignee, 'displayName', 'Unassigned')} | "
        f"Reporter: {getattr(f.reporter, 'displayName', 'Unknown')}\n"
        f"Created: {f.created[:10]} | Updated: {f.updated[:10]}\n"
        f"Labels: {', '.join(f.labels) if f.labels else 'none'}"
    )


jira_agent = EnterpriseAgent(
    api_base=os.environ["LITELLM_PROVIDER_BASE_URL"],
    instructions=(
        "You are a Jira data agent. You query live Jira data using JQL. "
        "Always cite the issue key and status in your answers. "
        "Use search_jql for complex or ad-hoc JQL queries, search_tickets for keyword search, "
        "get_ticket for a specific issue, and filter_tickets for status/priority/label filtering. "
        "If a tool returns no results, respond with a clear 'nothing found' message."
    ),
    logfire_env="chanakya-alpha",
    service_name="jira",
    deps_type=RequestContext,
)


@jira_agent.tool
def search_jql(ctx: RunContext[RequestContext], jql: str, max_results: int = 20) -> str:
    """Execute a raw JQL query against Jira and return matching issues.

    Args:
        jql: A valid JQL query string, e.g.
             'project = TS AND status = "In Progress" ORDER BY updated DESC'.
        max_results: Maximum number of results to return (default 20, capped at 50).
    """
    _log.debug("search_jql caller={} tenant={}", ctx.deps.user_email, ctx.deps.tenant)
    try:
        issues = _jira_client().search_issues(jql, maxResults=min(max_results, 50))
    except JIRAError as e:
        return f"JQL error: {e.text}"
    if not issues:
        return f"No issues found for JQL: {jql}"
    return "\n---\n".join(_fmt_issue(i) for i in issues)


@jira_agent.tool
def search_tickets(ctx: RunContext[RequestContext], query: str, project: str = "", max_results: int = 20) -> str:
    """Search Jira issues by keyword using full-text JQL search.

    Args:
        query: Keyword or phrase to search across summary, description, and comments.
        project: Optional project key to scope the search, e.g. 'TS'.
        max_results: Maximum number of results to return (default 20).
    """
    _log.debug("search_tickets caller={} query={!r}", ctx.deps.user_email, query)
    jql = f'text ~ "{query}"'
    if project:
        jql = f"project = {project} AND {jql}"
    jql += " ORDER BY updated DESC"
    try:
        issues = _jira_client().search_issues(jql, maxResults=min(max_results, 50))
    except JIRAError as e:
        return f"Search error: {e.text}"
    if not issues:
        return f"No tickets found matching '{query}'."
    return "\n---\n".join(_fmt_issue(i) for i in issues)


@jira_agent.tool
def get_ticket(ctx: RunContext[RequestContext], ticket_id: str) -> str:
    """Retrieve full details of a specific Jira issue.

    Args:
        ticket_id: Issue key, e.g. 'TS-101'.
    """
    _log.debug("get_ticket caller={} ticket={}", ctx.deps.user_email, ticket_id)
    try:
        issue = _jira_client().issue(ticket_id)
    except JIRAError as e:
        return f"Ticket '{ticket_id}' not found: {e.text}"
    f = issue.fields
    description = getattr(f, "description", None) or "(no description)"
    return (
        f"Key: {issue.key} | Type: {f.issuetype.name} | Status: {f.status.name} | "
        f"Priority: {f.priority.name if f.priority else 'None'}\n"
        f"Summary: {f.summary}\n"
        f"Assignee: {getattr(f.assignee, 'displayName', 'Unassigned')} | "
        f"Reporter: {getattr(f.reporter, 'displayName', 'Unknown')}\n"
        f"Created: {f.created[:10]} | Updated: {f.updated[:10]}\n"
        f"Labels: {', '.join(f.labels) if f.labels else 'none'}\n\n"
        f"{description}"
    )


@jira_agent.tool
def filter_tickets(
    ctx: RunContext[RequestContext],
    status: str = "",
    priority: str = "",
    label: str = "",
    project: str = "",
    assignee: str = "",
    max_results: int = 20,
) -> str:
    """Filter Jira issues by status, priority, label, project, or assignee using JQL.

    Args:
        status: Filter by status, e.g. 'Open', 'In Progress', 'Done', 'To Do'.
        priority: Filter by priority, e.g. 'Critical', 'High', 'Medium', 'Low'.
        label: Filter by label, e.g. 'safety', 'procurement'.
        project: Filter by project key, e.g. 'TS'.
        assignee: Filter by assignee display name or email.
        max_results: Maximum number of results to return (default 20).
    """
    clauses = []
    if project:
        clauses.append(f"project = {project}")
    if status:
        clauses.append(f'status = "{status}"')
    if priority:
        clauses.append(f'priority = "{priority}"')
    if label:
        clauses.append(f'labels = "{label}"')
    if assignee:
        clauses.append(f'assignee = "{assignee}"')

    if not clauses:
        return "Please provide at least one filter criterion."

    jql = " AND ".join(clauses) + " ORDER BY updated DESC"
    try:
        issues = _jira_client().search_issues(jql, maxResults=min(max_results, 50))
    except JIRAError as e:
        return f"Filter error: {e.text}"
    if not issues:
        return "No tickets match the given filters."
    return "\n".join(
        f"[{i.key}] {i.fields.status.name} | "
        f"{i.fields.priority.name if i.fields.priority else 'None'} | "
        f"{i.fields.summary}"
        for i in issues
    )


app = make_metadata_aware_app(
    jira_agent,
    name=_cfg.name,
    description=_cfg.description,
)


@app.on_event("startup")
async def _startup_banner() -> None:
    port = os.environ.get("JIRA_AGENT_PORT", "8002")
    _log.info("\n  Jira Agent ready:")
    _log.info("    A2A   → http://0.0.0.0:{}/", port)
    _log.info("    Chat  → http://0.0.0.0:{}/chat", port)
    _log.info("    Card  → http://0.0.0.0:{}/.well-known/agent-card.json", port)


# Mount the pylogue chat UI at /chat on the same ASGI app.
# FastAPI path-strips /chat so pylogue sees / internally.
# Access at: http://localhost:8002/chat
# In Docker, PYLOGUE_DB_PATH=/data/chat_history.db (bind-mounted from ./data/conversation-histories/jira/).
_PROJECT_ROOT = Path(__file__).parents[3]
_jira_db_path = os.environ.get(
    "PYLOGUE_DB_PATH",
    str(_PROJECT_ROOT / "data" / "conversation-histories" / "jira" / "chat_history.db"),
)
Path(_jira_db_path).parent.mkdir(parents=True, exist_ok=True)

app.mount(
    "/chat",
    create_core_app(
        responder_factory=lambda: jira_agent,
        hero_title="Jira Agent",
        hero_subtitle="Search and retrieve Tata Steel Jira tickets.",
        db_path=_jira_db_path,
    ),
)


if __name__ == "__main__":
    import uvicorn
    uvicorn.run(_cfg.module_path, host=_cfg.host, port=_cfg.port, reload=True)
