"""
Confluence Core - Operations
Higher-level operations for Confluence integration.
"""

import os
import re
from typing import Optional, Dict, List, Tuple
from dotenv import load_dotenv

# Load environment variables
load_dotenv()

# Get configuration from environment
CONFLUENCE_SERVER = os.getenv('CONFLUENCE_SERVER', '')
CONFLUENCE_PAT = os.getenv('CONFLUENCE_PAT', '')
CONFLUENCE_EMAIL = os.getenv('CONFLUENCE_EMAIL', '')
CONFLUENCE_TOKEN = os.getenv('CONFLUENCE_TOKEN', '')
DEFAULT_SPACE = os.getenv('DEFAULT_SPACE', '')

# Load agent config
AGENT_CONFIG = {}
AGENT_CONFIG_PATHS = ['agent_config.json', '../agent_config.json']
for config_path in AGENT_CONFIG_PATHS:
    if os.path.exists(config_path):
        try:
            import json
            with open(config_path, 'r') as f:
                AGENT_CONFIG = json.load(f)
            break
        except Exception:
            pass

# Get Confluence config from agent_config
CONFLUENCE_CONFIG = AGENT_CONFIG.get('confluence', {})
if not CONFLUENCE_SERVER:
    CONFLUENCE_SERVER = CONFLUENCE_CONFIG.get('server', '')
if not DEFAULT_SPACE:
    DEFAULT_SPACE = CONFLUENCE_CONFIG.get('default_space', '')


def connect_confluence():
    """
    Connect to Confluence using configured credentials.
    
    Returns:
        ConfluenceClient instance
    """
    from .client import ConfluenceClient
    
    server = CONFLUENCE_SERVER
    if not server:
        raise ValueError("CONFLUENCE_SERVER not configured. Set in .env or agent_config.json")
    
    if CONFLUENCE_PAT:
        return ConfluenceClient(server, token=CONFLUENCE_PAT)
    elif CONFLUENCE_EMAIL and CONFLUENCE_TOKEN:
        return ConfluenceClient(server, basic_auth=(CONFLUENCE_EMAIL, CONFLUENCE_TOKEN))
    else:
        raise ValueError("Confluence credentials not configured. Set CONFLUENCE_PAT or CONFLUENCE_EMAIL+CONFLUENCE_TOKEN")


# ============================================================
# MARKDOWN <-> CONFLUENCE STORAGE FORMAT CONVERSION
# ============================================================

def validate_xhtml(content: str) -> tuple[bool, str]:
    """
    Validate that content is well-formed XHTML.
    
    Returns:
        Tuple of (is_valid, error_message)
    """
    try:
        from xml.etree import ElementTree as ET
        # Wrap in root element for parsing
        wrapped = f'<root xmlns:ac="http://atlassian.com/ac" xmlns:ri="http://atlassian.com/ri">{content}</root>'
        ET.fromstring(wrapped)
        return True, ""
    except ET.ParseError as e:
        return False, str(e)
    except Exception as e:
        return False, str(e)


def sanitize_for_xhtml(text: str) -> str:
    """
    Sanitize text for safe XHTML embedding.
    Escape characters that could break XML parsing.
    """
    # Escape XML special characters (but not already escaped ones)
    text = re.sub(r'&(?!(amp|lt|gt|quot|apos|#\d+|#x[0-9a-fA-F]+);)', '&amp;', text)
    text = text.replace('<', '&lt;').replace('>', '&gt;')
    return text

