#!/usr/bin/env python3
"""
Convert Claude Code conversation from JSONL to MDX format for Suno Wiki.

Usage:
    python convert-claude-chat.py <jsonl-file> [--output <output-file>] [--title <title>]

    # Convert latest session
    python convert-claude-chat.py $(ls -t ~/.claude/projects/*/*.jsonl | head -n 1)

    # Convert with custom title and output
    python convert-claude-chat.py session.jsonl --title "My Conversation" --output my-convo.mdx
"""

import argparse
import json
import re
import sys
from datetime import datetime
from pathlib import Path
from typing import Any, Dict, List, Tuple

# Import our utility modules
from jsonl_utils import (
    parse_jsonl,
    get_session_metadata,
    extract_token_usage,
    extract_model_info,
    calculate_relative_time,
    get_relative_file_path,
    find_tool_use_by_id,
    calculate_total_tokens,
)
from diff_formatter import (
    format_edit_as_diff,
    detect_language_from_path,
)


def format_timestamp(ts_str: str) -> str:
    """Format ISO timestamp to readable format with timezones."""
    from zoneinfo import ZoneInfo

    # Parse UTC timestamp
    dt_utc = datetime.fromisoformat(ts_str.replace("Z", "+00:00"))

    # Convert to Eastern and Pacific times
    dt_eastern = dt_utc.astimezone(ZoneInfo("America/New_York"))
    dt_pacific = dt_utc.astimezone(ZoneInfo("America/Los_Angeles"))

    # Format with timezone abbreviations
    eastern_str = dt_eastern.strftime("%Y-%m-%d %I:%M %p %Z")
    pacific_str = dt_pacific.strftime("%I:%M %p %Z")

    return f"{eastern_str} / {pacific_str}"


def escape_jsx_and_imports(text: str) -> str:
    """
    Escape JSX-like syntax and import statements for MDX.
    Handles curly braces, angle brackets, and import/export keywords.
    """
    # Escape curly braces
    text = text.replace("{", "\\{").replace("}", "\\}")

    # Escape self-closing tags as complete units
    # Match <ComponentName attr="value" /> and escape the whole thing
    text = re.sub(r"<(\w+)([^>]*?)/>", r"\\<\1\2/\\>", text)

    # Now escape remaining angle brackets (but not ones we just escaped)
    # We need to be careful not to double-escape
    # First, temporarily mark our escaped tags
    text = text.replace("\\<", "\x00ESCAPED_LT\x00")
    text = text.replace("/\\>", "\x00ESCAPED_SELFCLOSE\x00")

    # Now escape remaining unescaped brackets
    text = text.replace("<", "\\<").replace(">", "\\>")

    # Restore our escaped tags
    text = text.replace("\x00ESCAPED_LT\x00", "\\<")
    text = text.replace("\x00ESCAPED_SELFCLOSE\x00", "/\\>")

    # Escape import/export statements by escaping the keywords
    # This prevents them from being parsed as actual JS imports
    text = re.sub(r"\b(import|export)\s", r"\\\1 ", text)

    return text


def escape_mdx(text: str) -> str:
    """Escape text for safe MDX rendering."""
    # Escape curly braces and angle brackets - in MDX they're interpreted as JSX
    # Only preserve triple-backtick code blocks without escaping
    # Inline code (single backticks) still needs escaping
    #
    # Also escape import/export statements that appear outside code blocks,
    # as these will be parsed as JavaScript by the MDX compiler

    # Split by code fence blocks only (triple backticks)
    parts = []
    code_fence_pattern = r"```[\s\S]*?```"

    last_end = 0
    for match in re.finditer(code_fence_pattern, text):
        # Add text before code block (escaped)
        before_text = text[last_end : match.start()]
        before_text = escape_jsx_and_imports(before_text)
        parts.append(before_text)

        # Add code fence block (not escaped)
        parts.append(match.group(0))
        last_end = match.end()

    # Add remaining text (escaped)
    remaining_text = text[last_end:]
    remaining_text = escape_jsx_and_imports(remaining_text)
    parts.append(remaining_text)

    return "".join(parts)


