import re
from typing import List, Dict, Any, Optional, Tuple

# Common emoji shortcodes mapping
EMOJI_MAP = {
    # Common emojis
    ':smile:': '😄', ':grin:': '😁', ':joy:': '😂', ':smiley:': '😃',
    ':heart:': '❤️', ':thumbsup:': '👍', ':thumbsdown:': '👎', ':ok_hand:': '👌',
    ':wave:': '👋', ':pray:': '🙏', ':clap:': '👏', ':muscle:': '💪',
    ':eyes:': '👀', ':rocket:': '🚀', ':fire:': '🔥', ':star:': '⭐',
    # Status indicators
    ':check:': '✅', ':x:': '❌', ':warning:': '⚠️', ':bulb:': '💡',
    ':white_check_mark:': '✅', ':heavy_check_mark:': '✔️',
    # Objects
    ':computer:': '💻', ':phone:': '📱', ':calendar:': '📅', ':book:': '📚',
    # Nature
    ':sun:': '☀️', ':moon:': '🌙', ':cloud:': '☁️', ':rain:': '🌧️',
    # Slack-specific
    ':hourglass:': '⏳', ':hourglass_flowing_sand:': '⏳', ':heavy_multiplication_x:': '✖️'
}

class MarkdownToSlackConverter:
    """Simplified and robust markdown to Slack blocks converter"""

    def __init__(self):
        self.blocks = []
        self.current_text = []
    
    def convert_emojis(self, text: str) -> str:
        """Convert emoji shortcodes to actual emojis"""
        pattern = r':[a-zA-Z0-9_+-]+:'
        return re.sub(pattern, lambda m: EMOJI_MAP.get(m.group(0), m.group(0)), text)
    
    def convert_inline_formatting(self, text: str) -> str:
        """Convert inline markdown to Slack mrkdwn format

        Args:
            text: The markdown text to convert
        """
        text = self.convert_emojis(text)

        # Process in order to avoid conflicts
        # 1. Bold: **text** or __text__ -> *text*
        text = re.sub(r'\*\*(.*?)\*\*', r'⚡BOLD⚡\1⚡/BOLD⚡', text)
        text = re.sub(r'__(.*?)__', r'⚡BOLD⚡\1⚡/BOLD⚡', text)

        # 2. Italic: *text* or _text_ -> _text_
        text = re.sub(r'\*([^*]+?)\*', r'_\1_', text)
        text = re.sub(r'_([^_]+?)_', r'_\1_', text)

        # 3. Replace bold placeholders with actual Slack bold format
        text = re.sub(r'⚡BOLD⚡(.*?)⚡/BOLD⚡', r'*\1*', text)

        # 4. Strikethrough: ~~text~~ -> ~text~
        text = re.sub(r'~~(.*?)~~', r'~\1~', text)

        # 5. Links: [text](url) -> <url|text>
        # This format prevents unfurling while showing as hyperlinked text
        text = re.sub(r'\[([^\]]+?)\]\(([^\)]+?)\)', r'<\2|\1>', text)

        return text
    
    def flush_text_section(self):
        """Add accumulated text as a section block"""
        if not self.current_text:
            return
        
        text = '\n'.join(self.current_text)
        if len(text) > 3000:
            # Split long text into chunks
            chunks = self._split_text(text, 2900)
            for chunk in chunks:
                self.blocks.append(self._create_section_block(chunk))
        else:
            self.blocks.append(self._create_section_block(text))
        
        self.current_text = []
    
    def _split_text(self, text: str, max_length: int) -> List[str]:
        """Split text into chunks under max_length"""
        if len(text) <= max_length:
            return [text]
        
        chunks = []
        lines = text.split('\n')
        current_chunk = []
        current_length = 0
        
        for line in lines:
            line_length = len(line) + 1  # +1 for newline
            if current_length + line_length > max_length and current_chunk:
                chunks.append('\n'.join(current_chunk))
                current_chunk = [line]
                current_length = line_length
            else:
                current_chunk.append(line)
                current_length += line_length
        
        if current_chunk:
            chunks.append('\n'.join(current_chunk))
        
        return chunks
    
    def _create_section_block(self, text: str, expand: Optional[bool] = True) -> Dict[str, Any]:
        """Create a section block"""
        block = {
            "type": "section",
            "text": {
                "type": "mrkdwn",
                "text": text
            }
        }

        # Add expand property if specified
        if expand is not None:
            block["expand"] = expand

        return block
    
    def _create_header_block(self, text: str) -> Dict[str, Any]:
        """Create a header block"""
        return {
            "type": "header",
            "text": {
                "type": "plain_text",
                "text": self.convert_emojis(text),
                "emoji": True
            }
        }
    
    def parse_table(self, lines: List[str], start_idx: int) -> Tuple[Optional[str], int]:
        """Parse markdown table and return formatted text"""
        if start_idx >= len(lines) or '|' not in lines[start_idx]:
            return None, start_idx
        
        # Parse header
        header_line = lines[start_idx].strip()
        headers = [h.strip() for h in header_line.split('|') if h.strip()]
        
        # Check separator row
        if start_idx + 1 >= len(lines) or not re.match(r'^[\|\s\-:]+$', lines[start_idx + 1]):
            return None, start_idx
        
        # Parse data rows
        rows = []
        idx = start_idx + 2
        while idx < len(lines) and '|' in lines[idx] and lines[idx].strip():
            cells = [self.convert_inline_formatting(c.strip())
                    for c in lines[idx].split('|') if c.strip()]
            if cells:
                rows.append(cells)
            idx += 1
        
        # Format table
        if not rows:
            return None, start_idx
        
        # Simple table formatting
        table_lines = ['*' + ' | '.join(headers) + '*']
        table_lines.append('─' * (len(' | '.join(headers)) + 2))
        for row in rows:
            table_lines.append(' | '.join(row))
        
        return '\n'.join(table_lines), idx
    
    def parse_code_block(self, lines: List[str], start_idx: int) -> Tuple[Optional[str], int]:
        """Parse code block"""
        if not lines[start_idx].startswith('```'):
            return None, start_idx
        
        # Find end of code block
        end_idx = start_idx + 1
        while end_idx < len(lines) and not lines[end_idx].startswith('```'):
            end_idx += 1
        
        if end_idx >= len(lines):
            # No closing backticks found, treat as regular text
            return None, start_idx
        
        # Extract code content
        code_lines = lines[start_idx + 1:end_idx]
        code_text = '\n'.join(code_lines)
        
        return f'```{code_text}```', end_idx + 1