def markdown_to_storage_simple(markdown_content: str) -> str:
    """
    Simple/safe markdown to storage conversion.
    Used as fallback when complex conversion fails.
    Uses mistune library for reliable conversion, or basic HTML if not available.
    """
    try:
        import mistune
        # Use mistune to convert to HTML
        html = mistune.html(markdown_content)
        
        # Convert HTML5 to XHTML (self-closing tags)
        html = re.sub(r'<br\s*/?>', '<br/>', html)
        html = re.sub(r'<hr\s*/?>', '<hr/>', html)
        html = re.sub(r'<img([^>]*?)(?<!/)>', r'<img\1/>', html)
        
        # Convert HTML code blocks to Confluence code macros
        def replace_code_block(match):
            lang = match.group(1) or ''
            code = match.group(2)
            # Unescape HTML entities in code
            code = code.replace('&lt;', '<').replace('&gt;', '>').replace('&amp;', '&')
            # Escape CDATA
            code = code.replace(']]>', ']]]]><![CDATA[>')
            lang_param = f'<ac:parameter ac:name="language">{lang}</ac:parameter>' if lang else ''
            return f'<ac:structured-macro ac:name="code">{lang_param}<ac:plain-text-body><![CDATA[{code}]]></ac:plain-text-body></ac:structured-macro>'
        
        html = re.sub(r'<pre><code class="language-(\w+)">(.*?)</code></pre>', replace_code_block, html, flags=re.DOTALL)
        html = re.sub(r'<pre><code>(.*?)</code></pre>', lambda m: replace_code_block(type('Match', (), {'group': lambda s, i: '' if i==1 else m.group(1)})()), html, flags=re.DOTALL)
        
        return html
        
    except ImportError:
        # Fallback: basic conversion without mistune
        content = markdown_content
        
        # Escape HTML special chars first
        content = content.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
        
        # Basic headers
        for i in range(6, 0, -1):
            pattern = r'^' + ('#' * i) + r'\s+(.+)$'
            content = re.sub(pattern, f'<h{i}>\\1</h{i}>', content, flags=re.MULTILINE)
        
        # Bold
        content = re.sub(r'\*\*(.+?)\*\*', r'<strong>\1</strong>', content)
        
        # Convert newlines to line breaks
        content = content.replace('\n\n', '</p><p>')
        content = content.replace('\n', '<br/>')
        content = f'<p>{content}</p>'
        
        # Clean up
        content = re.sub(r'<p>\s*</p>', '', content)
        
        return content


def markdown_to_storage(markdown_content: str) -> str:
    """
    Convert Markdown to Confluence storage format (XHTML).
    
    Features:
    - Headers (h1-h6)
    - Bold, italic, strikethrough
    - Code blocks with syntax highlighting
    - Inline code
    - Tables
    - Lists (ordered and unordered)
    - Links
    - Mermaid diagrams (as code macro)
    - Blockquotes with GitHub alerts
    
    Safety:
    - Validates output XHTML before returning
    - Falls back to simple conversion if complex parsing fails
    """
    try:
        result = _markdown_to_storage_impl(markdown_content)
        
        # Validate the result
        is_valid, error = validate_xhtml(result)
        if not is_valid:
            print(f"⚠️ XHTML validation failed: {error}")
            print("   Falling back to simple markdown rendering...")
            return markdown_to_storage_simple(markdown_content)
        
        return result
        
    except Exception as e:
        print(f"⚠️ Markdown conversion error: {e}")
        print("   Falling back to simple markdown rendering...")
        return markdown_to_storage_simple(markdown_content)


