"""Export Jira tickets to markdown or JSON."""

import argparse
import json
import re
import sys
from dataclasses import asdict
from datetime import datetime
from pathlib import Path
from typing import Any

from zaira.config import TICKETS_DIR, find_project_root
from zaira.jira_client import get_jira, get_jira_site
from zaira.boards import get_board_issues_jql, get_sprint_issues_jql
from zaira.types import Comment, Ticket, get_user_identifier, yaml_quote


def normalize_title(title: str) -> str:
    """Convert title to filename-safe slug."""
    slug = title.lower()
    slug = re.sub(r"[^a-z0-9]+", "-", slug)
    slug = re.sub(r"-+", "-", slug)
    slug = slug.strip("-")
    if len(slug) > 50:
        slug = slug[:50].rsplit("-", 1)[0]
    return slug


def extract_description(desc: dict | str | list | Any | None) -> str:
    """Extract plain text from Atlassian Document Format."""
    if not desc:
        return "No description"
    if isinstance(desc, str):
        return desc

    def extract_text(node) -> str:
        if isinstance(node, str):
            return node
        if isinstance(node, dict):
            if node.get("type") == "text":
                return node.get("text", "")
            if node.get("type") == "hardBreak":
                return "\n"
            if node.get("type") == "inlineCard":
                return node.get("attrs", {}).get("url", "")
            content = node.get("content", [])
            return "".join(extract_text(c) for c in content)
        if isinstance(node, list):
            return "".join(extract_text(c) for c in node)
        return ""

    return extract_text(desc).strip()


def get_ticket(key: str, full: bool = False) -> Ticket | None:
    """Fetch ticket details. If full=True, include more fields for JSON export."""
    jira = get_jira()
    try:
        issue = jira.issue(key, expand="renderedFields")
        fields = issue.fields

        # Handle description - could be ADF or string
        desc = fields.description
        if hasattr(desc, "__dict__"):
            # It's an ADF object, convert to dict
            desc = desc.raw if hasattr(desc, "raw") else None

        ticket = {
            "key": issue.key,
            "summary": fields.summary or "No summary",
            "issuetype": fields.issuetype.name if fields.issuetype else "Unknown",
            "status": fields.status.name if fields.status else "Unknown",
            "priority": fields.priority.name if fields.priority else "None",
            "assignee": get_user_identifier(fields.assignee) or "Unassigned",
            "reporter": get_user_identifier(fields.reporter) or "Unknown",
            "created": fields.created or "Unknown",
            "updated": fields.updated or "Unknown",
            "description": extract_description(desc),
            "components": [c.name for c in (fields.components or [])],
            "labels": fields.labels or [],
            "parent": {
                "key": fields.parent.key,
                "summary": fields.parent.fields.summary,
            }
            if hasattr(fields, "parent") and fields.parent
            else None,
            "issuelinks": [
                {
                    "type": link.type.name,
                    "direction": "outward"
                    if hasattr(link, "outwardIssue")
                    else "inward",
                    "key": (
                        link.outwardIssue.key
                        if hasattr(link, "outwardIssue")
                        else link.inwardIssue.key
                    ),
                    "summary": (
                        link.outwardIssue.fields.summary
                        if hasattr(link, "outwardIssue")
                        else link.inwardIssue.fields.summary
                    ),
                }
                for link in (fields.issuelinks or [])
            ],
        }

        # Add extra fields for JSON export
        if full:
            ticket["project"] = fields.project.key if fields.project else None
            ticket["resolution"] = fields.resolution.name if fields.resolution else None
            ticket["resolutiondate"] = (
                fields.resolutiondate if hasattr(fields, "resolutiondate") else None
            )
            ticket["statusCategory"] = (
                fields.status.statusCategory.name
                if fields.status and fields.status.statusCategory
                else None
            )
            ticket["fixVersions"] = [v.name for v in (fields.fixVersions or [])]
            ticket["versions"] = [v.name for v in (fields.versions or [])]
            ticket["votes"] = fields.votes.votes if fields.votes else 0
            ticket["watches"] = fields.watches.watchCount if fields.watches else 0
            ticket["subtasks"] = [
                {
                    "key": st.key,
                    "summary": st.fields.summary,
                    "status": st.fields.status.name,
                }
                for st in (fields.subtasks or [])
            ]
            ticket["assigneeDisplayName"] = (
                fields.assignee.displayName if fields.assignee else None
            )
            ticket["reporterDisplayName"] = (
                fields.reporter.displayName if fields.reporter else None
            )
            ticket["creator"] = get_user_identifier(fields.creator)
            ticket["creatorDisplayName"] = (
                fields.creator.displayName if fields.creator else None
            )

        return ticket
    except Exception as e:
        print(f"  Error fetching {key}: {e}")
        return None


