"""
Storm Logger Smart Test Formatter
Beautiful, informative test output that helps fix issues
"""

import re
import sys
from collections import defaultdict
from dataclasses import dataclass
from typing import Dict, List, Optional, Tuple


@dataclass
class TestFailure:
    """Represents a test failure with all relevant information"""
    test_name: str
    test_file: str
    test_function: str
    error_type: str
    error_message: str
    traceback: List[str]
    line_number: Optional[int] = None
    
    @property
    def location(self) -> str:
        """Get test location in file:line format"""
        if self.line_number:
            return f"{self.test_file}:{self.line_number}"
        return self.test_file


class TestOutputFormatter:
    """Formats test output for better readability and actionability"""
    
    # ANSI color codes
    COLORS = {
        'green': '\033[38;5;78m',
        'blue': '\033[38;5;75m',
        'yellow': '\033[38;5;220m',
        'red': '\033[38;5;203m',
        'purple': '\033[38;5;141m',
        'gray': '\033[38;5;244m',
        'bold': '\033[1m',
        'dim': '\033[2m',
        'reset': '\033[0m',
    }
    
    # Unicode symbols with fallbacks
    SYMBOLS = {
        'check': '✓',
        'cross': '✗',
        'info': 'ℹ',
        'arrow': '→',
        'dot': '•',
        'box': '▸',
    }
    
    def __init__(self, use_colors: bool = True, use_unicode: bool = True):
        self.use_colors = use_colors and sys.stdout.isatty()
        self.use_unicode = use_unicode and sys.stdout.isatty()
        
        if not self.use_colors:
            self.COLORS = {k: '' for k in self.COLORS}
        
        if not self.use_unicode:
            self.SYMBOLS = {
                'check': '[OK]',
                'cross': '[FAIL]',
                'info': '[i]',
                'arrow': '->',
                'dot': '*',
                'box': '>',
            }
    
    def format_test_failure(self, failure: TestFailure) -> str:
        """Format a single test failure with helpful information"""
        output = []
        
        # Header with test name
        output.append(f"\n{self.color('red')}{self.SYMBOLS['cross']} "
                     f"{self.color('bold')}{failure.test_name}{self.color('reset')}")
        
        # Location
        output.append(f"   {self.color('gray')}Location: {failure.location}{self.color('reset')}")
        
        # Error type and message
        output.append(f"   {self.color('yellow')}Error: {failure.error_type}{self.color('reset')}")
        if failure.error_message:
            # Clean up error message
            msg = self._clean_error_message(failure.error_message)
            output.append(f"   {msg}")
        
        # Relevant code snippet if available
        code_snippet = self._extract_code_snippet(failure)
        if code_snippet:
            output.append(f"\n   {self.color('gray')}Code:{self.color('reset')}")
            output.append(code_snippet)
        
        # Suggestion
        suggestion = self._generate_suggestion(failure)
        if suggestion:
            output.append(f"\n   {self.color('yellow')}{self.SYMBOLS['info']} "
                         f"Suggestion:{self.color('reset')}")
            output.append(suggestion)
        
        return '\n'.join(output)
    
    def format_failure_summary(self, failures: List[TestFailure]) -> str:
        """Format a summary of all failures grouped by error type"""
        if not failures:
            return ""
        
        output = []
        output.append(f"\n{self.color('bold')}━━━ Failure Summary ━━━{self.color('reset')}\n")
        
        # Group failures by error type
        grouped = defaultdict(list)
        for failure in failures:
            grouped[failure.error_type].append(failure)
        
        # Display grouped failures
        for error_type, group in grouped.items():
            output.append(f"{self.color('red')}{self.SYMBOLS['dot']} "
                         f"{error_type} ({len(group)} test{'s' if len(group) > 1 else ''})"
                         f"{self.color('reset')}")
            
            for failure in group[:3]:  # Show max 3 per group
                output.append(f"   {self.color('gray')}{self.SYMBOLS['arrow']} "
                             f"{failure.test_name}{self.color('reset')}")
            
            if len(group) > 3:
                output.append(f"   {self.color('gray')}... and {len(group) - 3} more"
                             f"{self.color('reset')}")
            output.append("")
        
        return '\n'.join(output)
    
    def format_fix_command(self, failures: List[TestFailure]) -> str:
        """Generate helpful commands to fix issues"""
        output = []
        output.append(f"\n{self.color('bold')}━━━ Quick Fixes ━━━{self.color('reset')}\n")
        
        # Check for import errors
        import_errors = [f for f in failures if 'ImportError' in f.error_type]
        if import_errors:
            output.append(f"{self.color('blue')}{self.SYMBOLS['box']} "
                         f"Fix imports:{self.color('reset')}")
            output.append(f"   {self.color('gray')}./test_smart.sh --fix{self.color('reset')}")
            output.append("")
        
        # Check for assertion errors
        assertion_errors = [f for f in failures if 'AssertionError' in f.error_type]
        if assertion_errors:
            output.append(f"{self.color('blue')}{self.SYMBOLS['box']} "
                         f"Debug assertions:{self.color('reset')}")
            output.append(f"   {self.color('gray')}./test_smart.sh --failed -v{self.color('reset')}")
            output.append("")
        
        # General re-run command
        output.append(f"{self.color('blue')}{self.SYMBOLS['box']} "
                     f"Re-run failed tests only:{self.color('reset')}")
        output.append(f"   {self.color('gray')}./test_smart.sh --failed{self.color('reset')}")
        
        return '\n'.join(output)
    
    def color(self, color_name: str) -> str:
        """Get color code"""
        return self.COLORS.get(color_name, '')
    
    def _clean_error_message(self, message: str) -> str:
        """Clean up error message for better readability"""
        # Remove excessive whitespace
        message = ' '.join(message.split())
        
        # Truncate if too long
        if len(message) > 200:
            message = message[:197] + '...'
        
        # Highlight important parts
        message = re.sub(r"'([^']+)'", f"{self.color('bold')}\\1{self.color('reset')}", message)
        
        return message
    
    def _extract_code_snippet(self, failure: TestFailure) -> Optional[str]:
        """Extract relevant code snippet from failure"""
        # This would need access to the actual test file
        # For now, return None
        return None
    
    def _generate_suggestion(self, failure: TestFailure) -> Optional[str]:
        """Generate helpful suggestion based on error type"""
        suggestions = {
            'AssertionError': [
                f"   {self.color('gray')}{self.SYMBOLS['arrow']} Check expected vs actual values",
                f"   {self.SYMBOLS['arrow']} Add debug print statements",
                f"   {self.SYMBOLS['arrow']} Use pytest -vv for detailed diff{self.color('reset')}"
            ],
            'ImportError': [
                f"   {self.color('gray')}{self.SYMBOLS['arrow']} Run: pip install -e .[dev]",
                f"   {self.SYMBOLS['arrow']} Check PYTHONPATH includes project root",
                f"   {self.SYMBOLS['arrow']} Verify module name spelling{self.color('reset')}"
            ],
            'AttributeError': [
                f"   {self.color('gray')}{self.SYMBOLS['arrow']} Check if method/property exists",
                f"   {self.SYMBOLS['arrow']} Verify object initialization",
                f"   {self.SYMBOLS['arrow']} Check for typos in attribute name{self.color('reset')}"
            ],
            'KeyError': [
                f"   {self.color('gray')}{self.SYMBOLS['arrow']} Print dictionary keys to debug",
                f"   {self.SYMBOLS['arrow']} Check fixture return value",
                f"   {self.SYMBOLS['arrow']} Use .get() with default value{self.color('reset')}"
            ],
            'TypeError': [
                f"   {self.color('gray')}{self.SYMBOLS['arrow']} Check function arguments",
                f"   {self.SYMBOLS['arrow']} Verify parameter types",
                f"   {self.SYMBOLS['arrow']} Review function signature{self.color('reset')}"
            ],
        }
        
        # Find matching suggestion
        for error_key, suggestion_list in suggestions.items():
            if error_key in failure.error_type:
                return '\n'.join(suggestion_list)
        
        # Default suggestion
        return (f"   {self.color('gray')}{self.SYMBOLS['arrow']} "
                f"Run with -v flag for detailed traceback{self.color('reset')}")