def _convert_markdown_to_slack_blocks(message: str) -> List[Dict[str, Any]]:
    """
    Convert markdown text to Slack Block Kit blocks.

    Args:
        message: The markdown text to convert

    Supports:
    - Headers (H1-H6)
    - Bold, italic, strikethrough formatting
    - Code blocks and inline code
    - Links (always formatted as <url|text> to prevent unfurling)
    - Lists (ordered, unordered, and task lists)
    - Tables
    - Horizontal rules
    - Emoji shortcodes
    """
    converter = MarkdownToSlackConverter()
    lines = message.strip().split('\n')
    i = 0
    
    while i < len(lines):
        line = lines[i].rstrip()
        
        # Handle code blocks
        code_block, new_i = converter.parse_code_block(lines, i)
        if code_block:
            converter.flush_text_section()
            converter.blocks.append(converter._create_section_block(code_block))
            i = new_i
            continue
        
        # Handle tables
        table_text, new_i = converter.parse_table(lines, i)
        if table_text:
            converter.flush_text_section()
            converter.blocks.append(converter._create_section_block(f'```{table_text}```'))
            i = new_i
            continue
        
        # Handle empty lines (section breaks)
        if not line.strip():
            converter.flush_text_section()
            i += 1
            continue
        
        # Handle horizontal rules
        if re.match(r'^(-{3,}|\*{3,}|_{3,})$', line.strip()):
            converter.flush_text_section()
            converter.blocks.append({"type": "divider"})
            i += 1
            continue
        
        # Handle headers
        header_match = re.match(r'^(#{1,6})\s+(.+)', line)
        if header_match:
            converter.flush_text_section()
            level = len(header_match.group(1))
            header_text = header_match.group(2)
            
            if level <= 2:  # H1 and H2 use header block
                converter.blocks.append(converter._create_header_block(header_text))
            else:  # H3-H6 use bold text
                converter.current_text.append(f"*{converter.convert_inline_formatting(header_text)}*")
            i += 1
            continue
        
        # Handle blockquotes
        if line.startswith('>'):
            quote_lines = []
            while i < len(lines) and lines[i].startswith('>'):
                quote_lines.append(lines[i][1:].strip())
                i += 1
            quote_text = '\n'.join(quote_lines)

            # Check if quote is long enough to benefit from expandable formatting
            if len(quote_text) > 200:  # Default threshold
                converter.flush_text_section()
                # Create expandable quote block for long quotes
                formatted_quote = converter.convert_inline_formatting(quote_text)
                converter.blocks.append(converter._create_section_block(f">{formatted_quote}", expand=False))
            else:
                # Use regular quote formatting for short quotes
                formatted_quote = converter.convert_inline_formatting(quote_text)
                converter.current_text.append(f">{formatted_quote}")
            continue
        
        # Handle task lists (checkboxes)
        task_match = re.match(r'^(\s*)- \[([ xX])\]\s+(.+)', line)
        if task_match:
            indent = '  ' * (len(task_match.group(1)) // 2)
            checked = '☑️' if task_match.group(2).lower() == 'x' else '☐'
            item_text = converter.convert_inline_formatting(task_match.group(3))
            converter.current_text.append(f"{indent}{checked} {item_text}")
            i += 1
            continue

        # Handle regular lists
        list_match = re.match(r'^(\s*)([*\-+]|\d+\.)\s+(.+)', line)
        if list_match:
            indent_level = len(list_match.group(1)) // 2
            bullets = ['•', '◦', '▪']
            bullet = '  ' * indent_level + bullets[min(indent_level, 2)]
            item_text = converter.convert_inline_formatting(list_match.group(3))
            converter.current_text.append(f"{bullet} {item_text}")
            i += 1
            continue

        # Regular text
        formatted_text = converter.convert_inline_formatting(line)
        converter.current_text.append(formatted_text)
        i += 1
    
    # Flush any remaining text
    converter.flush_text_section()
    
    # Limit to 50 blocks (Slack's limit)
    if len(converter.blocks) > 50:
        converter.blocks = converter.blocks[:50]
    
    return converter.blocks

def create_expandable_quote_block(quote_text: str, expand: bool = False) -> List[Dict[str, Any]]:
    """
    Create a Slack Section block with expandable quote functionality.

    Args:
        quote_text: The text content of the quote
        expand: If False (default), Slack will show "see more" for long quotes.
                If True, the quote will always be fully expanded.

    Returns:
        Dict representing a Slack Section block with quote formatting
    """
    # Convert inline formatting and emojis
    converter = MarkdownToSlackConverter()
    formatted_text = converter.convert_inline_formatting(quote_text.strip())
    suffix_text = converter.convert_emojis("*User Message:*")

    # Add quote formatting
    quote_formatted = f"{suffix_text}\n>{formatted_text}"

    return [{
        "type": "section",
        "text": {
            "type": "mrkdwn",
            "text": quote_formatted
        },
        "expand": expand
    }]


def markdown_to_slack_blocks(message: str) -> Tuple[bool, List[Dict[str, Any]] | str]:
    """
    Convert markdown message to Slack blocks.
    Links are always formatted as <url|text> to prevent unfurling.

    Args:
        message: The markdown text to convert

    Returns:
        Tuple of (success: bool, blocks: List[Dict] | error_message: str)
    """
    try:
        blocks = _convert_markdown_to_slack_blocks(message)
        return True, blocks
    except Exception as e:
        print(f"Error converting markdown to slack blocks: {e}")
        return False, message

__all__ = ["markdown_to_slack_blocks", "create_expandable_quote_block"]