"""
Code editing commands for the MiniMax Code CLI.

Provides commands for modifying, refactoring, and formatting existing code.
"""

import os
from typing import Any, List, Dict

from .base import BaseCommand


class EditCommands(BaseCommand):
    """Commands for editing and modifying existing code."""
    
    def __init__(self, client, config, presenter):
        """Initialize edit commands with presenter support."""
        super().__init__(client, config, presenter)
    
    def modify(self, args: Any) -> bool:
        """Modify existing code based on instructions."""
        file_path = args.file
        instruction = args.instruction
        
        if not os.path.exists(file_path):
            self.show_notification(f"File not found: {file_path}", level='error')
            return False
        
        self.show_notification(f"Modifying {file_path}...", level='info')
        
        # Read the current file
        original_code = self.read_file(file_path)
        if not original_code:
            return False
        
        # Detect language
        language = self.detect_language(file_path)
        
        # Create modification prompt
        prompt = f"""
Please modify the following {language} code according to this instruction:

Instruction: {instruction}

Current code:
{original_code}

Requirements:
- Make only the necessary changes to fulfill the instruction
- Preserve existing functionality unless explicitly asked to change it
- Maintain code style and formatting
- Add comments if adding new functionality
- Ensure the modified code is syntactically correct

Please provide the complete modified code.
"""
        
        # Get AI response
        response = self.ask_ai(prompt)
        if not response:
            self.show_notification("Failed to generate modifications", level='error')
            return False
        
        # Extract modified code
        modified_code = self._extract_code_from_response(response)
        
        if args.preview:
            self.show_markdown(f"## Preview of modifications for `{file_path}`")
            self.show_code(modified_code, language=language, title="Modified Code")
            return True
        
        # Show original and modified code side by side
        self.show_markdown(f"## Proposed modifications for `{file_path}`")
        
        # Display diff as a table for better comparison
        diff_data = self._create_diff_table(original_code, modified_code)
        if diff_data:
            self.show_table(diff_data, title="Code Changes", show_header=True)
        
        # Also show the full modified code
        self.show_code(modified_code, language=language, title="Complete Modified Code")
        
        if self.confirm_action("Apply these modifications?"):
            # Create backup if requested
            backup = getattr(args, 'backup', True)
            if self.write_file(file_path, modified_code, backup=backup):
                self.show_notification(f"File modified: {file_path}", level='success')
                if backup:
                    self.show_notification(f"Backup created: {file_path}.backup", level='info')
                return True
            else:
                self.show_notification("Failed to write modifications", level='error')
                return False
        else:
            self.show_notification("Modifications cancelled", level='info')
            return True
    
    def refactor(self, args: Any) -> bool:
        """Refactor code structure."""
        file_path = args.file
        refactor_type = args.type
        target = getattr(args, 'target', None)
        
        if not os.path.exists(file_path):
            self.show_notification(f"File not found: {file_path}", level='error')
            return False
        
        self.show_notification(f"Refactoring {file_path} ({refactor_type})...", level='info')
        
        # Read the current file
        original_code = self.read_file(file_path)
        if not original_code:
            return False
        
        # Detect language
        language = self.detect_language(file_path)
        
        # Create refactoring prompt based on type
        if refactor_type == 'extract':
            prompt = f"""
Please refactor the following {language} code by extracting the specified element into a separate function/method:

Target to extract: {target or 'identify suitable code to extract'}
Current code:
{original_code}

Requirements:
- Extract the specified code into a well-named function/method
- Replace the original code with a call to the new function
- Ensure parameters and return values are handled correctly
- Maintain the same functionality
- Add appropriate documentation

Please provide the complete refactored code.
"""
        elif refactor_type == 'inline':
            prompt = f"""
Please refactor the following {language} code by inlining the specified function/method:

Target to inline: {target}
Current code:
{original_code}

Requirements:
- Replace calls to the target function with its implementation
- Remove the function definition if no longer used
- Maintain the same functionality
- Ensure variable scoping is correct

Please provide the complete refactored code.
"""
        elif refactor_type == 'rename':
            prompt = f"""
Please refactor the following {language} code by renaming the specified element:

Target to rename: {target}
Current code:
{original_code}

Requirements:
- Rename all occurrences of the target element
- Suggest a better name if none provided
- Update all references consistently
- Maintain functionality

Please provide the complete refactored code with the new name.
"""
        else:
            prompt = f"""
Please refactor the following {language} code to improve its structure and readability:

Focus area: {refactor_type}
Target element: {target or 'general improvements'}
Current code:
{original_code}

Requirements:
- Improve code organization and structure
- Enhance readability and maintainability
- Follow best practices for {language}
- Maintain existing functionality
- Add comments where helpful

Please provide the complete refactored code.
"""
        
        # Get AI response
        response = self.ask_ai(prompt)
        if not response:
            self.show_notification("Failed to generate refactoring", level='error')
            return False
        
        # Extract refactored code
        refactored_code = self._extract_code_from_response(response)
        
        # Display refactoring information
        refactor_info = f"## Refactoring: `{file_path}`\n\n"
        refactor_info += f"**Type:** {refactor_type}\n"
        if target:
            refactor_info += f"**Target:** {target}\n"
        
        self.show_markdown(refactor_info)
        
        # Show original code
        self.show_code(original_code, language=language, title="Original Code")
        
        # Show refactored code
        self.show_code(refactored_code, language=language, title="Refactored Code")
        
        # Create and show diff table
        diff_data = self._create_diff_table(original_code, refactored_code)
        if diff_data:
            self.show_table(diff_data, title="Refactoring Changes", show_header=True)
        
        if self.confirm_action("Apply this refactoring?"):
            if self.write_file(file_path, refactored_code, backup=True):
                self.show_notification(f"File refactored: {file_path}", level='success')
                self.show_notification(f"Backup created: {file_path}.backup", level='info')
                return True
            else:
                self.show_notification("Failed to write refactored code", level='error')
                return False
        else:
            self.show_notification("Refactoring cancelled", level='info')
            return True
    
    def format_code(self, args: Any) -> bool:
        """Format and style code."""
        files = args.files
        style = getattr(args, 'style', 'standard')
        
        self.show_notification(f"Formatting {len(files)} file(s) with {style} style...", level='info')
        
        # Track formatting results
        formatting_results = []
        
        for file_path in files:
            if not os.path.exists(file_path):
                self.show_notification(f"File not found: {file_path}", level='error')
                formatting_results.append({
                    'File': file_path,
                    'Status': 'Error',
                    'Changes': 'File not found',
                    'Size': 'N/A'
                })
                continue
            
            # Read the file
            original_code = self.read_file(file_path)
            if not original_code:
                formatting_results.append({
                    'File': file_path,
                    'Status': 'Error',
                    'Changes': 'Failed to read',
                    'Size': 'N/A'
                })
                continue
            
            # Detect language
            language = self.detect_language(file_path)
            
            # Create formatting prompt
            prompt = f"""
Please format and style the following {language} code according to {style} style guidelines:

Current code:
{original_code}

Requirements:
- Apply proper indentation
- Fix spacing and line breaks
- Organize imports/includes
- Follow {style} style conventions for {language}
- Maintain functionality exactly
- Only change formatting, not logic

Please provide the properly formatted code.
"""
            
            # Get AI response
            response = self.ask_ai(prompt)
            if not response:
                self.show_notification(f"Failed to format {file_path}", level='error')
                formatting_results.append({
                    'File': file_path,
                    'Status': 'Error',
                    'Changes': 'AI formatting failed',
                    'Size': f"{len(original_code)} chars"
                })
                continue
            
            # Extract formatted code
            formatted_code = self._extract_code_from_response(response)
            
            # Check if there are actual changes
            if formatted_code.strip() == original_code.strip():
                self.show_notification(f"No formatting changes needed for {file_path}", level='info')
                formatting_results.append({
                    'File': file_path,
                    'Status': 'No Changes',
                    'Changes': 'Already formatted',
                    'Size': f"{len(original_code)} chars"
                })
                continue
            
            # Show formatting preview
            self.show_markdown(f"## Formatting Preview: `{file_path}`")
            
            # Show before and after
            self.show_code(original_code, language=language, title="Original Code")
            self.show_code(formatted_code, language=language, title="Formatted Code")
            
            # Create diff table for formatting changes
            diff_data = self._create_diff_table(original_code, formatted_code, context_lines=2)
            if diff_data:
                self.show_table(diff_data, title="Formatting Changes", show_header=True)
            
            if self.confirm_action(f"Apply formatting to {file_path}?"):
                if self.write_file(file_path, formatted_code, backup=True):
                    self.show_notification(f"File formatted: {file_path}", level='success')
                    formatting_results.append({
                        'File': file_path,
                        'Status': 'Formatted',
                        'Changes': f"{len(self._get_diff_lines(original_code, formatted_code))} lines changed",
                        'Size': f"{len(formatted_code)} chars"
                    })
                else:
                    self.show_notification(f"Failed to write formatted code to {file_path}", level='error')
                    formatting_results.append({
                        'File': file_path,
                        'Status': 'Error',
                        'Changes': 'Write failed',
                        'Size': f"{len(formatted_code)} chars"
                    })
            else:
                formatting_results.append({
                    'File': file_path,
                    'Status': 'Skipped',
                    'Changes': 'User cancelled',
                    'Size': f"{len(original_code)} chars"
                })
        
        # Show summary table
        if formatting_results:
            self.show_markdown("## Formatting Summary")
            self.show_table(formatting_results, title="Formatting Results", show_header=True)
        
        return True
    
    def _extract_code_from_response(self, response: str) -> str:
        """Extract code content from AI response."""
        # This is a simplified extraction
        lines = response.split('\n')
        code_lines = []
        in_code_block = False
        
        for line in lines:
            if line.strip().startswith('```'):
                in_code_block = not in_code_block
                continue
            if in_code_block:
                code_lines.append(line)
        
        if code_lines:
            return '\n'.join(code_lines)
        else:
            # If no code blocks found, return the whole response
            return response
    
    def _create_diff_table(self, original: str, modified: str, context_lines: int = 3) -> List[Dict[str, Any]]:
        """Create a table representation of code differences.
        
        Args:
            original: Original code content
            modified: Modified code content
            context_lines: Number of context lines to show around changes
            
        Returns:
            List of dictionaries representing diff table rows
        """
        import difflib
        
        original_lines = original.splitlines()
        modified_lines = modified.splitlines()
        
        diff_data = []
        
        # Create unified diff
        diff = list(difflib.unified_diff(
            original_lines,
            modified_lines,
            lineterm='',
            n=context_lines
        ))
        
        if not diff:
            return []
        
        line_num_orig = 0
        line_num_mod = 0
        
        for line in diff[2:]:  # Skip the header lines
            if line.startswith('@@'):
                # Parse line numbers from hunk header
                import re
                match = re.match(r'@@ -(\d+),?\d* \+(\d+),?\d* @@', line)
                if match:
                    line_num_orig = int(match.group(1))
                    line_num_mod = int(match.group(2))
                continue
            
            change_type = 'Context'
            line_content = line[1:] if line else ''
            
            if line.startswith('-'):
                change_type = 'Removed'
                diff_data.append({
                    'Line': str(line_num_orig),
                    'Change Type': change_type,
                    'Original': line_content,
                    'Modified': '',
                })
                line_num_orig += 1
            elif line.startswith('+'):
                change_type = 'Added'
                diff_data.append({
                    'Line': str(line_num_mod),
                    'Change Type': change_type,
                    'Original': '',
                    'Modified': line_content,
                })
                line_num_mod += 1
            else:
                # Context line
                diff_data.append({
                    'Line': f"{line_num_orig}/{line_num_mod}",
                    'Change Type': change_type,
                    'Original': line_content,
                    'Modified': line_content,
                })
                line_num_orig += 1
                line_num_mod += 1
        
        return diff_data
    
    def _get_diff_lines(self, original: str, modified: str) -> List[str]:
        """Get list of changed lines for summary purposes.
        
        Args:
            original: Original code content
            modified: Modified code content
            
        Returns:
            List of line numbers that changed
        """
        import difflib
        
        original_lines = original.splitlines()
        modified_lines = modified.splitlines()
        
        diff = list(difflib.unified_diff(
            original_lines,
            modified_lines,
            lineterm=''
        ))
        
        changed_lines = []
        for line in diff:
            if line.startswith('-') or line.startswith('+'):
                changed_lines.append(line)
        
        return changed_lines