def _markdown_to_storage_impl(markdown_content: str) -> str:
    """
    Internal implementation of markdown to storage conversion.
    """
    content = markdown_content
    
    # ============================================================
    # PHASE 1: PRESERVE CODE BLOCKS (protect from markdown processing)
    # ============================================================
    code_blocks = []
    
    def preserve_code_block(match):
        lang = match.group(1) or ''
        code = match.group(2)
        idx = len(code_blocks)
        code_blocks.append((lang, code))
        return f'\x00CODE{idx}\x00'  # Use null char as marker (won't appear in text)
    
    # Match fenced code blocks - be very precise
    content = re.sub(r'```(\w*)\s*\n(.*?)\n```', preserve_code_block, content, flags=re.DOTALL)
    
    # ============================================================
    # PHASE 2: PRESERVE INLINE CODE (protect from formatting)
    # ============================================================
    inline_codes = []
    
    def preserve_inline_code(match):
        code = match.group(1)
        idx = len(inline_codes)
        inline_codes.append(code)
        return f'\x00INLINE{idx}\x00'
    
    content = re.sub(r'`([^`\n]+)`', preserve_inline_code, content)
    
    # ============================================================
    # PHASE 3: CONVERT BLOCK ELEMENTS
    # ============================================================
    
    # Headers (must be at line start)
    for i in range(6, 0, -1):
        header_pattern = r'^' + ('#' * i) + r'\s+(.+)$'
        content = re.sub(header_pattern, f'<h{i}>\\1</h{i}>', content, flags=re.MULTILINE)
    
    # Horizontal rules (but not YAML frontmatter separators)
    content = re.sub(r'^-{3,}$', '<hr/>', content, flags=re.MULTILINE)
    
    # Tables
    def convert_table(match):
        lines = [l for l in match.group(0).strip().split('\n') if l.strip()]
        if len(lines) < 2:
            return match.group(0)
        
        html = '<table><tbody>'
        for i, line in enumerate(lines):
            # Skip separator line
            if re.match(r'^\|[\s\-:|]+\|$', line):
                continue
            
            cells = [c.strip() for c in line.strip('|').split('|')]
            tag = 'th' if i == 0 else 'td'
            html += '<tr>'
            for cell in cells:
                # Sanitize cell content
                safe_cell = sanitize_for_xhtml(cell) if '<' in cell or '&' in cell else cell
                html += f'<{tag}>{safe_cell}</{tag}>'
            html += '</tr>'
        
        html += '</tbody></table>'
        return html
    
    content = re.sub(r'(?:\|.+\|\n)+', convert_table, content)
    
    # Blockquotes with GitHub alerts
    def convert_blockquote(match):
        lines = match.group(0).split('\n')
        text_lines = [re.sub(r'^>\s?', '', line) for line in lines if line.strip()]
        inner = '<br/>'.join(text_lines)
        
        # GitHub-style alerts
        alert_map = {
            '[!NOTE]': 'info',
            '[!WARNING]': 'warning', 
            '[!IMPORTANT]': 'note',
            '[!TIP]': 'tip',
            '[!CAUTION]': 'warning'
        }
        
        for marker, macro in alert_map.items():
            if marker in inner:
                inner = inner.replace(marker, '').strip()
                return f'<ac:structured-macro ac:name="{macro}"><ac:rich-text-body><p>{inner}</p></ac:rich-text-body></ac:structured-macro>'
        
        return f'<blockquote><p>{inner}</p></blockquote>'
    
    content = re.sub(r'(?:^>.*$\n?)+', convert_blockquote, content, flags=re.MULTILINE)
    
    # Unordered lists
    def convert_ul(match):
        lines = match.group(0).strip().split('\n')
        html = '<ul>'
        for line in lines:
            item = re.sub(r'^[\s]*[-*+]\s+', '', line)
            html += f'<li>{item}</li>'
        html += '</ul>'
        return html
    
    content = re.sub(r'(?:^[\s]*[-*+]\s+.+$\n?)+', convert_ul, content, flags=re.MULTILINE)
    
    # Ordered lists
    def convert_ol(match):
        lines = match.group(0).strip().split('\n')
        html = '<ol>'
        for line in lines:
            item = re.sub(r'^[\s]*\d+\.\s+', '', line)
            html += f'<li>{item}</li>'
        html += '</ol>'
        return html
    
    content = re.sub(r'(?:^[\s]*\d+\.\s+.+$\n?)+', convert_ol, content, flags=re.MULTILINE)
    
    # ============================================================
    # PHASE 4: CONVERT INLINE ELEMENTS
    # ============================================================
    
    # Bold + Italic
    content = re.sub(r'\*\*\*(.+?)\*\*\*', r'<strong><em>\1</em></strong>', content)
    
    # Bold
    content = re.sub(r'\*\*(.+?)\*\*', r'<strong>\1</strong>', content)
    
    # Italic (careful not to match * in other contexts)
    content = re.sub(r'(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)', r'<em>\1</em>', content)
    
    # Strikethrough
    content = re.sub(r'~~(.+?)~~', r'<del>\1</del>', content)
    
    # Links
    content = re.sub(r'\[([^\]]+)\]\(([^)]+)\)', r'<a href="\2">\1</a>', content)
    
    # ============================================================
    # PHASE 5: WRAP IN PARAGRAPHS
    # ============================================================
    
    # Split by block elements and wrap text in paragraphs
    block_tags = r'(<h[1-6]>|</h[1-6]>|<hr/>|<ul>|</ul>|<ol>|</ol>|<table>|</table>|<blockquote>|</blockquote>|<ac:structured-macro|</ac:structured-macro>|\x00CODE\d+\x00)'
    
    parts = re.split(block_tags, content)
    result_parts = []
    
    for part in parts:
        if not part or not part.strip():
            continue
        
        # If it's a tag or code marker, keep as-is
        if re.match(block_tags, part) or part.startswith('\x00'):
            result_parts.append(part)
        else:
            # It's text content - wrap paragraphs
            text = part.strip()
            if text:
                # Split by double newlines for paragraphs
                paragraphs = re.split(r'\n\n+', text)
                for p in paragraphs:
                    p = p.strip()
                    if p:
                        # Replace single newlines with <br/>
                        p = p.replace('\n', '<br/>')
                        result_parts.append(f'<p>{p}</p>')
    
    content = ''.join(result_parts)
    
    # ============================================================
    # PHASE 6: RESTORE CODE BLOCKS
    # ============================================================
    
    for idx, (lang, code) in enumerate(code_blocks):
        # Escape CDATA closing sequence
        code_safe = code.replace(']]>', ']]]]><![CDATA[>')
        
        if lang.lower() == 'mermaid':
            code_macro = f'<ac:structured-macro ac:name="code"><ac:parameter ac:name="language">mermaid</ac:parameter><ac:plain-text-body><![CDATA[{code_safe}]]></ac:plain-text-body></ac:structured-macro>'
        else:
            lang_param = f'<ac:parameter ac:name="language">{lang}</ac:parameter>' if lang else ''
            code_macro = f'<ac:structured-macro ac:name="code">{lang_param}<ac:plain-text-body><![CDATA[{code_safe}]]></ac:plain-text-body></ac:structured-macro>'
        
        content = content.replace(f'\x00CODE{idx}\x00', code_macro)
    
    # ============================================================
    # PHASE 7: RESTORE INLINE CODE
    # ============================================================
    
    for idx, code in enumerate(inline_codes):
        # Sanitize inline code content
        safe_code = sanitize_for_xhtml(code)
        content = content.replace(f'\x00INLINE{idx}\x00', f'<code>{safe_code}</code>')
    
    # ============================================================
    # PHASE 8: CLEAN UP
    # ============================================================
    
    # Remove empty paragraphs
    content = re.sub(r'<p>\s*</p>', '', content)
    
    # Fix paragraph tags around block elements (shouldn't happen but safety)
    content = re.sub(r'<p>\s*(<(?:h[1-6]|ul|ol|table|blockquote|ac:structured-macro))', r'\1', content)
    content = re.sub(r'(</(?:h[1-6]|ul|ol|table|blockquote|ac:structured-macro)>)\s*</p>', r'\1', content)
    
    return content