def format_code_block(code: str, language: str = "") -> str:
    """Format code block with language specification."""
    return f"```{language}\n{code}\n```"


def enhance_at_mentions(text: str, cwd: str = "") -> str:
    """
    Enhance @ mentions by formatting them as code references.
    Skips @ symbols in import statements.

    Args:
        text: Text potentially containing @mentions
        cwd: Working directory for relative path conversion

    Returns:
        Text with enhanced @ mentions
    """
    # Don't process import/from statements - they use @ for path aliases
    if text.strip().startswith(("import ", "from ")):
        return text

    # Pattern: @ followed by file path (with or without spaces)
    # Match @path/to/file.ext or @"path with spaces/file.ext"
    def replace_mention(match):
        path = match.group(1).strip("\"'")
        relative_path = get_relative_file_path(path, cwd) if cwd else path
        return f"`@{relative_path}`"

    # Pattern for @mentions
    text = re.sub(r"@([^\s]+(?:\s+[^\s]+)*?\.\w+)", replace_mention, text)

    return text


def format_token_usage(usage: Dict[str, int]) -> str:
    """
    Format token usage as a badge/callout with smaller font.

    Args:
        usage: Token usage dictionary

    Returns:
        Formatted markdown string wrapped in span for smaller font
    """
    input_tokens = usage.get("input_tokens", 0)
    output_tokens = usage.get("output_tokens", 0)
    cache_read = usage.get("cache_read_input_tokens", 0)
    cache_creation = usage.get("cache_creation_input_tokens", 0)

    content = f"**Tokens:** {input_tokens:,} in / {output_tokens:,} out"

    if cache_read > 0 or cache_creation > 0:
        content += f" | **Cache:** {cache_read:,} read"
        if cache_creation > 0:
            content += f", {cache_creation:,} created"

    return f'<div class="token-usage">{content}</div>'


def format_tool_use(
    tool_data: Dict[str, Any],
    all_messages: List[Dict[str, Any]] = None,
    cwd: str = "",
) -> str:
    """
    Format tool use into readable markdown with enhanced formatting.

    Args:
        tool_data: Tool use block
        all_messages: All messages for context
        cwd: Working directory for relative paths

    Returns:
        Formatted markdown string
    """
    tool_name = tool_data.get("name", "Unknown Tool")
    tool_input = tool_data.get("input", {})

    # Special formatting for Edit tool - show as diff
    if tool_name == "Edit":
        return format_edit_as_diff(tool_input)

    # Special formatting for other tools with file paths
    if "file_path" in tool_input and cwd:
        tool_input = tool_input.copy()
        tool_input["file_path"] = get_relative_file_path(tool_input["file_path"], cwd)

    output = f"**🔧 Tool Used: `{tool_name}`**\n\n"

    if tool_input:
        # Pretty print the input as JSON
        input_json = json.dumps(tool_input, indent=2)
        output += format_code_block(input_json, "json")

    return output


