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

Provides commands for generating functions, classes, tests, and entire projects.
"""

import os
import re
import json
from typing import Any, Dict, List, Tuple

from .base import BaseCommand


class GenerateCommands(BaseCommand):
    """Commands for generating new code."""
    
    def __init__(self, client, config, presenter):
        """Initialize generate commands with presenter integration."""
        super().__init__(client, config, presenter)
    
    def function(self, args: Any) -> bool:
        """Generate a function based on description."""
        self.show_notification(f"Generating {args.language} function...", level='info')
        
        # Prepare additional requirements
        additional_requirements = []
        if args.tests:
            additional_requirements.append("- Include unit tests for the function")
        
        additional_req_text = "\n".join(additional_requirements) if additional_requirements else ""
        
        # Create generation prompt
        prompt = self.create_prompt_template(
            'code_generation',
            language=args.language,
            description=args.description,
            additional_requirements=additional_req_text
        )
        
        # Get AI response
        response = self.ask_ai(prompt)
        if not response:
            self.show_notification("Failed to generate function", level='error')
            return False
        
        self.presenter.show_header(f"Generated {args.language} function", icon="🔧")
        
        # Extract and display code with proper syntax highlighting
        code_content = self._extract_code_from_response(response)
        self.show_code(code_content, language=args.language)
        
        # Save to file if requested
        if args.output:
            if self.confirm_action(f"Save to {args.output}?"):
                if self.write_file(args.output, code_content):
                    self.show_notification(f"Function saved to {args.output}", level='success')
                else:
                    self.show_notification(f"Failed to save to {args.output}", level='error')
        
        return True
    
    def class_code(self, args: Any) -> bool:
        """Generate a class based on description."""
        self.show_notification(f"Generating {args.language} class...", level='info')
        
        # Create generation prompt for class
        prompt = f"""
Please generate a {args.language} class for the following requirement:

{args.description}

Requirements:
- Write clean, well-documented code
- Follow best practices for {args.language}
- Include appropriate methods and properties
- Add docstrings/comments explaining the class
- Include constructor/initialization
- Add any necessary imports

Please provide a complete, working class implementation.
"""
        
        # Get AI response
        response = self.ask_ai(prompt)
        if not response:
            self.show_notification("Failed to generate class", level='error')
            return False
        
        self.presenter.show_header(f"Generated {args.language} class", icon="🏗️")
        
        # Extract and display code with proper syntax highlighting
        code_content = self._extract_code_from_response(response)
        self.show_code(code_content, language=args.language)
        
        # Save to file if requested
        if args.output:
            if self.confirm_action(f"Save to {args.output}?"):
                if self.write_file(args.output, code_content):
                    self.show_notification(f"Class saved to {args.output}", level='success')
                else:
                    self.show_notification(f"Failed to save to {args.output}", level='error')
        
        return True
    
    def test(self, args: Any) -> bool:
        """Generate tests for existing code."""
        file_path = args.file
        
        if not os.path.exists(file_path):
            self.show_notification(f"File not found: {file_path}", level='error')
            return False
        
        self.show_notification(f"Generating tests for {file_path}...", level='info')
        
        # Read the source file
        code = self.read_file(file_path)
        if not code:
            return False
        
        # Detect language
        language = self.detect_language(file_path)
        
        # Determine test framework
        framework = args.framework
        if not framework:
            # Default frameworks by language
            framework_map = {
                'python': 'pytest',
                'javascript': 'jest',
                'typescript': 'jest',
                'java': 'junit',
                'csharp': 'nunit',
                'go': 'testing'
            }
            framework = framework_map.get(language, 'standard')
        
        # Create test generation prompt
        prompt = f"""
Please generate comprehensive unit tests for the following {language} code using {framework}:

Source Code:
{code}

Requirements:
- Test all public functions/methods
- Include edge cases and error conditions
- Use {framework} testing framework
- Add descriptive test names
- Include setup/teardown if needed
- Test both positive and negative scenarios
- Add comments explaining test purposes