def storage_to_markdown(storage_content: str) -> str:
    """
    Convert Confluence storage format to Markdown.
    
    Improved conversion with better line break handling.
    """
    content = storage_content
    
    # Extract code blocks first and preserve them
    code_blocks = []
    def preserve_code(match):
        full_match = match.group(0)
        lang_match = re.search(r'ac:name="language">([^<]+)<', full_match)
        lang = lang_match.group(1) if lang_match else ''
        
        code_match = re.search(r'<!\[CDATA\[(.*?)\]\]>', full_match, re.DOTALL)
        code = code_match.group(1) if code_match else ''
        
        idx = len(code_blocks)
        code_blocks.append((lang, code))
        return f'{{{{CODE_BLOCK_{idx}}}}}'
    
    content = re.sub(
        r'<ac:structured-macro ac:name="code"[^>]*>.*?</ac:structured-macro>',
        preserve_code, content, flags=re.DOTALL
    )
    
    # Remove/handle anchor macros FIRST - they create duplicate text
    # Confluence anchors: <ac:link anchor="..."><ac:plain-text-link-body>text</ac:plain-text-link-body></ac:link>
    # or: <ac:link anchor="..."><ri:page ri:content-title="..."/>...</ac:link>
    content = re.sub(
        r'<ac:link[^>]*>.*?<ac:plain-text-link-body><!\[CDATA\[([^\]]*)\]\]></ac:plain-text-link-body>.*?</ac:link>',
        r'\1', content, flags=re.DOTALL
    )
    # Also handle simpler anchor links - just extract the visible text
    content = re.sub(r'<ac:link[^>]*>.*?</ac:link>', '', content, flags=re.DOTALL)
    
    # Remove ri:page and other resource identifiers (they're just metadata)
    content = re.sub(r'<ri:[^>]*/>', '', content)
    
    # Handle line breaks BEFORE removing tags
    content = re.sub(r'<br\s*/?>', '\n', content)
    
    # Convert headers - add newlines around them
    # First strip bold/strong inside headers since markdown headers are inherently bold
    def clean_header(match):
        level = len(match.group(1)) if match.group(1) else 1
        header_content = match.group(2)
        # Remove strong/bold tags inside - headers are already bold in markdown
        header_content = re.sub(r'</?strong[^>]*>', '', header_content)
        header_content = re.sub(r'</?b[^>]*>', '', header_content)
        header_content = re.sub(r'\*\*', '', header_content)  # Remove any markdown bold
        # Remove any remaining HTML tags
        header_content = re.sub(r'<[^>]+>', '', header_content)
        header_content = header_content.strip()
        return f'\n\n{"#" * level} {header_content}\n\n'
    
    for i in range(6, 0, -1):
        content = re.sub(f'<h({i})[^>]*>(.+?)</h{i}>', clean_header, content, flags=re.DOTALL)
    
    # Convert formatting
    content = re.sub(r'<strong[^>]*>(.+?)</strong>', r'**\1**', content, flags=re.DOTALL)
    content = re.sub(r'<b[^>]*>(.+?)</b>', r'**\1**', content, flags=re.DOTALL)
    content = re.sub(r'<em[^>]*>(.+?)</em>', r'*\1*', content, flags=re.DOTALL)
    content = re.sub(r'<i[^>]*>(.+?)</i>', r'*\1*', content, flags=re.DOTALL)
    content = re.sub(r'<del[^>]*>(.+?)</del>', r'~~\1~~', content, flags=re.DOTALL)
    content = re.sub(r'<code[^>]*>(.+?)</code>', r'`\1`', content, flags=re.DOTALL)
    content = re.sub(r'<u[^>]*>(.+?)</u>', r'\1', content, flags=re.DOTALL)
    
    # Convert links
    content = re.sub(r'<a[^>]*href="([^"]+)"[^>]*>([^<]+)</a>', r'[\2](\1)', content)
    
    # Convert lists with proper newlines
    def convert_list(match, ordered=False):
        list_html = match.group(0)
        items = re.findall(r'<li[^>]*>(.*?)</li>', list_html, re.DOTALL)
        result = '\n'
        for i, item in enumerate(items):
            item = re.sub(r'<[^>]+>', '', item).strip()
            prefix = f'{i+1}.' if ordered else '-'
            result += f'{prefix} {item}\n'
        return result + '\n'
    
    content = re.sub(r'<ul[^>]*>.*?</ul>', lambda m: convert_list(m, False), content, flags=re.DOTALL)
    content = re.sub(r'<ol[^>]*>.*?</ol>', lambda m: convert_list(m, True), content, flags=re.DOTALL)
    
    # Convert tables
    def convert_table_to_md(match):
        table_html = match.group(0)
        rows = re.findall(r'<tr[^>]*>(.*?)</tr>', table_html, re.DOTALL)
        if not rows:
            return ''
        
        md_lines = ['\n']
        for i, row in enumerate(rows):
            cells = re.findall(r'<t[hd][^>]*>(.*?)</t[hd]>', row, re.DOTALL)
            cells = [re.sub(r'<[^>]+>', '', c).strip() for c in cells]
            if cells:
                md_lines.append('| ' + ' | '.join(cells) + ' |')
                if i == 0:
                    md_lines.append('|' + '---|' * len(cells))
        
        return '\n'.join(md_lines) + '\n\n'
    
    content = re.sub(r'<table[^>]*>.*?</table>', convert_table_to_md, content, flags=re.DOTALL)
    
    # Convert info macros to blockquotes
    def convert_macro(match):
        macro_type = match.group(1)
        body = match.group(2)
        body = re.sub(r'<[^>]+>', '', body).strip()
        return f'\n> [{macro_type.upper()}] {body}\n'
    
    content = re.sub(
        r'<ac:structured-macro ac:name="(info|warning|note|tip)"[^>]*>'
        r'.*?<ac:rich-text-body>(.*?)</ac:rich-text-body>.*?</ac:structured-macro>',
        convert_macro, content, flags=re.DOTALL
    )
    
    # Convert blockquotes
    content = re.sub(r'<blockquote[^>]*>(.+?)</blockquote>', r'\n> \1\n', content, flags=re.DOTALL)
    
    # Convert paragraphs - add double newlines
    content = re.sub(r'</p>\s*<p[^>]*>', '\n\n', content)
    content = re.sub(r'<p[^>]*>', '\n', content)
    content = re.sub(r'</p>', '\n', content)
    
    # Convert divs to newlines
    content = re.sub(r'</div>\s*<div[^>]*>', '\n', content)
    content = re.sub(r'</?div[^>]*>', '\n', content)
    
    # Horizontal rules
    content = re.sub(r'<hr[^>]*/?\s*>', '\n---\n', content)
    
    # Remove remaining HTML tags
    content = re.sub(r'<[^>]+>', '', content)
    
    # Restore code blocks
    for idx, (lang, code) in enumerate(code_blocks):
        content = content.replace(f'{{{{CODE_BLOCK_{idx}}}}}', f'\n```{lang}\n{code}\n```\n')
    
    # Clean up whitespace but preserve structure
    content = re.sub(r'\n{4,}', '\n\n\n', content)  # Max 3 newlines
    content = re.sub(r'[ \t]+\n', '\n', content)      # Trailing spaces
    content = content.strip()
    
    return content