def format_tool_result(
    tool_data: Dict[str, Any],
    all_messages: List[Dict[str, Any]] = None,
    cwd: str = "",
) -> str:
    """
    Format tool result with enhanced formatting by tool type.

    Args:
        tool_data: Tool result block
        all_messages: All messages for context
        cwd: Working directory for relative paths

    Returns:
        Formatted markdown string
    """
    tool_id = tool_data.get("tool_use_id", "unknown")
    content = tool_data.get("content", "")
    is_error = tool_data.get("is_error", False)

    # Find corresponding tool use to determine tool type
    tool_use = None
    if all_messages:
        tool_use = find_tool_use_by_id(all_messages, tool_id)

    tool_name = tool_use.get("name") if tool_use else "Unknown"

    # Status indicator
    status_icon = "❌" if is_error else "✅"

    if is_error:
        return f"**{status_icon} Tool Error** (`{tool_name}`)\n\n{format_code_block(str(content), '')}"

    output = f"**{status_icon} Tool Result** (`{tool_name}`)\n\n"

    # Enhanced formatting based on tool type
    if tool_name == "Read":
        # Read results already have line numbers with → character
        output += f"<details>\n<summary>View File Contents</summary>\n\n{format_code_block(content, '')}\n\n</details>"

    elif tool_name == "Bash":
        # Format bash output with command context
        if tool_use:
            command = tool_use.get("input", {}).get("command", "")
            output += f"**Command:** `{command}`\n\n"
        output += f"<details>\n<summary>View Output</summary>\n\n{format_code_block(content, 'bash')}\n\n</details>"

    elif tool_name == "Grep":
        # Grep results with syntax highlighting
        output += f"<details>\n<summary>View Search Results</summary>\n\n{format_code_block(content, '')}\n\n</details>"

    elif tool_name == "Write":
        # Show file created
        if tool_use:
            file_path = tool_use.get("input", {}).get("file_path", "")
            relative_path = get_relative_file_path(file_path, cwd) if cwd else file_path
            language = detect_language_from_path(file_path)
            output = f"**{status_icon} Created File:** `{relative_path}`\n\n"
            if content and len(content) < 1000:
                output += f"<details>\n<summary>View Contents</summary>\n\n{format_code_block(content, language)}\n\n</details>"

    elif tool_name == "Edit":
        # Edit confirmation
        output = f"**{status_icon} Edit Applied Successfully**\n\n"
        if content:
            output += (
                f"<details>\n<summary>Details</summary>\n\n{content}\n\n</details>"
            )

    else:
        # Generic formatting for other tools
        if isinstance(content, str) and (len(content) > 200 or "\n" in content):
            output += f"<details>\n<summary>View Output</summary>\n\n{format_code_block(content, '')}\n\n</details>"
        else:
            output += f"{content}"

    return output


def format_text_content(text: str, cwd: str = "") -> str:
    """Format text content, preserving code blocks and enhancing @ mentions."""
    text = enhance_at_mentions(text, cwd)
    text = escape_mdx(text)
    # Convert any markdown headings to HTML to prevent them from appearing in TOC
    # This handles headings in user messages or assistant responses that aren't in the main heading
    text = re.sub(r"^## (.+)$", r"<h3>\1</h3>", text, flags=re.MULTILINE)
    text = re.sub(r"^### (.+)$", r"<h4>\1</h4>", text, flags=re.MULTILINE)
    text = re.sub(r"^#### (.+)$", r"<h5>\1</h5>", text, flags=re.MULTILINE)
    text = re.sub(r"^##### (.+)$", r"<h6>\1</h6>", text, flags=re.MULTILINE)
    return text


def extract_prompt_summary(content: str, max_length: int = 50) -> str:
    """
    Extract a short summary from user prompt for TOC.
    Takes first sentence or first N characters, whichever is shorter.
    """
    # Strip system tags first
    content = strip_system_tags(content)

    # Remove IDE file opening messages - everything before the backtick close
    if "📁 Opened `" in content:
        parts = content.split("`", 2)
        if len(parts) > 2:
            content = parts[2].strip()
        else:
            content = content.replace("📁 Opened `", "").replace("`", "").strip()

    # Remove markdown formatting
    content = re.sub(r"\*\*(.+?)\*\*", r"\1", content)  # Bold
    content = re.sub(r"\*(.+?)\*", r"\1", content)  # Italic
    content = re.sub(r"`(.+?)`", r"\1", content)  # Code
    content = re.sub(r"\[(.+?)\]\(.+?\)", r"\1", content)  # Links

    # Get first sentence or first N chars
    first_sentence = content.split(".")[0].split("?")[0].split("!")[0].strip()

    if len(first_sentence) > max_length:
        return first_sentence[:max_length].strip() + "..."

    return first_sentence if first_sentence else "User Prompt"


def format_assistant_message(
    content_blocks: List[Dict[str, Any]],
    all_messages: List[Dict[str, Any]] = None,
    cwd: str = "",
) -> str:
    """Format assistant message content blocks."""
    output = []

    for block in content_blocks:
        block_type = block.get("type", "text")

        if block_type == "text":
            text = format_text_content(block.get("text", ""), cwd)
            # Replace markdown headings with HTML to avoid TOC entries
            # Replace all headings (##, ###, ####, etc.) with HTML tags to hide from TOC
            text = re.sub(r"^## (.+)$", r"<h3>\1</h3>", text, flags=re.MULTILINE)
            text = re.sub(r"^### (.+)$", r"<h4>\1</h4>", text, flags=re.MULTILINE)
            text = re.sub(r"^#### (.+)$", r"<h5>\1</h5>", text, flags=re.MULTILINE)
            text = re.sub(r"^##### (.+)$", r"<h6>\1</h6>", text, flags=re.MULTILINE)
            output.append(text)
        elif block_type == "tool_use":
            output.append(format_tool_use(block, all_messages, cwd))
        elif block_type == "tool_result":
            output.append(format_tool_result(block, all_messages, cwd))

    return "\n\n".join(output)


