"""Enhanced error formatting for IncludeCPP build system.

Provides user-friendly error messages with context and suggestions.
"""

from typing import List, Optional
import re


class BuildErrorFormatter:
    """Format C++ compilation errors with context and suggestions."""

    @staticmethod
    def format_type_mismatch(expected: str, got: str, location: str) -> str:
        """Format type mismatch error with suggestions.

        Args:
            expected: Expected type string
            got: Actual type string
            location: Location of error (file:line)

        Returns:
            Formatted error message with suggestions
        """
        return f"""
╔══════════════════════════════════════════════════════════════╗
║ TYPE MISMATCH ERROR                                          ║
╚══════════════════════════════════════════════════════════════╝

Location: {location}

Expected: {expected}
Got:      {got}

Suggestions:
  • Check if you're passing the correct type
  • For struct → dict: use struct_instance.to_dict()
  • For dict → struct: use StructName.from_dict(dict_instance)
  • For vector conversions: Python list ↔ std::vector is automatic

Type Conversion Examples:
  # Struct to dict
  point_dict = point.to_dict()

  # Dict to struct
  point = Point.from_dict({{"x": 10, "y": 20}})

  # Vector of structs
  points: List[Point] = [...]  # Automatically converts
"""

    @staticmethod
    def format_dependency_error(module: str, missing_dep: str) -> str:
        """Format missing dependency error.

        Args:
            module: Module name that has missing dependency
            missing_dep: Name of missing dependency module

        Returns:
            Formatted error message with fix instructions
        """
        return f"""
╔══════════════════════════════════════════════════════════════╗
║ DEPENDENCY ERROR                                             ║
╚══════════════════════════════════════════════════════════════╝

Module: {module}
Missing Dependency: {missing_dep}

To Fix:
  1. Add dependency to {module}.cp:

     SOURCE(path/to/{module}.cpp) {module}
     DEPENDS({missing_dep})
     PUBLIC(
         ...
     )

  2. Ensure {missing_dep} module exists in plugins/ directory

  3. Rebuild:
     python -m includecpp rebuild --verbose

Dependency Chain:
  {module} → {missing_dep}
"""

    @staticmethod
    def format_circular_dependency(cycle: List[str]) -> str:
        """Format circular dependency error.

        Args:
            cycle: List of module names forming the cycle

        Returns:
            Formatted error message with refactoring suggestions
        """
        cycle_display = " → ".join(cycle + [cycle[0]])
        return f"""
╔══════════════════════════════════════════════════════════════╗
║ CIRCULAR DEPENDENCY DETECTED                                 ║
╚══════════════════════════════════════════════════════════════╝

Dependency Cycle:
  {cycle_display}

To Fix:
  • Refactor modules to remove circular dependency
  • Use forward declarations where possible
  • Consider merging modules if they're tightly coupled
  • Create a third module for shared types

Example Refactoring:
  Before:
    module_a → module_b → module_a  ❌

  After:
    module_a → shared_types
    module_b → shared_types         ✓
"""

    @staticmethod
    def format_module_not_found(module: str, available: List[str]) -> str:
        """Format module not found error.

        Args:
            module: Requested module name
            available: List of available module names

        Returns:
            Formatted error message
        """
        return f"""
╔══════════════════════════════════════════════════════════════╗
║ MODULE NOT FOUND                                             ║
╚══════════════════════════════════════════════════════════════╝

Requested: {module}

Available modules:
  {', '.join(available) if available else '(none)'}

To Fix:
  • Check spelling of module name
  • Ensure {module}.cp exists in plugins/ directory
  • Run 'python -m includecpp rebuild' to build all modules
  • Run 'python -m includecpp list' to see available modules
"""

    @staticmethod
    def format_build_failed(module: str, reason: str) -> str:
        """Format general build failure error.

        Args:
            module: Module name that failed to build
            reason: Reason for failure

        Returns:
            Formatted error message
        """
        return f"""
╔══════════════════════════════════════════════════════════════╗
║ BUILD FAILED                                                 ║
╚══════════════════════════════════════════════════════════════╝

Module: {module}

Reason:
  {reason}

To Debug:
  • Run with --verbose flag for detailed output
  • Check {module}.cpp for syntax errors
  • Verify all includes are available
  • Check build log in AppData/IncludeCPP/
"""

    @staticmethod
    def parse_compiler_error(stderr: str, module_name: str) -> str:
        """Parse C++ compiler errors and add helpful context.

        Args:
            stderr: Standard error output from compiler
            module_name: Name of module being compiled

        Returns:
            Formatted error messages with context
        """
        lines = stderr.split('\n')
        formatted = []
        error_count = 0

        for line in lines:
            # GCC/Clang format: file:line:col: error: message
            if ': error:' in line:
                error_count += 1
                parts = line.split(':')

                if len(parts) >= 4:
                    file_path = parts[0].strip()
                    line_num = parts[1].strip() if parts[1].strip().isdigit() else "?"
                    error_msg = ':'.join(parts[3:]).strip()

                    formatted.append("")
                    formatted.append("╔══════════════════════════════════════════╗")
                    formatted.append("║ COMPILATION ERROR                        ║")
                    formatted.append("╚══════════════════════════════════════════╝")
                    formatted.append("")
                    formatted.append(f"Module: {module_name}")
                    formatted.append(f"File: {file_path}")
                    formatted.append(f"Line: {line_num}")
                    formatted.append(f"Error: {error_msg}")
                    formatted.append("")

                    # Add context based on error type
                    if "undefined reference" in error_msg.lower() or "unresolved external" in error_msg.lower():
                        formatted.append("Suggestion:")
                        formatted.append("  • Check if function/class is declared in header")
                        formatted.append("  • Verify all source files are listed in .cp file")
                        formatted.append("  • Check for missing DEPENDS() if using types from other modules")
                        formatted.append("  • Ensure function is in 'includecpp' namespace")

                    elif "no matching function" in error_msg.lower() or "no member named" in error_msg.lower():
                        formatted.append("Suggestion:")
                        formatted.append("  • Check parameter types match function signature")
                        formatted.append("  • Verify template instantiations are correct")
                        formatted.append("  • Check for const/reference qualifiers")
                        formatted.append("  • Ensure all required headers are included")

                    elif "expected" in error_msg.lower() and "before" in error_msg.lower():
                        formatted.append("Suggestion:")
                        formatted.append("  • Check for missing semicolons")
                        formatted.append("  • Verify bracket/parenthesis matching")
                        formatted.append("  • Check for typos in type names")

                    elif "does not name a type" in error_msg.lower():
                        formatted.append("Suggestion:")
                        formatted.append("  • Check if type is defined before use")
                        formatted.append("  • Verify #include statements are correct")
                        formatted.append("  • Check namespace qualifications (std::, includecpp::)")

                    elif "incomplete type" in error_msg.lower():
                        formatted.append("Suggestion:")
                        formatted.append("  • Add forward declaration or full type definition")
                        formatted.append("  • Check if header file is included")
                        formatted.append("  • Verify struct/class definition is complete")

            # MSVC format: file(line): error C####: message
            elif re.match(r'.*\(\d+\):\s*error\s+C\d+:', line):
                error_count += 1
                match = re.match(r'(.*)\((\d+)\):\s*error\s+C\d+:\s*(.*)', line)
                if match:
                    file_path = match.group(1).strip()
                    line_num = match.group(2)
                    error_msg = match.group(3).strip()

                    formatted.append("")
                    formatted.append("╔══════════════════════════════════════════╗")
                    formatted.append("║ COMPILATION ERROR                        ║")
                    formatted.append("╚══════════════════════════════════════════╝")
                    formatted.append("")
                    formatted.append(f"Module: {module_name}")
                    formatted.append(f"File: {file_path}")
                    formatted.append(f"Line: {line_num}")
                    formatted.append(f"Error: {error_msg}")
                    formatted.append("")

            # Warning lines - pass through but mark them
            elif ': warning:' in line or 'warning C' in line:
                if not formatted or formatted[-1] != "":
                    formatted.append("")
                formatted.append(f"⚠️  {line}")

            # Note lines
            elif ': note:' in line:
                formatted.append(f"ℹ️  {line}")

            # Empty lines
            elif not line.strip():
                if formatted and formatted[-1] != "":
                    formatted.append("")

        if error_count > 0:
            header = [
                "",
                "═" * 60,
                f"COMPILATION FAILED: {error_count} error(s) in {module_name}",
                "═" * 60,
                ""
            ]
            formatted = header + formatted

        return '\n'.join(formatted) if formatted else stderr

    @staticmethod
    def format_cmake_error(stderr: str, module_name: str) -> str:
        """Format CMake configuration errors.

        Args:
            stderr: CMake error output
            module_name: Name of module being configured

        Returns:
            Formatted error message
        """
        return f"""
╔══════════════════════════════════════════════════════════════╗
║ CMAKE CONFIGURATION ERROR                                    ║
╚══════════════════════════════════════════════════════════════╝

Module: {module_name}

CMake Output:
{stderr}

Common Issues:
  • CMake not installed or not in PATH
  • C++ compiler not found (install g++, clang++, or MSVC)
  • pybind11 not found (auto-installed, check network)
  • Python development headers missing

To Fix:
  • Install CMake 3.15+ from cmake.org
  • Install C++ compiler:
    - Windows: Visual Studio or MinGW
    - Linux: apt-get install g++ cmake python3-dev
    - macOS: xcode-select --install
  • Run with --verbose for detailed CMake output
"""

    @staticmethod
    def format_import_error(module: str, error_msg: str, pyd_path: str) -> str:
        """Format Python import errors for .pyd modules.

        Args:
            module: Module name
            error_msg: Import error message
            pyd_path: Path to .pyd file

        Returns:
            Formatted error message
        """
        return f"""
╔══════════════════════════════════════════════════════════════╗
║ MODULE IMPORT ERROR                                          ║
╚══════════════════════════════════════════════════════════════╝

Module: {module}
Path: {pyd_path}

Error:
  {error_msg}

Common Causes:
  • Missing dependencies (DLLs on Windows, .so on Linux)
  • Python version mismatch (rebuild for current Python)
  • Corrupted .pyd file (rebuild module)
  • Missing Visual C++ Redistributable (Windows)

To Fix:
  1. Rebuild module:
     python -m includecpp rebuild {module} --clean

  2. Check Python version matches build:
     python --version

  3. Windows: Install Visual C++ Redistributable
     https://aka.ms/vs/17/release/vc_redist.x64.exe

  4. Run with --verbose for detailed output
"""