# ============================================================
# DOCUMENT OPERATIONS
# ============================================================

def generate_confluence_title(content: str, original_title: str) -> str:
    """
    Use AI to generate a better Confluence page title.
    
    Args:
        content: Document content (first 2000 chars)
        original_title: Original title from frontmatter
        
    Returns:
        Improved title or original if AI fails
    """
    try:
        import google.generativeai as genai
        
        api_key = os.getenv('GEMINI_API_KEY', '')
        if not api_key:
            return original_title
        
        genai.configure(api_key=api_key)
        model = genai.GenerativeModel('gemini-2.0-flash')
        
        prompt = f"""You are a technical writer. Create a concise, professional Confluence page title for this document.

Original title: {original_title}

Document excerpt:
{content[:2000]}

Rules:
- Keep it under 60 characters
- Make it clear and professional
- Use Title Case
- Don't use quotes or special characters
- Focus on the main topic/action

Respond with ONLY the title, nothing else."""

        response = model.generate_content(prompt)
        new_title = response.text.strip().strip('"\'')
        
        # Validate title
        if new_title and 5 < len(new_title) < 100:
            return new_title
        return original_title
        
    except Exception as e:
        print(f"⚠️ AI title generation failed: {e}")
        return original_title


def push_document(file_path: str, space_key: str = None, parent_title: str = None,
                  parent_id: str = None, ai_title: bool = False) -> Tuple[str, str, bool]:
    """
    Push a markdown document to Confluence.
    Creates new page or updates existing based on frontmatter.
    
    Args:
        file_path: Path to markdown file
        space_key: Confluence space key (uses default if not specified)
        parent_title: Title of parent page to create under
        parent_id: ID of parent page (alternative to parent_title)
        ai_title: If True, use AI to generate better title
        
    Returns:
        Tuple of (page_id, page_url, is_new)
    """
    import frontmatter
    
    # Read file
    with open(file_path, 'r', encoding='utf-8') as f:
        post = frontmatter.load(f)
    
    title = post.metadata.get('title', os.path.basename(file_path).replace('.md', ''))
    confluence_id = post.metadata.get('confluence_id')
    
    # Generate AI title if requested
    if ai_title and not confluence_id:  # Only for new pages
        print(f"🤖 Generating AI title...")
        title = generate_confluence_title(post.content, title)
        print(f"   📝 Title: {title}")
    
    # Get space key
    space = space_key or post.metadata.get('confluence_space') or DEFAULT_SPACE
    if not space:
        raise ValueError("Space key not specified. Use --space or set in frontmatter/config")
    
    # Strip header/metadata section (content before first ## heading)
    # This removes duplicate titles like "# Tech-Spec: ..." and metadata like "**Created:**"
    content = post.content.strip()
    h2_match = re.search(r'^##\s', content, re.MULTILINE)
    if h2_match:
        # Keep content starting from first H2
        content = content[h2_match.start():]
    
    # Convert content to storage format
    body = markdown_to_storage(content)
    
    # Connect to Confluence
    conf = connect_confluence()
    
    try:
        if confluence_id:
            # UPDATE existing page
            page = conf.get_page(confluence_id)
            updated_page = conf.update_page(
                confluence_id, 
                title, 
                body, 
                page.version,
                message=f"Updated via sync_confluence"
            )
            page_url = updated_page.get_url()
            is_new = False
            print(f"✅ Updated: {title}")
            print(f"   🔗 {page_url}")
        else:
            # CREATE new page
            # Find parent if specified
            actual_parent_id = parent_id
            if not actual_parent_id and parent_title:
                parent_page = conf.find_parent_page(space, parent_title)
                if parent_page:
                    actual_parent_id = parent_page.id
                else:
                    print(f"⚠️ Parent '{parent_title}' not found, creating at root")
            
            new_page = conf.create_page(space, title, body, actual_parent_id)
            confluence_id = new_page.id
            page_url = new_page.get_url()
            is_new = True
            
            # Update frontmatter with confluence info
            post.metadata['confluence_id'] = confluence_id
            post.metadata['confluence_space'] = space
            post.metadata['confluence_url'] = page_url
            if parent_title:
                post.metadata['confluence_parent'] = parent_title
            
            # Write back to file
            with open(file_path, 'w', encoding='utf-8') as f:
                f.write(frontmatter.dumps(post))
            
            print(f"✅ Created: {title}")
            print(f"   🔗 {page_url}")
            print(f"   📝 Updated frontmatter with confluence_id")
        
        return confluence_id, page_url, is_new
        
    finally:
        conf.close()