def strip_system_tags(content: str) -> str:
    """Remove system tags like <ide_selection> and <system-reminder> from content."""
    # Remove <ide_selection> tags and their content
    content = re.sub(
        r"<ide_selection>.*?</ide_selection>", "", content, flags=re.DOTALL
    )

    # Remove <system-reminder> tags and their content
    content = re.sub(
        r"<system-reminder>.*?</system-reminder>", "", content, flags=re.DOTALL
    )

    # Remove <command-message> and <command-name> tags and their content
    content = re.sub(
        r"<command-message>.*?</command-message>", "", content, flags=re.DOTALL
    )
    content = re.sub(r"<command-name>.*?</command-name>", "", content, flags=re.DOTALL)
    content = re.sub(r"<command-args>.*?</command-args>", "", content, flags=re.DOTALL)

    # Remove <ide_opened_file> tags and their content
    content = re.sub(
        r"<ide_opened_file>.*?</ide_opened_file>", "", content, flags=re.DOTALL
    )

    # Remove local command output tags (stderr, stdout, etc.)
    content = re.sub(
        r"<local-command-stderr>.*?</local-command-stderr>",
        "",
        content,
        flags=re.DOTALL,
    )
    content = re.sub(
        r"<local-command-stdout>.*?</local-command-stdout>",
        "",
        content,
        flags=re.DOTALL,
    )

    # Remove entire "Caveat" messages about local commands
    content = re.sub(
        r"Caveat: The messages below.*?(?=\n\n|$)", "", content, flags=re.DOTALL
    )

    # Clean up extra whitespace
    content = re.sub(r"\n\n\n+", "\n\n", content)

    return content.strip()


def condense_ide_message(content: str) -> str:
    """Condense IDE file opening messages to a compact format."""
    # Pattern for IDE file opening messages
    ide_pattern = r"<ide_opened_file>The user opened the file (.+?) in the IDE\. This may or may not be related to the current task\.</ide_opened_file>"

    # Replace with condensed format
    match = re.search(ide_pattern, content)
    if match:
        file_path = match.group(1)
        # Extract just the filename or relative path
        if "/" in file_path:
            # Try to get a meaningful relative path (last 2-3 segments)
            parts = file_path.split("/")
            if len(parts) > 3:
                condensed_path = "/".join(parts[-3:])
            else:
                condensed_path = parts[-1]
        else:
            condensed_path = file_path

        # Use backticks for code-style formatting instead of markdown links (MDX-safe)
        return f"📁 Opened `{condensed_path}`"

    return content


def format_user_message(content: Any, cwd: str = "") -> Tuple[str, bool]:
    """
    Format user message content.
    Returns tuple of (formatted_content, is_system_action)
    """
    if isinstance(content, str):
        # Strip system tags first
        content = strip_system_tags(content)

        # Check if this is an IDE file opening message
        if "<ide_opened_file>" in content:
            condensed = condense_ide_message(content)
            # Check if there's additional user input after the IDE message
            remaining = content.split("</ide_opened_file>")[-1].strip()
            if remaining:
                return (condensed + " " + format_text_content(remaining, cwd), False)
            else:
                return (condensed, True)
        return (format_text_content(content, cwd), False)
    elif isinstance(content, list):
        # Handle content blocks if user message has them
        texts = []
        has_tool_results_only = True

        for block in content:
            if isinstance(block, dict):
                if block.get("type") == "text":
                    texts.append(block.get("text", ""))
                    has_tool_results_only = False
                elif block.get("type") != "tool_result":
                    # If there's any non-tool_result, non-text block, it's not a pure system continuation
                    has_tool_results_only = False
            elif isinstance(block, str):
                texts.append(block)
                has_tool_results_only = False

        combined = " ".join(texts)

        # Strip system tags
        combined = strip_system_tags(combined)

        # If message contains only tool_result blocks (no text), treat as system continuation
        if has_tool_results_only and not combined.strip():
            return ("_[System continuation]_", True)

        if "<ide_opened_file>" in combined:
            condensed = condense_ide_message(combined)
            remaining = combined.split("</ide_opened_file>")[-1].strip()
            if remaining:
                return (condensed + " " + format_text_content(remaining, cwd), False)
            else:
                return (condensed, True)
        return (format_text_content(combined, cwd), False)
    return (str(content), False)