class TestProgressReporter:
    """Real-time test progress reporter"""
    
    def __init__(self, formatter: TestOutputFormatter):
        self.formatter = formatter
        self.total_tests = 0
        self.completed_tests = 0
        self.failed_tests = 0
        self.current_file = ""
    
    def update(self, test_name: str, status: str):
        """Update progress with test result"""
        self.completed_tests += 1
        
        if status == 'FAILED':
            self.failed_tests += 1
        
        # Extract file name
        file_match = re.match(r'(tests/[^:]+)', test_name)
        if file_match:
            new_file = file_match.group(1)
            if new_file != self.current_file:
                self.current_file = new_file
                self._print_file_header(new_file)
        
        # Print test result
        self._print_test_result(test_name, status)
    
    def _print_file_header(self, file_name: str):
        """Print file header"""
        print(f"\n{self.formatter.color('blue')}"
              f"{self.formatter.SYMBOLS['box']} {file_name}"
              f"{self.formatter.color('reset')}")
    
    def _print_test_result(self, test_name: str, status: str):
        """Print individual test result"""
        # Extract just the test function name
        func_match = re.search(r'::([^:]+)$', test_name)
        func_name = func_match.group(1) if func_match else test_name
        
        if status == 'PASSED':
            symbol = self.formatter.SYMBOLS['check']
            color = 'green'
        elif status == 'FAILED':
            symbol = self.formatter.SYMBOLS['cross']
            color = 'red'
        elif status == 'SKIPPED':
            symbol = self.formatter.SYMBOLS['arrow']
            color = 'yellow'
        else:
            symbol = self.formatter.SYMBOLS['dot']
            color = 'gray'
        
        print(f"   {self.formatter.color(color)}{symbol}{self.formatter.color('reset')} "
              f"{func_name}")