Please provide complete, runnable test code.
"""
        
        # Get AI response
        response = self.ask_ai(prompt)
        if not response:
            self.show_notification("Failed to generate tests", level='error')
            return False
        
        self.presenter.show_header(f"Generated tests for {file_path}", subtitle=f"Framework: {framework}", icon="🧪")
        
        # Extract and display code with proper syntax highlighting
        code_content = self._extract_code_from_response(response)
        self.show_code(code_content, language=language)
        
        # Save to file if requested
        output_file = args.output
        if not output_file:
            # Generate default test filename
            base_name = os.path.splitext(os.path.basename(file_path))[0]
            ext = self.get_file_extension(file_path)
            output_file = f"test_{base_name}{ext}"
        
        if self.confirm_action(f"Save tests to {output_file}?"):
            if self.write_file(output_file, code_content):
                self.show_notification(f"Tests saved to {output_file}", level='success')
            else:
                self.show_notification(f"Failed to save to {output_file}", level='error')
        
        return True
    
    def project(self, args: Any) -> bool:
        """Scaffold a new project."""
        project_name = args.name
        project_type = args.type or 'library'
        language = args.language
        dry_run = getattr(args, 'dry_run', False)
        
        self.show_notification(f"Scaffolding {language} {project_type} project: {project_name}", level='info')
        
        # Check if project directory already exists
        if os.path.exists(project_name):
            self.show_notification(f"Directory '{project_name}' already exists", level='error')
            return False
        
        # Create project generation prompt
        prompt = f"""
Please create a complete project structure for a {language} {project_type} project named "{project_name}".

Requirements:
- Create appropriate directory structure
- Include necessary configuration files
- Add a basic README.md
- Include package/dependency management files
- Add basic source code structure
- Include appropriate .gitignore
- Add any necessary build/setup files

Project Type: {project_type}
Language: {language}
Name: {project_name}

Please provide the response in the following format:
1. Start with "PROJECT STRUCTURE:" followed by a directory tree
2. Then for each file, use the format:
   FILE: path/to/file.ext
   CONTENT:
   [file content here]
   END_FILE