def convert_to_mdx(
    messages: List[Dict[str, Any]], title: str, session_id: str = None
) -> str:
    """Convert parsed messages to MDX format with enhanced features."""
    # Filter to main conversation (not sidechains)
    main_messages = [m for m in messages if not m.get("isSidechain", False)]

    # Get session metadata
    metadata = get_session_metadata(messages)
    cwd = metadata.get("cwd", "")
    git_branch = metadata.get("git_branch", "")
    first_timestamp = metadata.get("first_timestamp", "")

    # Format date
    formatted_date = (
        format_timestamp(first_timestamp) if first_timestamp else "Unknown date"
    )

    # Extract repository name from cwd
    repo_name = Path(cwd).name if cwd else "Unknown"

    # Start building MDX with Astro components
    mdx_parts = [
        "---\n",
        f'title: "{title}"\n',
        f"date: {first_timestamp if first_timestamp else datetime.now().isoformat()}\n",
        'desc: "Claude Code conversation"\n',
        f'slug: "{title.lower().replace(" ", "-")}"\n',
        'tags: ["claude-code", "conversation"]\n',
        'category: "prompt"\n',
    ]

    if session_id:
        mdx_parts.append(f'sessionId: "{session_id}"\n')
    if repo_name:
        mdx_parts.append(f'repository: "{repo_name}"\n')
    if git_branch:
        mdx_parts.append(f'branch: "{git_branch}"\n')

    mdx_parts.append("---\n\n")
    mdx_parts.append(
        "import PromptSummary from '@/components/prompts/PromptSummary.astro';\n\n"
    )

    # Track models used
    models_used = set()

    # Process conversation pairs
    conversation_index = 1
    user_prompt_number = 0  # Sequential numbering for user prompts in TOC
    message_index = 0
    i = 0

    while i < len(main_messages):
        msg = main_messages[i]
        msg_type = msg.get("type")

        if msg_type == "user":
            message_index += 1
            user_content = msg.get("message", {}).get("content", "")

            # Add anchor for message threading
            anchor_id = f"message-{message_index}"

            # Calculate relative time
            msg_timestamp = msg.get("timestamp", "")
            relative_time = ""
            if msg_timestamp and first_timestamp:
                relative_time = calculate_relative_time(msg_timestamp, first_timestamp)

            # Format user message and check if it's a system action
            formatted_content, is_system_action = format_user_message(user_content, cwd)

            # Skip completely empty messages
            if not formatted_content.strip():
                # Still look for assistant responses but mark as system continuation
                formatted_content = "_[System continuation]_"
                is_system_action = True

            # Look ahead to see if we have consecutive system continuations
            system_continuation_group = []
            if is_system_action and "[System continuation]" in formatted_content:
                # Collect consecutive system continuations starting from current message
                temp_i = i
                continuation_count = 0

                while temp_i < len(main_messages):
                    temp_msg = main_messages[temp_i]
                    if temp_msg.get("type") != "user":
                        break

                    temp_content, temp_is_system = format_user_message(
                        temp_msg.get("message", {}).get("content", ""), cwd
                    )

                    if (
                        not temp_is_system
                        or "[System continuation]" not in temp_content
                    ):
                        break

                    # Collect this system continuation with its assistant responses
                    system_continuation_group.append(
                        {
                            "index": temp_i,
                            "conversation_index": conversation_index
                            + continuation_count,
                            "timestamp": temp_msg.get("timestamp", ""),
                            "message_index": message_index + continuation_count,
                        }
                    )
                    continuation_count += 1

                    # Skip past assistant messages for this continuation
                    temp_j = temp_i + 1
                    while (
                        temp_j < len(main_messages)
                        and main_messages[temp_j].get("type") == "assistant"
                    ):
                        temp_j += 1
                    temp_i = temp_j

            # If we have multiple system continuations, group them
            if len(system_continuation_group) > 1:
                start_idx = system_continuation_group[0]["conversation_index"]
                end_idx = system_continuation_group[-1]["conversation_index"]

                mdx_parts.append(
                    f'\n<span id="{anchor_id}"></span>\n\n_**Actions {start_idx}-{end_idx}:** System continuations_'
                )
                if relative_time:
                    mdx_parts.append(f" _{relative_time}_")
                mdx_parts.append("\n")

                # Collect all assistant responses from the group
                mdx_parts.append("\n<details>")
                mdx_parts.append("\n<summary>🤖 View Claude's Responses</summary>\n")

                for group_item in system_continuation_group:
                    group_i = group_item["index"]
                    group_j = group_i + 1

                    # Collect assistant messages for this continuation
                    while (
                        group_j < len(main_messages)
                        and main_messages[group_j].get("type") == "assistant"
                    ):
                        asst_msg = main_messages[group_j]

                        # Extract model info
                        model = extract_model_info(asst_msg)
                        if model:
                            models_used.add(model)

                        # Extract token usage
                        usage = extract_token_usage(asst_msg)

                        content = asst_msg.get("message", {}).get("content", [])
                        if isinstance(content, list):
                            formatted = format_assistant_message(
                                content, main_messages, cwd
                            )
                            if formatted.strip():
                                if (
                                    group_item != system_continuation_group[0]
                                    or group_j != group_i + 1
                                ):
                                    mdx_parts.append("\n\n---\n\n")
                                mdx_parts.append(formatted)

                                # Add token usage after assistant response
                                if usage:
                                    mdx_parts.append(f"\n\n{format_token_usage(usage)}")

                        group_j += 1

                mdx_parts.append("\n\n</details>\n")
                mdx_parts.append("\n---\n")

                # Update indices
                conversation_index = end_idx + 1
                i = system_continuation_group[-1]["index"]

                # Skip past the last group's assistant messages
                j = i + 1
                while (
                    j < len(main_messages)
                    and main_messages[j].get("type") == "assistant"
                ):
                    j += 1
                i = j

            else:
                # Single message (system action or regular prompt)
                if is_system_action:
                    # System action - use italic/subdued style
                    mdx_parts.append(
                        f'\n<span id="{anchor_id}"></span>\n\n_**Action {conversation_index}:** {formatted_content}_'
                    )
                    if relative_time:
                        mdx_parts.append(f" _{relative_time}_")
                    mdx_parts.append("\n")
                else:
                    # Regular user prompt - increment prompt number and extract summary for heading
                    user_prompt_number += 1
                    prompt_summary = extract_prompt_summary(formatted_content)
                    mdx_parts.append(
                        f'\n<span id="{anchor_id}"></span>\n\n## {user_prompt_number}. {prompt_summary}'
                    )
                    if relative_time:
                        mdx_parts.append(f" <small>_{relative_time}_</small>")
                    mdx_parts.append(f"\n{formatted_content}\n")

                # Look for corresponding assistant response(s)
                assistant_responses = []
                j = i + 1

                # Collect all assistant messages until next user message
                while (
                    j < len(main_messages)
                    and main_messages[j].get("type") == "assistant"
                ):
                    assistant_responses.append(main_messages[j])
                    j += 1

                # Format assistant responses
                if assistant_responses:
                    mdx_parts.append("\n<details>")
                    mdx_parts.append("\n<summary>🤖 View Claude's Response</summary>\n")

                    for idx, asst_msg in enumerate(assistant_responses):
                        # Extract model info
                        model = extract_model_info(asst_msg)
                        if model:
                            models_used.add(model)

                        # Extract token usage
                        usage = extract_token_usage(asst_msg)

                        content = asst_msg.get("message", {}).get("content", [])
                        if isinstance(content, list):
                            formatted = format_assistant_message(
                                content, main_messages, cwd
                            )
                            if formatted.strip():
                                if idx > 0:
                                    mdx_parts.append("\n\n---\n\n")
                                mdx_parts.append(formatted)

                                # Add token usage after assistant response
                                if usage:
                                    mdx_parts.append(
                                        f"\n\n---\n\n{format_token_usage(usage)}"
                                    )

                    mdx_parts.append("\n\n</details>\n")

                mdx_parts.append("\n---\n")
                conversation_index += 1

                # Skip past the assistant messages we just processed
                i = j
        else:
            i += 1

    # Add summary at the end using Astro component
    total_tokens = calculate_total_tokens(main_messages)
    if total_tokens["total_input"] > 0:
        mdx_parts.append("\n---\n\n")
        mdx_parts.append("<PromptSummary\n")
        mdx_parts.append(f"  totalInput={{{total_tokens['total_input']}}}\n")
        mdx_parts.append(f"  totalOutput={{{total_tokens['total_output']}}}\n")
        if total_tokens["total_cache_read"] > 0:
            mdx_parts.append(f"  cacheRead={{{total_tokens['total_cache_read']}}}\n")
        if total_tokens["total_cache_creation"] > 0:
            mdx_parts.append(
                f"  cacheCreation={{{total_tokens['total_cache_creation']}}}\n"
            )
        if models_used:
            models_list = ", ".join(f'"{m}"' for m in sorted(models_used))
            mdx_parts.append(f"  models={{[{models_list}]}}\n")
        mdx_parts.append("/>\n")

    return "".join(mdx_parts)


