#!/usr/bin/env python3
"""
Base target interface for the build system.
Defines the abstract Target class that all specific targets must implement.
"""

from abc import ABC, abstractmethod
from pathlib import Path
from typing import List, Optional
from enum import Enum


class BuildType(Enum):
    """Type of build to perform."""
    RELEASE = "release"
    DEBUG = "debug"
    TEST = "test"
    
    def __str__(self) -> str:
        return self.value


class Target(ABC):
    """Abstract base class for all build targets."""
    
    def __init__(self, name: str):
        """
        Initialize the target.
        
        Args:
            name: Target name (e.g., "linux", "windows", "rp2040")
        """
        self.name = name
        self._build_type = BuildType.RELEASE
    
    @property
    def build_type(self) -> BuildType:
        """Get the current build type."""
        return self._build_type
    
    @build_type.setter
    def build_type(self, value: BuildType) -> None:
        """Set the build type."""
        self._build_type = value
    
    @abstractmethod
    def build(self) -> bool:
        """
        Build the target.
        
        Returns:
            True if build succeeded, False otherwise
        """
        pass
    
    @abstractmethod
    def clean(self) -> bool:
        """
        Clean build artifacts for this target.
        
        Returns:
            True if clean succeeded, False otherwise
        """
        pass
    
    def run(self) -> bool:
        """
        Run the built executable.
        
        Returns:
            True if execution succeeded, False otherwise
        
        Raises:
            NotImplementedError: If the target doesn't support running
        """
        raise NotImplementedError(f"Target '{self.name}' does not support running")
    
    def test(self) -> bool:
        """
        Build and run tests for this target.
        
        Returns:
            True if tests passed, False otherwise
        
        Raises:
            NotImplementedError: If the target doesn't support testing
        """
        raise NotImplementedError(f"Target '{self.name}' does not support testing")
    
    def debug(self) -> bool:
        """
        Launch debugger with the built executable.
        
        Returns:
            True if debugger launched successfully, False otherwise
        
        Raises:
            NotImplementedError: If the target doesn't support debugging
        """
        raise NotImplementedError(f"Target '{self.name}' does not support debugging")
    
    def get_output_path(self) -> Optional[Path]:
        """
        Get the path to the main output binary.
        
        Returns:
            Path to output binary, or None if not applicable
        """
        return None
    
    def get_test_output_path(self) -> Optional[Path]:
        """
        Get the path to the test binary.
        
        Returns:
            Path to test binary, or None if not applicable
        """
        return None
    
    def get_build_dir(self) -> Optional[Path]:
        """
        Get the build directory for this target.
        
        Returns:
            Path to build directory, or None if using shared directories
        """
        return None
    
    def supports_command(self, command: str) -> bool:
        """
        Check if this target supports a specific command.
        
        Args:
            command: Command name ("build", "run", "test", "debug", "clean")
        
        Returns:
            True if command is supported, False otherwise
        """
        if command == "build":
            return True
        elif command == "clean":
            return True
        elif command == "run":
            try:
                # Check if run() is overridden
                return type(self).run != Target.run
            except AttributeError:
                return False
        elif command == "test":
            try:
                return type(self).test != Target.test
            except AttributeError:
                return False
        elif command == "debug":
            try:
                return type(self).debug != Target.debug
            except AttributeError:
                return False
        return False
    
    def get_sources(self, exclude: Optional[List[str]] = None) -> List[Path]:
        """
        Get list of source files to compile.
        
        Args:
            exclude: List of source file names to exclude
        
        Returns:
            List of source file paths
        """
        from build_system.config import get_config
        config = get_config()
        
        sources = list(config.src_dir.glob("*.c"))
        
        if exclude:
            sources = [s for s in sources if s.name not in exclude]
        
        return sources
    
    def get_test_sources(self) -> List[Path]:
        """
        Get list of test source files.
        
        Returns:
            List of test source file paths
        """
        from build_system.config import get_config
        config = get_config()
        
        if not config.test_dir.exists():
            return []
        
        return list(config.test_dir.glob("*.c"))
    
    def generate_tests(self) -> bool:
        """
        Generate test files from YAML if needed.
        
        Returns:
            True if generation succeeded or was not needed, False otherwise
        """
        from build_system.config import get_config
        from build_system.utils import run
        
        config = get_config()
        
        script = config.test_script_path
        yaml = config.test_yaml_path
        output = config.test_output_file
        
        if not script.exists() or not yaml.exists():
            print("Test generation skipped (missing files).")
            return False
        
        if output.exists() and output.stat().st_mtime > yaml.stat().st_mtime:
            return True
        
        try:
            run([config.python_command, str(script), str(yaml), str(output)])
            return True
        except Exception as e:
            print(f"ERROR: Test generation failed: {e}")
            return False
    
    def __str__(self) -> str:
        """String representation of the target."""
        return f"{self.name}"
    
    def __repr__(self) -> str:
        """Detailed string representation."""
        return f"Target(name='{self.name}', build_type={self.build_type})"
