"""
Code Generator - Converts recorded actions into executable Qastra test code.
"""

import os
import time
from typing import List, Dict, Any, Optional
from datetime import datetime


class CodeGenerator:
    """Generates Qastra test code from recorded actions."""
    
    def __init__(self, output_dir: str = "generated_tests"):
        self.output_dir = output_dir
        os.makedirs(output_dir, exist_ok=True)
    
    def generate_test(self, actions: List[Dict[str, Any]], url: str, test_name: Optional[str] = None) -> str:
        """
        Generate a complete test file from recorded actions.
        
        Args:
            actions: List of recorded actions
            url: Starting URL for the test
            test_name: Optional name for the test file
            
        Returns:
            Path to generated test file
        """
        if not actions:
            raise ValueError("No actions to generate test from")
        
        # Generate test name if not provided
        if not test_name:
            test_name = self._generate_test_name(url)
        
        # Generate test code
        test_code = self._generate_test_code(actions, url, test_name)
        
        # Save test file
        test_file = os.path.join(self.output_dir, f"{test_name}.py")
        
        with open(test_file, 'w') as f:
            f.write(test_code)
        
        # Generate metadata
        metadata = self._generate_metadata(actions, url, test_name, test_file)
        metadata_file = os.path.join(self.output_dir, f"{test_name}_metadata.json")
        
        import json
        with open(metadata_file, 'w') as f:
            json.dump(metadata, f, indent=2)
        
        print(f"✅ Test generated: {test_file}")
        print(f"📄 Metadata saved: {metadata_file}")
        
        return test_file
    
    def _generate_test_name(self, url: str) -> str:
        """Generate a test name from URL."""
        try:
            from urllib.parse import urlparse
            domain = urlparse(url).netloc.replace('www.', '')
            clean_domain = ''.join(c for c in domain if c.isalnum() or c in ('-', '_'))
            
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            return f"recorded_{clean_domain}_{timestamp}"
        
        except:
            return f"recorded_test_{int(time.time())}"
    
    def _generate_test_code(self, actions: List[Dict[str, Any]], url: str, test_name: str) -> str:
        """Generate the actual Python test code."""
        
        # Header and imports
        header = f'''#!/usr/bin/env python3
"""
Auto-generated test from Qastra Recorder
Generated on: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
URL: {url}
Test name: {test_name}
"""

from playwright.sync_api import sync_playwright
from qastra.engine.action_wrapper import click, fill, select, wait_for

def {test_name}(page):
    """
    Auto-generated test from recorded actions.
    
    This test was generated by Qastra's AI Test Recorder.
    It uses smart locators to find elements by intent, making it resilient to UI changes.
    """
    
    # Navigate to starting page
    page.goto("{url}")
    page.wait_for_load_state("networkidle")
    
    # Wait for page to be ready
    page.wait_for_timeout(2000)
    
'''
        
        # Generate action code
        action_code = self._generate_action_code(actions)
        
        # Footer
        footer = '''
    print("✅ Recorded test completed successfully!")

if __name__ == "__main__":
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=False)
        page = browser.new_page()
        
        try:
            {test_name}(page)
        except Exception as e:
            print(f"❌ Test failed: {{e}}")
            raise
        finally:
            browser.close()
'''
        
        return header + action_code + footer
    
    def _generate_action_code(self, actions: List[Dict[str, Any]]) -> str:
        """Generate Python code for recorded actions."""
        code_lines = []
        last_action_time = 0
        
        for i, action in enumerate(actions):
            action_type = action['type']
            intent = action.get('intent', '')
            
            # Add wait between actions if there was a significant pause
            if i > 0 and 'relative_time' in action:
                time_diff = action['relative_time'] - last_action_time
                if time_diff > 2000:  # 2 seconds pause
                    code_lines.append(f"    page.wait_for_timeout({int(time_diff)})")
            
            if action_type == 'click':
                code_lines.append(f'    click(page, "{intent}")')
            
            elif action_type == 'fill':
                value = action.get('value', '')
                # Escape quotes in value
                escaped_value = value.replace('"', '\\"')
                code_lines.append(f'    fill(page, "{intent}", "{escaped_value}")')
            
            elif action_type == 'select':
                value = action.get('value', '')
                escaped_value = value.replace('"', '\\"')
                code_lines.append(f'    select(page, "{intent}", "{escaped_value}")')
            
            elif action_type == 'navigate':
                nav_url = action.get('url', '')
                code_lines.append(f'    page.goto("{nav_url}")')
                code_lines.append('    page.wait_for_load_state("networkidle")')
            
            elif action_type == 'submit':
                code_lines.append('    # Form submission detected')
                # Add wait for navigation after submit
                code_lines.append('    page.wait_for_timeout(3000)')
            
            elif action_type == 'scroll':
                code_lines.append('    # Scroll action detected')
                code_lines.append('    page.wait_for_timeout(500)')
            
            # Update last action time
            if 'relative_time' in action:
                last_action_time = action['relative_time']
        
        return '\n'.join(code_lines) if code_lines else '    # No actions recorded'
    
    def _generate_metadata(self, actions: List[Dict[str, Any]], url: str, test_name: str, test_file: str) -> Dict[str, Any]:
        """Generate metadata for the recorded test."""
        
        # Count action types
        action_types = {}
        intents = []
        
        for action in actions:
            action_type = action['type']
            action_types[action_type] = action_types.get(action_type, 0) + 1
            
            if 'intent' in action:
                intents.append(action['intent'])
        
        # Calculate duration
        duration = 0
        if len(actions) > 1:
            first_action = actions[0]
            last_action = actions[-1]
            duration = (last_action['timestamp'] - first_action['timestamp']) / 1000
        
        return {
            'test_name': test_name,
            'test_file': test_file,
            'url': url,
            'generated_at': datetime.now().isoformat(),
            'total_actions': len(actions),
            'duration_seconds': duration,
            'action_types': action_types,
            'intents': list(set(intents)),
            'unique_intents': len(set(intents)),
            'actions': actions
        }
    
    def generate_natural_language_test(self, actions: List[Dict[str, Any]], url: str) -> str:
        """
        Convert recorded actions into natural language test description.
        
        Args:
            actions: List of recorded actions
            url: Starting URL
            
        Returns:
            Natural language test description
        """
        if not actions:
            return "No actions recorded"
        
        nl_actions = []
        
        for action in actions:
            action_type = action['type']
            intent = action.get('intent', '')
            
            if action_type == 'click':
                nl_actions.append(f"click {intent}")
            elif action_type == 'fill':
                value = action.get('value', '')
                nl_actions.append(f"fill {intent} with {value}")
            elif action_type == 'select':
                value = action.get('value', '')
                nl_actions.append(f"select {value} in {intent}")
            elif action_type == 'navigate':
                nav_url = action.get('url', '')
                nl_actions.append(f"navigate to {nav_url}")
            elif action_type == 'submit':
                nl_actions.append("submit form")
        
        # Join actions with natural flow
        if len(nl_actions) == 1:
            return nl_actions[0]
        elif len(nl_actions) == 2:
            return f"{nl_actions[0]} and {nl_actions[1]}"
        else:
            return ", ".join(nl_actions[:-1]) + f", and {nl_actions[-1]}"
    
    def optimize_actions(self, actions: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
        """
        Optimize recorded actions by removing redundant ones and combining related actions.
        
        Args:
            actions: List of recorded actions
            
        Returns:
            Optimized list of actions
        """
        if not actions:
            return actions
        
        optimized = []
        
        # Remove duplicate actions in sequence
        for i, action in enumerate(actions):
            if i == 0:
                optimized.append(action)
                continue
            
            prev_action = optimized[-1]
            
            # Skip if same action type and intent as previous
            if (action['type'] == prev_action['type'] and 
                action.get('intent') == prev_action.get('intent')):
                continue
            
            # Skip rapid successive clicks on same element
            if (action['type'] == 'click' and prev_action['type'] == 'click' and
                action.get('intent') == prev_action.get('intent') and
                'relative_time' in action and 'relative_time' in prev_action and
                action['relative_time'] - prev_action['relative_time'] < 500):
                continue
            
            optimized.append(action)
        
        return optimized
    
    def generate_test_variants(self, actions: List[Dict[str, Any]], url: str, test_name: str) -> List[str]:
        """
        Generate multiple test variants from recorded actions.
        
        Args:
            actions: List of recorded actions
            url: Starting URL
            test_name: Base test name
            
        Returns:
            List of generated test file paths
        """
        variants = []
        
        # Original test
        original_file = self.generate_test(actions, url, test_name)
        variants.append(original_file)
        
        # Optimized test
        optimized_actions = self.optimize_actions(actions)
        if len(optimized_actions) != len(actions):
            optimized_name = f"{test_name}_optimized"
            optimized_file = self.generate_test(optimized_actions, url, optimized_name)
            variants.append(optimized_file)
        
        # Natural language version
        nl_description = self.generate_natural_language_test(actions, url)
        nl_test_name = f"{test_name}_nl"
        nl_code = self._generate_natural_language_test_code(nl_description, url, nl_test_name)
        
        nl_file = os.path.join(self.output_dir, f"{nl_test_name}.py")
        with open(nl_file, 'w') as f:
            f.write(nl_code)
        
        variants.append(nl_file)
        print(f"✅ Natural language test generated: {nl_file}")
        
        return variants
    
    def _generate_natural_language_test_code(self, nl_description: str, url: str, test_name: str) -> str:
        """Generate test code using natural language approach."""
        
        return f'''#!/usr/bin/env python3
"""
Natural language test generated by Qastra Recorder
Generated on: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
URL: {url}
Test: {nl_description}
"""

from playwright.sync_api import sync_playwright
from qastra.ai.nlp.executor import execute_test

def {test_name}():
    """
    Natural language test generated from recorded actions.
    
    Test: {nl_description}
    """
    
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=False)
        page = browser.new_page()
        
        try:
            # Execute natural language test
            execute_test("{nl_description}", "{url}")
            
        except Exception as e:
            print(f"❌ Test failed: {{e}}")
            raise
        
        finally:
            browser.close()

if __name__ == "__main__":
    {test_name}()
'''
    
    def get_recording_statistics(self, actions: List[Dict[str, Any]]) -> Dict[str, Any]:
        """
        Get detailed statistics about the recording.
        
        Args:
            actions: List of recorded actions
            
        Returns:
            Statistics dictionary
        """
        if not actions:
            return {
                'total_actions': 0,
                'unique_intents': 0,
                'action_types': {},
                'avg_time_between_actions': 0,
                'most_common_action': None
            }
        
        # Count action types
        action_types = {}
        intents = []
        time_diffs = []
        
        for i, action in enumerate(actions):
            action_type = action['type']
            action_types[action_type] = action_types.get(action_type, 0) + 1
            
            if 'intent' in action:
                intents.append(action['intent'])
            
            # Calculate time between actions
            if i > 0 and 'relative_time' in action:
                prev_action = actions[i-1]
                if 'relative_time' in prev_action:
                    time_diffs.append(action['relative_time'] - prev_action['relative_time'])
        
        # Find most common action
        most_common_action = max(action_types.items(), key=lambda x: x[1]) if action_types else None
        
        return {
            'total_actions': len(actions),
            'unique_intents': len(set(intents)),
            'action_types': action_types,
            'intents': list(set(intents)),
            'avg_time_between_actions': sum(time_diffs) / len(time_diffs) if time_diffs else 0,
            'most_common_action': most_common_action,
            'time_distribution': {
                'min': min(time_diffs) if time_diffs else 0,
                'max': max(time_diffs) if time_diffs else 0,
                'median': sorted(time_diffs)[len(time_diffs)//2] if time_diffs else 0
            }
        }


# Convenience functions
def generate_test(actions: List[Dict[str, Any]], url: str, test_name: Optional[str] = None, 
                 output_dir: str = "generated_tests") -> str:
    """Quick function to generate test from actions."""
    generator = CodeGenerator(output_dir)
    return generator.generate_test(actions, url, test_name)


def generate_natural_language_test(actions: List[Dict[str, Any]], url: str) -> str:
    """Quick function to generate natural language description."""
    generator = CodeGenerator()
    return generator.generate_natural_language_test(actions, url)