def main():
    parser = argparse.ArgumentParser(
        description="Convert Claude Code conversation from JSONL to MDX format.",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  # Convert latest session
  python convert-claude-chat.py $(ls -t ~/.claude/projects/*/*.jsonl | head -n 1)

  # Convert with custom title
  python convert-claude-chat.py session.jsonl --title "Building a Wiki Feature"

  # Convert to specific output file
  python convert-claude-chat.py session.jsonl --output ../src/content/prompts/my-chat.mdx
        """,
    )

    parser.add_argument(
        "jsonl_file", type=Path, help="Path to Claude Code JSONL session file"
    )
    parser.add_argument(
        "--output", "-o", type=Path, help="Output MDX file path (default: stdout)"
    )
    parser.add_argument(
        "--title",
        "-t",
        type=str,
        help="Title for the conversation (default: auto-generated)",
    )

    args = parser.parse_args()

    # Validate input file
    if not args.jsonl_file.exists():
        print(f"Error: File not found: {args.jsonl_file}", file=sys.stderr)
        sys.exit(1)

    # Parse JSONL
    try:
        messages = parse_jsonl(args.jsonl_file)
    except Exception as e:
        print(f"Error parsing JSONL: {e}", file=sys.stderr)
        sys.exit(1)

    if not messages:
        print("Error: No messages found in JSONL file", file=sys.stderr)
        sys.exit(1)

    # Extract session ID from messages
    session_id = messages[0].get("sessionId", None)

    # Generate title if not provided
    if not args.title:
        first_user_msg = next((m for m in messages if m.get("type") == "user"), None)
        if first_user_msg:
            content = first_user_msg.get("message", {}).get("content", "")
            if isinstance(content, str):
                # Use first line or first 50 chars as title
                title_text = content.split("\n")[0][:50]
                args.title = f"Conversation: {title_text}"
            else:
                args.title = "Claude Code Conversation"
        else:
            args.title = "Claude Code Conversation"

    # Convert to MDX
    try:
        mdx_content = convert_to_mdx(messages, args.title, session_id)
    except Exception as e:
        print(f"Error converting to MDX: {e}", file=sys.stderr)
        import traceback

        traceback.print_exc()
        sys.exit(1)

    # Output
    if args.output:
        args.output.parent.mkdir(parents=True, exist_ok=True)
        args.output.write_text(mdx_content, encoding="utf-8")
        print(f"✅ Converted to: {args.output}")
    else:
        print(mdx_content)


if __name__ == "__main__":
    main()