def pull_document(output_path: str, page_id: str = None, 
                  space_key: str = None, title: str = None) -> str:
    """
    Pull a Confluence page to local markdown file.
    
    Args:
        output_path: Path to save markdown file
        page_id: Confluence page ID
        space_key: Space key (required if using title)
        title: Page title (alternative to page_id)
        
    Returns:
        Path to saved file
    """
    import frontmatter
    
    conf = connect_confluence()
    
    try:
        if page_id:
            page = conf.get_page(page_id)
        elif space_key and title:
            page = conf.get_page_by_title(space_key, title)
            if not page:
                raise ValueError(f"Page '{title}' not found in space {space_key}")
        else:
            raise ValueError("Either page_id or (space_key + title) must be provided")
        
        # Convert content to markdown
        markdown_content = storage_to_markdown(page.content)
        
        # Create frontmatter
        metadata = {
            'title': page.title,
            'confluence_id': page.id,
            'confluence_space': page.get_space_key() or space_key,
            'confluence_url': page.get_url()
        }
        
        # Get parent info
        if page.ancestors:
            parent = page.ancestors[-1]
            metadata['confluence_parent'] = parent.get('title', '')
        
        # Create post with frontmatter
        post = frontmatter.Post(markdown_content, **metadata)
        
        # Ensure directory exists
        os.makedirs(os.path.dirname(output_path) or '.', exist_ok=True)
        
        # Write file
        with open(output_path, 'w', encoding='utf-8') as f:
            f.write(frontmatter.dumps(post))
        
        print(f"✅ Pulled: {page.title}")
        print(f"   📄 Saved to: {output_path}")
        
        return output_path
        
    finally:
        conf.close()