def get_comments(key: str) -> list[Comment]:
    """Fetch ticket comments."""
    jira = get_jira()
    try:
        issue = jira.issue(key, fields="comment")
        comments = issue.fields.comment.comments if issue.fields.comment else []
        result: list[Comment] = []
        for c in comments:
            body = c.body
            if hasattr(body, "raw"):
                body = extract_description(body.raw)
            elif hasattr(body, "__dict__"):
                body = extract_description(body.__dict__)
            result.append(
                Comment(
                    author=c.author.displayName if c.author else "Unknown",
                    created=c.created or "",
                    body=body if isinstance(body, str) else str(body),
                )
            )
        return result
    except Exception:
        return []


def search_tickets(jql: str) -> list[str]:
    """Search for tickets and return list of keys."""
    jira = get_jira()
    try:
        issues = jira.search_issues(jql, maxResults=False)
        return [issue.key for issue in issues]
    except Exception as e:
        print(f"Error searching: {e}")
        return []


def export_ticket(key: str, output_dir: Path, fmt: str = "md") -> bool:
    """Export a single ticket to markdown or JSON."""
    print(f"Exporting {key}...")

    ticket = get_ticket(key, full=(fmt == "json"))
    if not ticket:
        print(f"  Error: Could not fetch {key}")
        return False

    comments = get_comments(key)
    synced = datetime.now().isoformat(timespec="seconds")
    jira_site = get_jira_site()

    # Extract fields
    summary = ticket.get("summary", "No summary")
    parent_data = ticket.get("parent")

    ext = "json" if fmt == "json" else "md"
    filename = f"{key}-{normalize_title(summary)}.{ext}"

    output_dir.mkdir(parents=True, exist_ok=True)
    outfile = output_dir / filename

    if fmt == "json":
        # JSON output
        data = {
            **ticket,
            "comments": [asdict(c) for c in comments],
            "synced": synced,
            "url": f"https://{jira_site}/browse/{key}",
        }
        outfile.write_text(json.dumps(data, indent=2))
    else:
        # Markdown output
        issue_type = ticket.get("issuetype", "Unknown")
        status = ticket.get("status", "Unknown")
        priority = ticket.get("priority", "None")
        assignee = ticket.get("assignee", "Unassigned")
        reporter = ticket.get("reporter", "Unknown")
        description = ticket.get("description", "No description") or "No description"
        components = ", ".join(ticket.get("components", [])) or "None"
        labels = ", ".join(ticket.get("labels", [])) or "None"
        parent = parent_data["key"] if parent_data else "None"

        md = f"""---
key: {key}
summary: {yaml_quote(summary)}
type: {yaml_quote(issue_type)}
status: {yaml_quote(status)}
priority: {yaml_quote(priority)}
assignee: {yaml_quote(assignee)}
reporter: {yaml_quote(reporter)}
components: {yaml_quote(components)}
labels: {yaml_quote(labels)}
parent: {parent}
synced: {synced}
url: https://{jira_site}/browse/{key}
---

# {key}: {summary}

## Description

{description}

## Links

"""
        issuelinks = ticket.get("issuelinks", [])
        if issuelinks:
            for link in issuelinks:
                link_type = link.get("type", "Related")
                direction = link.get("direction", "outward")
                link_key = link.get("key", "")
                link_summary = link.get("summary", "")
                dir_label = "" if direction == "outward" else " (inward)"
                md += f"- {link_type}{dir_label}: {link_key} - {link_summary}\n"
        else:
            md += "_No links_\n"

        md += """
## Comments

"""
        if comments:
            for c in comments:
                md += f"### {c.author} ({c.created})\n\n{c.body}\n\n"
        else:
            md += "_No comments_\n"

        outfile.write_text(md)

    print(f"  Saved to {outfile}")

    # Create symlinks (only for markdown)
    if fmt == "md":
        # Create symlinks by component
        for comp in ticket.get("components", []):
            if comp:
                comp_dir = output_dir / "by-component" / comp.lower().replace(" ", "-")
                comp_dir.mkdir(parents=True, exist_ok=True)
                link = comp_dir / filename
                link.unlink(missing_ok=True)
                link.symlink_to(f"../../{filename}")

        # Create symlinks by parent
        if parent_data:
            parent_dirname = (
                f"{parent_data['key']}-{normalize_title(parent_data['summary'])}"
            )
            parent_dir = output_dir / "by-parent" / parent_dirname
            parent_dir.mkdir(parents=True, exist_ok=True)
            link = parent_dir / filename
            link.unlink(missing_ok=True)
            link.symlink_to(f"../../{filename}")

    return True