def parse_pytest_output(output: str) -> Tuple[Dict[str, int], List[TestFailure]]:
    """Parse pytest output to extract statistics and failures"""
    stats = {
        'total': 0,
        'passed': 0,
        'failed': 0,
        'skipped': 0,
        'errors': 0,
    }
    
    failures = []
    
    # Parse summary line
    summary_match = re.search(
        r'(\d+) passed|(\d+) failed|(\d+) skipped|(\d+) error',
        output
    )
    
    if summary_match:
        if summary_match.group(1):
            stats['passed'] = int(summary_match.group(1))
        if summary_match.group(2):
            stats['failed'] = int(summary_match.group(2))
        if summary_match.group(3):
            stats['skipped'] = int(summary_match.group(3))
        if summary_match.group(4):
            stats['errors'] = int(summary_match.group(4))
    
    stats['total'] = sum(stats.values())
    
    # Parse failures
    failure_pattern = re.compile(
        r'FAILED (tests/[^:]+)::([^:]+)::([^:]+)\s*-\s*(.+?):\s*(.+)'
    )
    
    for match in failure_pattern.finditer(output):
        failure = TestFailure(
            test_name=f"{match.group(2)}::{match.group(3)}",
            test_file=match.group(1),
            test_function=match.group(3),
            error_type=match.group(4),
            error_message=match.group(5),
            traceback=[]
        )
        failures.append(failure)
    
    return stats, failures


# Example usage for testing
if __name__ == "__main__":
    formatter = TestOutputFormatter()
    
    # Example failure
    failure = TestFailure(
        test_name="TestConfig::test_validation",
        test_file="tests/unit/test_config.py",
        test_function="test_validation",
        error_type="AssertionError",
        error_message="assert 'expected' == 'actual'",
        traceback=[],
        line_number=42
    )
    
    print(formatter.format_test_failure(failure))
    print(formatter.format_failure_summary([failure]))
    print(formatter.format_fix_command([failure]))