# ============================================================
# PAGE TREE DISPLAY
# ============================================================

def print_page_tree(space_key: str = None, parent_id: str = None, 
                    parent_title: str = None, depth: int = 2) -> List[Dict]:
    """
    Print page tree and return flat list for selection.
    
    Returns list of dicts: [{"num": 1, "id": "123", "title": "Page"}, ...]
    """
    conf = connect_confluence()
    
    try:
        # Resolve parent_title to parent_id
        if parent_title and not parent_id:
            space = space_key or DEFAULT_SPACE
            parent_page = conf.find_parent_page(space, parent_title)
            if parent_page:
                parent_id = parent_page.id
            else:
                print(f"❌ Parent '{parent_title}' not found")
                return []
        
        # Get tree
        tree = conf.get_page_tree(
            space_key=space_key or DEFAULT_SPACE, 
            parent_id=parent_id, 
            depth=depth
        )
        
        # Print and build flat list
        flat_list = []
        counter = [0]  # Use list to allow modification in nested function
        
        def print_node(node: dict, prefix: str = "", is_last: bool = True):
            counter[0] += 1
            num = counter[0]
            
            connector = "└── " if is_last else "├── "
            has_children = bool(node.get('children'))
            icon = "📁" if has_children else "📄"
            
            print(f"{prefix}{connector}{icon} [{num}] {node['title']}")
            
            flat_list.append({
                'num': num,
                'id': node['id'],
                'title': node['title'],
                'url': node['url']
            })
            
            children = node.get('children', [])
            child_prefix = prefix + ("    " if is_last else "│   ")
            for i, child in enumerate(children):
                print_node(child, child_prefix, i == len(children) - 1)
        
        # Print header
        header = f"📁 Page Tree"
        if parent_title or parent_id:
            header += f" (under: {parent_title or parent_id})"
        elif space_key:
            header += f" ({space_key})"
        print(f"\n{header}\n")
        
        for i, node in enumerate(tree):
            print_node(node, "", i == len(tree) - 1)
        
        print(f"\n📊 Total: {len(flat_list)} pages\n")
        
        return flat_list
        
    finally:
        conf.close()