This format will allow automatic project creation.
"""
        
        # Get AI response
        response = self.ask_ai(prompt)
        if not response:
            self.show_notification("Failed to generate project structure", level='error')
            return False
        
        # Parse the AI response to extract files and structure
        project_files = self._parse_project_response(response)
        
        if not project_files:
            # Fallback to manual mode if parsing fails
            self.presenter.show_header(f"Project structure for {project_name}", icon="📁")
            self.show_markdown(response)
            
            if self.confirm_action(f"Could not parse project structure automatically. Show manual instructions?"):
                self.show_notification("To create the project, please manually create the directories and files as shown above.", level='info')
            return True
        
        # Display what will be created
        self.presenter.show_header(f"Project structure for {project_name}", icon="📁")
        self._display_project_structure(project_files)
        
        if dry_run:
            self.show_notification("Dry run mode - no files will be created", level='info')
            return True
        
        # Ask for confirmation before creating files
        if not self.confirm_action(f"Create project '{project_name}' with {len(project_files)} files?"):
            self.show_notification("Project creation cancelled", level='info')
            return True
        
        # Create the project
        success = self._create_project_files(project_name, project_files)
        
        if success:
            self.show_notification(f"Project '{project_name}' created successfully!", level='success')
            self._show_next_steps(project_name, language, project_type)
        else:
            self.show_notification("Failed to create some project files", level='error')
        
        return success
    
    def _parse_project_response(self, response: str) -> List[Tuple[str, str]]:
        """Parse AI response to extract file paths and contents."""
        project_files = []
        lines = response.split('\n')
        current_file = None
        current_content = []
        in_content = False
        
        for line in lines:
            line_stripped = line.strip()
            
            # Look for file declarations
            if line_stripped.startswith('FILE:'):
                # Save previous file if exists
                if current_file and current_content:
                    content = '\n'.join(current_content).strip()
                    project_files.append((current_file, content))
                
                # Start new file
                current_file = line_stripped[5:].strip()
                current_content = []
                in_content = False
                continue
            
            # Look for content start
            if line_stripped == 'CONTENT:':
                in_content = True
                continue
            
            # Look for content end
            if line_stripped == 'END_FILE':
                if current_file and current_content:
                    content = '\n'.join(current_content).strip()
                    project_files.append((current_file, content))
                current_file = None
                current_content = []
                in_content = False
                continue
            
            # Collect content lines
            if in_content and current_file:
                current_content.append(line)
        
        # Handle last file if no END_FILE marker
        if current_file and current_content:
            content = '\n'.join(current_content).strip()
            project_files.append((current_file, content))
        
        # If no structured format found, try to extract from markdown code blocks
        if not project_files:
            project_files = self._extract_files_from_markdown(response)
        
        return project_files
    
    def _extract_files_from_markdown(self, response: str) -> List[Tuple[str, str]]:
        """Extract files from markdown code blocks as fallback."""
        project_files = []
        lines = response.split('\n')
        current_file = None
        current_content = []
        in_code_block = False
        
        for i, line in enumerate(lines):
            line_stripped = line.strip()
            
            # Look for file path indicators before code blocks
            if not in_code_block and ('/' in line_stripped or line_stripped.endswith('.py') or 
                                    line_stripped.endswith('.js') or line_stripped.endswith('.json') or
                                    line_stripped.endswith('.md') or line_stripped.endswith('.txt')):
                # Check if next line is a code block
                if i + 1 < len(lines) and lines[i + 1].strip().startswith('```'):
                    current_file = line_stripped.replace('**', '').replace('*', '').strip()
                    continue
            
            # Handle code blocks
            if line_stripped.startswith('```'):
                if not in_code_block:
                    in_code_block = True
                    current_content = []
                else:
                    in_code_block = False
                    if current_file and current_content:
                        content = '\n'.join(current_content).strip()
                        project_files.append((current_file, content))
                        current_file = None
                        current_content = []
                continue
            
            if in_code_block:
                current_content.append(line)
        
        return project_files
    
    def _display_project_structure(self, project_files: List[Tuple[str, str]]):
        """Display the project structure in a tree format."""
        if not project_files:
            return
        
        # Create tree structure for presenter
        tree_data = self._build_tree_structure(project_files)
        self.show_tree(tree_data, title="Project structure")
        
        # Also show file details in a table
        file_data = []
        for file_path, content in project_files:
            file_data.append({
                'File': file_path,
                'Type': self._get_file_type(file_path),
                'Size': f"{len(content)} chars",
                'Lines': str(content.count('\n') + 1) if content else '0'
            })
        
        if file_data:
            self.show_table(file_data, title="Project Files")
    
    def _build_tree_structure(self, project_files: List[Tuple[str, str]]) -> Dict[str, Any]:
        """Build a hierarchical tree structure from file paths."""
        tree = {}
        
        for file_path, _ in project_files:
            parts = file_path.split('/')
            current = tree
            
            # Navigate/create directory structure
            for i, part in enumerate(parts[:-1]):
                if part not in current:
                    current[part] = {}
                current = current[part]
            
            # Add the file
            filename = parts[-1]
            current[filename] = None  # None indicates it's a file, not a directory
        
        return tree
    
    def _create_project_files(self, project_name: str, project_files: List[Tuple[str, str]]) -> bool:
        """Create the actual project files and directories."""
        try:
            # Create project root directory
            os.makedirs(project_name, exist_ok=True)
            self.show_notification(f"Created project directory: {project_name}", level='info')
            
            created_files = 0
            failed_files = 0
            
            for file_path, content in project_files:
                try:
                    # Construct full path
                    full_path = os.path.join(project_name, file_path)
                    
                    # Create directory if needed
                    dir_path = os.path.dirname(full_path)
                    if dir_path:
                        os.makedirs(dir_path, exist_ok=True)
                    
                    # Write file
                    with open(full_path, 'w', encoding='utf-8') as f:
                        f.write(content)
                    
                    created_files += 1
                    self.logger.debug(f"Created file: {file_path}")
                    
                except Exception as e:
                    self.logger.error(f"Failed to create file {file_path}: {e}")
                    self.show_notification(f"Failed to create {file_path}: {e}", level='warning')
                    failed_files += 1
            
            self.show_notification(f"Created {created_files} files", level='info')
            if failed_files > 0:
                self.show_notification(f"Failed to create {failed_files} files", level='warning')
            
            return failed_files == 0
            
        except Exception as e:
            self.logger.error(f"Failed to create project: {e}")
            self.show_notification(f"Failed to create project: {e}", level='error')
            return False
    
    def _show_next_steps(self, project_name: str, language: str, project_type: str):
        """Show next steps after project creation."""
        self.presenter.show_header(f"Next steps for your {language} {project_type} project", icon="🎉")
        
        # Build markdown content for next steps
        markdown_content = f"# Next Steps\n\n"
        steps = [f"1. `cd {project_name}`"]
        
        # Language-specific next steps
        if language.lower() == 'python':
            steps.extend([
                "2. `python -m venv venv`",
                "3. `source venv/bin/activate`  # On Windows: `venv\\Scripts\\activate`",
                "4. `pip install -r requirements.txt`"
            ])
            if project_type == 'web':
                steps.append("5. `python app.py`")
            elif project_type == 'cli':
                steps.append("5. `python main.py --help`")
            else:
                steps.append("5. `python -m pytest tests/`")
        
        elif language.lower() in ['javascript', 'typescript']:
            steps.append("2. `npm install`")
            if project_type == 'web':
                steps.append("3. `npm start`")
            elif project_type == 'api':
                steps.append("3. `npm run dev`")
            else:
                steps.append("3. `npm test`")
        
        elif language.lower() == 'java':
            steps.extend([
                "2. `mvn clean install`",
                "3. `mvn test`"
            ])
            if project_type == 'web':
                steps.append("4. `mvn spring-boot:run`")
        
        elif language.lower() == 'go':
            steps.extend([
                "2. `go mod tidy`",
                "3. `go test ./...`"
            ])
            if project_type in ['web', 'api']:
                steps.append("4. `go run main.go`")
        
        else:
            steps.append("2. Follow the setup instructions in README.md")
        
        # Build markdown content
        for step in steps:
            markdown_content += f"{step}\n"
        
        markdown_content += f"\n---\n\n📖 **Check the README.md file in {project_name}/ for detailed instructions!**"
        
        # Display as markdown for rich formatting
        self.show_markdown(markdown_content)
    
    def _extract_code_from_response(self, response: str) -> str:
        """Extract code content from AI response (simplified)."""
        # This is a simplified extraction
        # In practice, you'd parse markdown code blocks properly
        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 _get_file_type(self, file_path: str) -> str:
        """Get a human-readable file type from file path."""
        ext = self.get_file_extension(file_path)
        type_map = {
            '.py': 'Python',
            '.js': 'JavaScript',
            '.ts': 'TypeScript',
            '.java': 'Java',
            '.cpp': 'C++',
            '.c': 'C',
            '.cs': 'C#',
            '.go': 'Go',
            '.rs': 'Rust',
            '.php': 'PHP',
            '.rb': 'Ruby',
            '.swift': 'Swift',
            '.kt': 'Kotlin',
            '.scala': 'Scala',
            '.sh': 'Shell',
            '.sql': 'SQL',
            '.html': 'HTML',
            '.css': 'CSS',
            '.json': 'JSON',
            '.yaml': 'YAML',
            '.yml': 'YAML',
            '.xml': 'XML',
            '.md': 'Markdown',
            '.txt': 'Text',
            '.gitignore': 'Git',
            '.dockerfile': 'Docker',
            '': 'Config'
        }
        
        if not ext and ('readme' in file_path.lower() or 'license' in file_path.lower()):
            return 'Documentation'
        
        return type_map.get(ext, 'Unknown')