def export_to_stdout(key: str, fmt: str = "md") -> bool:
    """Export a single ticket to stdout."""
    ticket = get_ticket(key, full=(fmt == "json"))
    if not ticket:
        print(f"Error: Could not fetch {key}", file=sys.stderr)
        return False

    comments = get_comments(key)
    synced = datetime.now().isoformat(timespec="seconds")
    jira_site = get_jira_site()

    if fmt == "json":
        data = {
            **ticket,
            "comments": [asdict(c) for c in comments],
            "synced": synced,
            "url": f"https://{jira_site}/browse/{key}",
        }
        print(json.dumps(data, indent=2))
    else:
        # Markdown output
        summary = ticket.get("summary", "No summary")
        issue_type = ticket.get("issuetype", "Unknown")
        status = ticket.get("status", "Unknown")
        priority = ticket.get("priority", "None")
        assignee = ticket.get("assignee", "Unassigned")
        reporter = ticket.get("reporter", "Unknown")
        description = ticket.get("description", "No description") or "No description"
        components = ", ".join(ticket.get("components", [])) or "None"
        labels = ", ".join(ticket.get("labels", [])) or "None"
        parent_data = ticket.get("parent")
        parent = parent_data["key"] if parent_data else "None"

        md = f"""---
key: {key}
summary: {yaml_quote(summary)}
type: {yaml_quote(issue_type)}
status: {yaml_quote(status)}
priority: {yaml_quote(priority)}
assignee: {yaml_quote(assignee)}
reporter: {yaml_quote(reporter)}
components: {yaml_quote(components)}
labels: {yaml_quote(labels)}
parent: {parent}
synced: {synced}
url: https://{jira_site}/browse/{key}
---

# {key}: {summary}

## Description

{description}

## Links

"""
        issuelinks = ticket.get("issuelinks", [])
        if issuelinks:
            for link in issuelinks:
                link_type = link.get("type", "Related")
                direction = link.get("direction", "outward")
                link_key = link.get("key", "")
                link_summary = link.get("summary", "")
                dir_label = "" if direction == "outward" else " (inward)"
                md += f"- {link_type}{dir_label}: {link_key} - {link_summary}\n"
        else:
            md += "_No links_\n"

        md += """
## Comments

"""
        if comments:
            for c in comments:
                md += f"### {c.author} ({c.created})\n\n{c.body}\n\n"
        else:
            md += "_No comments_\n"

        print(md)

    return True


def export_command(args: argparse.Namespace) -> None:
    """Handle export subcommand."""
    fmt = getattr(args, "format", "md")

    # Default to stdout if no zproject.toml found, otherwise files
    has_project = find_project_root() is not None
    if args.output == "-":
        to_stdout = True
    elif args.output:
        to_stdout = False
    else:
        to_stdout = not has_project

    tickets = list(args.tickets)

    # Build JQL from options
    jql = args.jql
    if args.board:
        jql = get_board_issues_jql(args.board)
        if not to_stdout:
            print(f"Using board {args.board}")
    elif args.sprint:
        jql = get_sprint_issues_jql(args.sprint)
        if not to_stdout:
            print(f"Using sprint {args.sprint}")

    if jql:
        if not to_stdout:
            print(f"Searching: {jql}")
        found = search_tickets(jql)
        if not to_stdout:
            print(f"Found {len(found)} tickets")
        tickets.extend(found)

    if not tickets:
        print("No tickets specified. Use ticket keys, --jql, --board, or --sprint.")
        sys.exit(1)

    if to_stdout:
        for key in tickets:
            export_to_stdout(key, fmt=fmt)
    else:
        output_dir = Path(args.output) if args.output else TICKETS_DIR
        success = 0
        for key in tickets:
            if export_ticket(key, output_dir, fmt=fmt):
                success += 1
        print(f"\nExported {success}/{len(tickets)} tickets to {output_dir}/")
