"""
Console presenter implementation for MiniMax CLI.

Provides plain text output using basic print() statements and input() for user interaction,
maintaining exact backward compatibility with the existing CLI behavior.
"""

import sys
import re
from datetime import datetime
from typing import Any, Dict, List, Optional, Union, Callable
from pathlib import Path

from .base import BasePresenter, ProgressContext, TreeNode, TreeData


class ConsoleProgressContext(ProgressContext):
    """Simple console-based progress indicator."""
    
    def start(self) -> None:
        """Start the progress indicator."""
        print(f"🔄 {self.task_name}...")
        sys.stdout.flush()
    
    def update(self, advance: int = 1, description: Optional[str] = None) -> None:
        """Update progress with simple dots."""
        self.current += advance
        if description:
            self.task_name = description
        
        # Print dots for progress indication
        print(".", end="")
        sys.stdout.flush()
    
    def finish(self) -> None:
        """Finish the progress indicator."""
        if not self.completed:
            print(" ✅ Done!")
            self.completed = True


class ConsolePresenter(BasePresenter):
    """Console presenter that maintains plain text output for backward compatibility.
    
    This presenter uses basic print() statements and input() for user interaction,
    preserving the exact current behavior with emoji prefixes for status messages.
    """
    
    def __init__(self, **kwargs):
        """Initialize the console presenter."""
        super().__init__(**kwargs)
        self.use_emojis = kwargs.get('use_emojis', True)
    
    def show_info(self, message: str, **kwargs) -> None:
        """Display an informational message with info emoji."""
        prefix = "ℹ️ " if self.use_emojis else "[INFO] "
        print(f"{prefix}{message}")
    
    def show_error(self, message: str, **kwargs) -> None:
        """Display an error message with error emoji."""
        prefix = "❌ " if self.use_emojis else "[ERROR] "
        print(f"{prefix}{message}", file=sys.stderr)
    
    def show_success(self, message: str, **kwargs) -> None:
        """Display a success message with success emoji."""
        prefix = "✅ " if self.use_emojis else "[SUCCESS] "
        print(f"{prefix}{message}")
    
    def show_warning(self, message: str, **kwargs) -> None:
        """Display a warning message with warning emoji."""
        prefix = "⚠️ " if self.use_emojis else "[WARNING] "
        print(f"{prefix}{message}")
    
    def show_header(self, title: str, **kwargs) -> None:
        """Display a section header with simple formatting."""
        subtitle = kwargs.get('subtitle', '')
        
        # Create a simple header with dashes
        header_line = "=" * min(len(title), 60)
        print(f"\n{header_line}")
        print(title)
        if subtitle:
            print(subtitle)
        print(header_line)
    
    def show_separator(self, **kwargs) -> None:
        """Display a visual separator."""
        char = kwargs.get('character', '-')
        length = kwargs.get('length', 50)
        print(char * length)
    
    def show_text(self, content: str, **kwargs) -> None:
        """Display plain text content."""
        indent = kwargs.get('indent', 0)
        wrap = kwargs.get('wrap', True)
        
        if indent > 0:
            # Add indentation to each line
            lines = content.split('\n')
            indented_lines = [' ' * indent + line for line in lines]
            content = '\n'.join(indented_lines)
        
        print(content)
    
    def show_code(self, code: str, language: Optional[str] = None, **kwargs) -> None:
        """Display code with simple markdown-style formatting."""
        show_line_numbers = kwargs.get('line_numbers', False)
        
        # Simple code block formatting
        lang_label = f" ({language})" if language else ""
        print(f"\n```{lang_label}")
        
        if show_line_numbers:
            lines = code.split('\n')
            for i, line in enumerate(lines, 1):
                print(f"{i:3d} | {line}")
        else:
            print(code)
        
        print("```\n")
    
    def show_tree(self, tree_data: TreeData, **kwargs) -> None:
        """Display hierarchical tree structure using ASCII art."""
        max_depth = kwargs.get('max_depth', 10)
        show_files = kwargs.get('show_files', True)
        
        if isinstance(tree_data, str):
            # If it's already a formatted string, just print it
            print(tree_data)
            return
        
        if isinstance(tree_data, (dict, list)):
            # Convert dict/list to TreeNode for consistent handling
            tree_data = self._convert_to_tree_node(tree_data)
        
        if isinstance(tree_data, TreeNode):
            self._print_tree_node(tree_data, "", True, max_depth, show_files)
        else:
            # Fallback for other types
            print(str(tree_data))
    
    def _convert_to_tree_node(self, data: Union[Dict, List]) -> TreeNode:
        """Convert dict or list to TreeNode."""
        if isinstance(data, dict):
            if 'name' in data:
                # Assume it's already in tree format
                node = TreeNode(data['name'], metadata=data.get('metadata', {}))
                for child_data in data.get('children', []):
                    child_node = self._convert_to_tree_node(child_data)
                    node.add_child(child_node)
                return node
            else:
                # Convert dict keys to tree structure
                root = TreeNode("Root")
                for key, value in data.items():
                    if isinstance(value, (dict, list)):
                        child = self._convert_to_tree_node(value)
                        child.name = str(key)
                        root.add_child(child)
                    else:
                        root.add_child(TreeNode(f"{key}: {value}"))
                return root
        elif isinstance(data, list):
            root = TreeNode("Items")
            for i, item in enumerate(data):
                if isinstance(item, (dict, list)):
                    child = self._convert_to_tree_node(item)
                    child.name = f"Item {i+1}"
                    root.add_child(child)
                else:
                    root.add_child(TreeNode(str(item)))
            return root
        else:
            return TreeNode(str(data))
    
    def _print_tree_node(self, node: TreeNode, prefix: str, is_last: bool, 
                         max_depth: int, show_files: bool, current_depth: int = 0) -> None:
        """Recursively print tree node with enhanced Unicode box drawing characters."""
        if current_depth >= max_depth:
            return
        
        # Skip files if show_files is False
        if not show_files and node.metadata.get('type') == 'file':
            return
        
        # Choose the appropriate tree characters (Unicode box drawing)
        if current_depth == 0:
            connector = ""
            new_prefix = ""
        else:
            connector = "└── " if is_last else "├── "
            new_prefix = prefix + ("    " if is_last else "│   ")
        
        # Add type indicator and metadata for files/directories
        type_indicator = ""
        metadata_info = ""
        
        if node.metadata.get('type') == 'directory':
            type_indicator = "📁 " if self.use_emojis else "[DIR] "
        elif node.metadata.get('type') == 'file':
            type_indicator = "📄 " if self.use_emojis else "[FILE] "
            # Add file size if available
            if 'size' in node.metadata and node.metadata['size'] is not None:
                size = node.metadata['size']
                if isinstance(size, int):
                    if size < 1024:
                        metadata_info = f" ({size}B)"
                    elif size < 1024 * 1024:
                        metadata_info = f" ({size/1024:.1f}KB)"
                    else:
                        metadata_info = f" ({size/(1024*1024):.1f}MB)"
        elif node.metadata.get('type') == 'error':
            type_indicator = "⚠️ " if self.use_emojis else "[ERROR] "
        
        # Add modification time if available
        if 'modified' in node.metadata and node.metadata['modified']:
            mod_time = node.metadata['modified']
            if isinstance(mod_time, str):
                metadata_info += f" [{mod_time}]"
        
        print(f"{prefix}{connector}{type_indicator}{node.name}{metadata_info}")
        
        # Print children
        if node.children and current_depth < max_depth - 1:
            for i, child in enumerate(node.children):
                is_child_last = i == len(node.children) - 1
                self._print_tree_node(child, new_prefix, is_child_last, 
                                    max_depth, show_files, current_depth + 1)
    
    def stream_content(self, content_generator: Callable[[], str], **kwargs) -> None:
        """Display streaming content in real-time."""
        prefix = kwargs.get('prefix', '')
        suffix = kwargs.get('suffix', '')
        
        if prefix:
            print(prefix, end='')
        
        try:
            # Handle both generator functions and generator objects
            if callable(content_generator):
                generator = content_generator()
            else:
                generator = content_generator
            
            for chunk in generator:
                if chunk:
                    print(chunk, end='')
                    sys.stdout.flush()
        except Exception as e:
            print(f"\n[Error during streaming: {e}]")
        
        if suffix:
            print(suffix, end='')
        
        print()  # Final newline
    
    def confirm_action(self, message: str, **kwargs) -> bool:
        """Prompt user for confirmation."""
        default = kwargs.get('default', None)
        choices = kwargs.get('choices', ['y', 'n'])
        
        # Format the prompt
        if default is not None:
            if default:
                prompt = f"{message} [Y/n]: "
            else:
                prompt = f"{message} [y/N]: "
        else:
            prompt = f"{message} [{'/'.join(choices)}]: "
        
        try:
            response = input(prompt).strip().lower()
            
            if not response and default is not None:
                return default
            
            # Handle common yes/no responses
            if response in ['y', 'yes', 'true', '1']:
                return True
            elif response in ['n', 'no', 'false', '0']:
                return False
            elif response in choices:
                return response == choices[0]  # First choice is typically "yes"
            else:
                # Invalid response, ask again
                print("Please enter a valid response.")
                return self.confirm_action(message, **kwargs)
                
        except (KeyboardInterrupt, EOFError):
            print("\nOperation cancelled.")
            return False
    
    def show_progress(self, 
                     task_name: str,
                     total: Optional[int] = None,
                     **kwargs) -> ProgressContext:
        """Display progress indicator for long operations."""
        return ConsoleProgressContext(self, task_name, total)
    
    def clear_screen(self) -> None:
        """Clear the screen using ANSI escape codes."""
        # Use ANSI escape sequence to clear screen
        print("\033[2J\033[H", end="")
        sys.stdout.flush()
    
    def set_title(self, title: str) -> None:
        """Set the terminal title using ANSI escape codes."""
        # Use ANSI escape sequence to set terminal title
        print(f"\033]0;{title}\007", end="")
        sys.stdout.flush()
    
    def flush(self) -> None:
        """Flush any pending output."""
        sys.stdout.flush()
        sys.stderr.flush()
    
    def show_table(self, data: List[Dict[str, Any]], **kwargs) -> None:
        """Display data in a simple ASCII table format with headers and borders."""
        if not data:
            return
        
        title = kwargs.get('title')
        show_header = kwargs.get('show_header', True)
        alignment = kwargs.get('alignment', 'left')  # left, right, center
        
        # Get all unique column headers
        headers = []
        for row in data:
            for key in row.keys():
                if key not in headers:
                    headers.append(key)
        
        # Calculate column widths
        col_widths = {}
        for header in headers:
            col_widths[header] = len(header)
            for row in data:
                value = str(row.get(header, ''))
                col_widths[header] = max(col_widths[header], len(value))
        
        # Add padding
        for header in headers:
            col_widths[header] += 2
        
        # Helper function to format cell content
        def format_cell(content: str, width: int, align: str = 'left') -> str:
            content = str(content)
            if align == 'center':
                return content.center(width)
            elif align == 'right':
                return content.rjust(width)
            else:
                return content.ljust(width)
        
        # Print title if provided
        if title:
            total_width = sum(col_widths.values()) + len(headers) - 1
            print(f"\n{title}")
            print("=" * min(len(title), total_width))
        
        # Print top border
        border_parts = []
        for header in headers:
            border_parts.append("─" * col_widths[header])
        print("┌" + "┬".join(border_parts) + "┐")
        
        # Print header row if enabled
        if show_header:
            header_parts = []
            for header in headers:
                formatted_header = format_cell(header, col_widths[header], 'center')
                header_parts.append(formatted_header)
            print("│" + "│".join(header_parts) + "│")
            
            # Print header separator
            separator_parts = []
            for header in headers:
                separator_parts.append("─" * col_widths[header])
            print("├" + "┼".join(separator_parts) + "┤")
        
        # Print data rows
        for row in data:
            row_parts = []
            for header in headers:
                value = str(row.get(header, ''))
                formatted_value = format_cell(value, col_widths[header], alignment)
                row_parts.append(formatted_value)
            print("│" + "│".join(row_parts) + "│")
        
        # Print bottom border
        border_parts = []
        for header in headers:
            border_parts.append("─" * col_widths[header])
        print("└" + "┴".join(border_parts) + "┘")
        print()
    
    def show_markdown(self, content: str, **kwargs) -> None:
        """Convert markdown to plain text with basic formatting."""
        if not content:
            return
        
        lines = content.split('\n')
        formatted_lines = []
        in_code_block = False
        list_indent = 0
        
        for line in lines:
            stripped = line.strip()
            
            # Handle code blocks
            if stripped.startswith('```'):
                in_code_block = not in_code_block
                if in_code_block:
                    lang = stripped[3:].strip()
                    formatted_lines.append(f"\n--- Code Block {f'({lang})' if lang else ''} ---")
                else:
                    formatted_lines.append("--- End Code Block ---\n")
                continue
            
            if in_code_block:
                # Indent code block content
                formatted_lines.append(f"    {line}")
                continue
            
            # Handle headers
            if stripped.startswith('#'):
                level = 0
                for char in stripped:
                    if char == '#':
                        level += 1
                    else:
                        break
                
                header_text = stripped[level:].strip()
                if level == 1:
                    formatted_lines.append(f"\n{header_text}")
                    formatted_lines.append("=" * len(header_text))
                elif level == 2:
                    formatted_lines.append(f"\n{header_text}")
                    formatted_lines.append("-" * len(header_text))
                else:
                    formatted_lines.append(f"\n{'  ' * (level-3)}{header_text}")
                continue
            
            # Handle unordered lists
            if re.match(r'^[\s]*[-*+]\s', line):
                indent_match = re.match(r'^(\s*)', line)
                indent = len(indent_match.group(1)) if indent_match else 0
                list_content = re.sub(r'^[\s]*[-*+]\s', '', line)
                bullet = "•" if self.use_emojis else "*"
                formatted_lines.append(f"{'  ' * (indent // 2)}{bullet} {list_content}")
                continue
            
            # Handle ordered lists
            if re.match(r'^[\s]*\d+\.\s', line):
                indent_match = re.match(r'^(\s*)', line)
                indent = len(indent_match.group(1)) if indent_match else 0
                number_match = re.match(r'^[\s]*(\d+)\.\s', line)
                number = number_match.group(1) if number_match else "1"
                list_content = re.sub(r'^[\s]*\d+\.\s', '', line)
                formatted_lines.append(f"{'  ' * (indent // 2)}{number}. {list_content}")
                continue
            
            # Handle inline code
            line = re.sub(r'`([^`]+)`', r'"\1"', line)
            
            # Handle bold and italic (simple conversion)
            line = re.sub(r'\*\*([^*]+)\*\*', r'\1', line)  # Remove bold
            line = re.sub(r'\*([^*]+)\*', r'\1', line)      # Remove italic
            line = re.sub(r'__([^_]+)__', r'\1', line)      # Remove bold
            line = re.sub(r'_([^_]+)_', r'\1', line)        # Remove italic
            
            # Handle links
            line = re.sub(r'\[([^\]]+)\]\([^)]+\)', r'\1', line)
            
            # Handle horizontal rules
            if re.match(r'^[\s]*[-*_]{3,}[\s]*$', stripped):
                formatted_lines.append("\n" + "-" * 50 + "\n")
                continue
            
            # Handle blockquotes
            if stripped.startswith('>'):
                quote_content = stripped[1:].strip()
                formatted_lines.append(f"  | {quote_content}")
                continue
            
            # Regular line
            if stripped:
                formatted_lines.append(line)
            else:
                formatted_lines.append("")
        
        # Print the formatted content
        for line in formatted_lines:
            print(line)
        print()
    
    def show_notification(self, message: str, level: str = 'info', **kwargs) -> None:
        """Display a timestamped notification message with appropriate prefixes."""
        timestamp = datetime.now().strftime("%H:%M:%S")
        duration = kwargs.get('duration')  # Not used in console mode
        title = kwargs.get('title')
        
        # Choose prefix based on level
        level_prefixes = {
            'info': "ℹ️ " if self.use_emojis else "[INFO]",
            'success': "✅ " if self.use_emojis else "[SUCCESS]",
            'warning': "⚠️ " if self.use_emojis else "[WARNING]",
            'error': "❌ " if self.use_emojis else "[ERROR]",
            'debug': "🔍 " if self.use_emojis else "[DEBUG]"
        }
        
        prefix = level_prefixes.get(level.lower(), level_prefixes['info'])
        
        # Format the notification
        if title:
            notification = f"[{timestamp}] {prefix} {title}: {message}"
        else:
            notification = f"[{timestamp}] {prefix} {message}"
        
        # Print to appropriate stream
        if level.lower() == 'error':
            print(notification, file=sys.stderr)
        else:
            print(notification)
        
        sys.stdout.flush()
