"""
Diagnostic Protocol Interpreter for semantic logging of automotive communication.
Provides human-readable descriptions of OBD2, UDS, and KWP protocols.
"""

from typing import List, Optional, Tuple
from enum import IntEnum


class ColorTheme:
    """Base class for color themes"""
    RESET = '\033[0m'
    BOLD = '\033[1m'
    
    # To be defined by subclasses
    ECU = ''
    REQUEST = ''
    RESPONSE = ''
    ERROR = ''
    SERVICE = ''
    SUBFUNCTION = ''
    DATA = ''
    HEX = ''
    SUCCESS = ''
    FAIL = ''
    INFO = ''


class DarkTheme(ColorTheme):
    """Colors optimized for dark terminals"""
    # ECU name colors
    ECU = '\033[1;36m'  # Bold Cyan
    
    # Direction indicators
    REQUEST = '\033[1;33m'  # Bold Yellow
    RESPONSE = '\033[1;32m'  # Bold Green
    ERROR = '\033[1;31m'  # Bold Red
    
    # Service types
    SERVICE = '\033[1;35m'  # Bold Magenta
    SUBFUNCTION = '\033[0;35m'  # Normal Magenta
    
    # Data
    DATA = '\033[0;37m'  # Normal White
    HEX = '\033[0;90m'  # Dark Gray
    
    # Status
    SUCCESS = '\033[1;32m'  # Bold Green
    FAIL = '\033[1;31m'  # Bold Red
    INFO = '\033[0;36m'  # Normal Cyan


class LightTheme(ColorTheme):
    """Colors optimized for light terminals"""
    # ECU name colors
    ECU = '\033[1;34m'  # Bold Blue
    
    # Direction indicators
    REQUEST = '\033[1;33m'  # Bold Yellow/Orange
    RESPONSE = '\033[1;32m'  # Bold Green
    ERROR = '\033[1;31m'  # Bold Red
    
    # Service types
    SERVICE = '\033[1;35m'  # Bold Magenta
    SUBFUNCTION = '\033[0;35m'  # Normal Magenta
    
    # Data
    DATA = '\033[0;30m'  # Normal Black
    HEX = '\033[0;34m'  # Normal Blue
    
    # Status
    SUCCESS = '\033[1;32m'  # Bold Green
    FAIL = '\033[1;31m'  # Bold Red
    INFO = '\033[0;36m'  # Normal Cyan


# Default to light theme
Colors = LightTheme

# Global settings for output formatting
_max_hex_bytes = 20  # Default limit for hex output
_unlimited_hex = False


def set_color_theme(theme: str = "light") -> None:
    """
    Set the color theme for diagnostic output.
    
    Args:
        theme: "light" or "dark" (default: "light")
    """
    global Colors
    if theme.lower() == "dark":
        Colors = DarkTheme
    else:
        Colors = LightTheme


def set_hex_output_limit(max_bytes: int = 20, unlimited: bool = False) -> None:
    """
    Set the maximum number of hex bytes to display.
    
    Args:
        max_bytes: Maximum number of bytes to show (default: 20)
        unlimited: If True, show all bytes regardless of max_bytes
    """
    global _max_hex_bytes, _unlimited_hex
    _max_hex_bytes = max_bytes
    _unlimited_hex = unlimited


def _format_hex_data(data: List[int]) -> str:
    """
    Format hex data with optional truncation.
    
    Args:
        data: List of bytes to format
        
    Returns:
        Formatted hex string, possibly truncated
    """
    if _unlimited_hex or len(data) <= _max_hex_bytes:
        return ' '.join(f'{b:02X}' for b in data)
    else:
        truncated = ' '.join(f'{b:02X}' for b in data[:_max_hex_bytes])
        return f"{truncated}... ({len(data)} bytes total)"


class UDSServices(IntEnum):
    DIAGNOSTIC_SESSION_CONTROL = 0x10
    ECU_RESET = 0x11
    SECURITY_ACCESS = 0x27
    COMMUNICATION_CONTROL = 0x28
    TESTER_PRESENT = 0x3E
    ACCESS_TIMING_PARAMETER = 0x83
    SECURED_DATA_TRANSMISSION = 0x84
    CONTROL_DTC_SETTING = 0x85
    RESPONSE_ON_EVENT = 0x86
    LINK_CONTROL = 0x87
    READ_DATA_BY_IDENTIFIER = 0x22
    READ_MEMORY_BY_ADDRESS = 0x23
    READ_SCALING_DATA_BY_IDENTIFIER = 0x24
    READ_DATA_BY_PERIODIC_IDENTIFIER = 0x2A
    DYNAMICALLY_DEFINE_DATA_IDENTIFIER = 0x2C
    WRITE_DATA_BY_IDENTIFIER = 0x2E
    WRITE_MEMORY_BY_ADDRESS = 0x3D
    CLEAR_DIAGNOSTIC_INFORMATION = 0x14
    READ_DTC_INFORMATION = 0x19
    INPUT_OUTPUT_CONTROL_BY_IDENTIFIER = 0x2F
    ROUTINE_CONTROL = 0x31
    REQUEST_DOWNLOAD = 0x34
    REQUEST_UPLOAD = 0x35
    TRANSFER_DATA = 0x36
    REQUEST_TRANSFER_EXIT = 0x37
    REQUEST_FILE_TRANSFER = 0x38
    
    # Positive response offset
    POSITIVE_RESPONSE_OFFSET = 0x40
    NEGATIVE_RESPONSE = 0x7F


class OBD2Services(IntEnum):
    SHOW_CURRENT_DATA = 0x01
    SHOW_FREEZE_FRAME_DATA = 0x02
    SHOW_STORED_DTCS = 0x03
    CLEAR_DTCS = 0x04
    TEST_RESULTS_OXYGEN_SENSORS = 0x05
    TEST_RESULTS_OTHER = 0x06
    SHOW_PENDING_DTCS = 0x07
    CONTROL_OPERATION = 0x08
    REQUEST_VEHICLE_INFORMATION = 0x09
    PERMANENT_DTCS = 0x0A


class KWPServices(IntEnum):
    START_DIAGNOSTIC_SESSION = 0x10
    ECU_RESET = 0x11
    READ_FREEZE_FRAME_DATA = 0x12
    READ_DIAGNOSTIC_TROUBLE_CODES = 0x13
    CLEAR_DIAGNOSTIC_INFORMATION = 0x14
    READ_STATUS_OF_DTCS = 0x17
    READ_DTCS_BY_STATUS = 0x18
    READ_ECU_IDENTIFICATION = 0x1A
    STOP_DIAGNOSTIC_SESSION = 0x20
    READ_DATA_BY_LOCAL_IDENTIFIER = 0x21
    READ_DATA_BY_COMMON_IDENTIFIER = 0x22
    READ_MEMORY_BY_ADDRESS = 0x23
    READ_SCALING_DATA_BY_IDENTIFIER = 0x24
    WET_DATA_RATES = 0x26
    SECURITY_ACCESS = 0x27
    COMMUNICATION_CONTROL = 0x28
    ENABLE_NORMAL_MESSAGE_TRANSMISSION = 0x29
    DYNAMICALLY_DEFINE_LOCAL_IDENTIFIER = 0x2C
    WRITE_DATA_BY_COMMON_IDENTIFIER = 0x2E
    INPUT_OUTPUT_CONTROL_BY_LOCAL_IDENTIFIER = 0x30
    START_ROUTINE_BY_LOCAL_IDENTIFIER = 0x31
    STOP_ROUTINE_BY_LOCAL_IDENTIFIER = 0x32
    REQUEST_ROUTINE_RESULTS_BY_LOCAL_IDENTIFIER = 0x33
    REQUEST_DOWNLOAD = 0x34
    REQUEST_UPLOAD = 0x35
    TRANSFER_DATA = 0x36
    REQUEST_TRANSFER_EXIT = 0x37
    START_ROUTINE_BY_ADDRESS = 0x38
    STOP_ROUTINE_BY_ADDRESS = 0x39
    REQUEST_ROUTINE_RESULTS_BY_ADDRESS = 0x3A
    WRITE_DATA_BY_LOCAL_IDENTIFIER = 0x3B
    WRITE_MEMORY_BY_ADDRESS = 0x3D
    TESTER_PRESENT = 0x3E
    START_COMMUNICATION = 0x81
    STOP_COMMUNICATION = 0x82
    ACCESS_TIMING_PARAMETERS = 0x83
    NETWORK_CONFIGURATION = 0x84

class NegativeResponseCodes(IntEnum):
    GENERAL_REJECT = 0x10
    SERVICE_NOT_SUPPORTED = 0x11
    SUBFUNCTION_NOT_SUPPORTED = 0x12
    INCORRECT_MESSAGE_LENGTH = 0x13
    RESPONSE_TOO_LONG = 0x14
    BUSY_REPEAT_REQUEST = 0x21
    CONDITIONS_NOT_CORRECT = 0x22
    REQUEST_SEQUENCE_ERROR = 0x24
    NO_RESPONSE_FROM_SUBNET = 0x25
    FAILURE_PREVENTS_EXECUTION = 0x26
    REQUEST_OUT_OF_RANGE = 0x31
    SECURITY_ACCESS_DENIED = 0x33
    INVALID_KEY = 0x35
    EXCEEDED_ATTEMPTS = 0x36
    TIME_DELAY_NOT_EXPIRED = 0x37
    UPLOAD_DOWNLOAD_NOT_ACCEPTED = 0x70
    TRANSFER_DATA_SUSPENDED = 0x71
    GENERAL_PROGRAMMING_FAILURE = 0x72
    WRONG_BLOCK_SEQUENCE = 0x73
    REQUEST_CORRECTLY_RECEIVED_RESPONSE_PENDING = 0x78
    SUBFUNCTION_NOT_SUPPORTED_IN_ACTIVE_SESSION = 0x7E
    SERVICE_NOT_SUPPORTED_IN_ACTIVE_SESSION = 0x7F
    RPM_TOO_HIGH = 0x81
    RPM_TOO_LOW = 0x82
    ENGINE_IS_RUNNING = 0x83
    ENGINE_IS_NOT_RUNNING = 0x84
    ENGINE_RUNTIME_TOO_LOW = 0x85
    TEMPERATURE_TOO_HIGH = 0x86
    TEMPERATURE_TOO_LOW = 0x87
    VEHICLE_SPEED_TOO_HIGH = 0x88
    VEHICLE_SPEED_TOO_LOW = 0x89
    THROTTLE_TOO_HIGH = 0x8A
    THROTTLE_TOO_LOW = 0x8B
    TRANSMISSION_NOT_IN_NEUTRAL = 0x8C
    TRANSMISSION_NOT_IN_GEAR = 0x8D
    BRAKE_NOT_APPLIED = 0x8F
    SHIFTER_NOT_IN_PARK = 0x90
    TORQUE_CONVERTER_CLUTCH_LOCKED = 0x91
    VOLTAGE_TOO_HIGH = 0x92
    VOLTAGE_TOO_LOW = 0x93


class DiagnosticInterpreter:
    """Interprets diagnostic messages and provides semantic descriptions"""
    
    @staticmethod
    def interpret_message(ecu_name: str, data: List[int], is_request: bool = True, is_broadcast: bool = False) -> str:
        """
        Interpret a diagnostic message and return a colored, semantic description.
        
        Args:
            ecu_name: Name of the ECU
            data: Message data bytes
            is_request: True if this is a request, False for response
            is_broadcast: True if this is a broadcast frame (e.g., BMW extended broadcast)
            
        Returns:
            Colored string with semantic description
        """
        if not data:
            return f"{Colors.ERROR}Empty message{Colors.RESET}"
            
        # Determine protocol and service
        if is_request:
            return DiagnosticInterpreter._interpret_request(ecu_name, data, is_broadcast)
        else:
            return DiagnosticInterpreter._interpret_response(ecu_name, data, is_broadcast)
    
    @staticmethod
    def _interpret_request(ecu_name: str, data: List[int], is_broadcast: bool = False) -> str:
        """Interpret a request message"""
        service = data[0]
        
        if is_broadcast:
            direction = f"{Colors.INFO}⟵ BROADCAST{Colors.RESET}"
        else:
            direction = f"{Colors.REQUEST}←{Colors.RESET}"
        
        # Check for UDS services
        if service in [s.value for s in UDSServices]:
            return DiagnosticInterpreter._interpret_uds_request(ecu_name, data, direction)

        # Check for OBD2 services
        if service in [s.value for s in OBD2Services]:
            return DiagnosticInterpreter._interpret_obd2_request(ecu_name, data, direction)

        # Check for KWP services
        if service in [s.value for s in KWPServices]:
            return DiagnosticInterpreter._interpret_kwp_request(ecu_name, data, direction)
            
        # Unknown service
        hex_data = _format_hex_data(data)
        return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}Unknown Service{Colors.RESET} {Colors.HEX}[{hex_data}]{Colors.RESET}"
    
    @staticmethod
    def _interpret_response(ecu_name: str, data: List[int], is_broadcast: bool = False) -> str:
        """Interpret a response message"""
        service = data[0]
        
        # Responses are typically not broadcast, but just in case
        if is_broadcast:
            direction = f"{Colors.INFO}⟶ BROADCAST{Colors.RESET}"
        else:
            direction = f"{Colors.RESPONSE}→{Colors.RESET}"
        
        # Negative response
        if service == UDSServices.NEGATIVE_RESPONSE:
            return DiagnosticInterpreter._interpret_negative_response(ecu_name, data, direction)
        
        # Check for positive response (any service + 0x40)
        if service >= 0x40:
            base_service = service - 0x40
            # Check if it's specifically an OBD2 response
            if service >= 0x41 and service <= 0x4A:
                return DiagnosticInterpreter._interpret_obd2_response(ecu_name, data, direction)
            # Prefer KWP when the base service is KWP-specific
            if base_service in [s.value for s in KWPServices] and base_service not in [s.value for s in UDSServices]:
                return DiagnosticInterpreter._interpret_kwp_response(ecu_name, data, direction)
            # Otherwise it's a UDS response
            return DiagnosticInterpreter._interpret_uds_response(ecu_name, data, direction)
            
        # Unknown response
        hex_data = _format_hex_data(data)
        return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}Unknown Response{Colors.RESET} {Colors.HEX}[{hex_data}]{Colors.RESET}"
    
    @staticmethod
    def _interpret_uds_request(ecu_name: str, data: List[int], direction: str) -> str:
        """Interpret UDS request"""
        service = data[0]
        
        if service == UDSServices.DIAGNOSTIC_SESSION_CONTROL:
            session_types = {0x01: "Default", 0x02: "Programming", 0x03: "Extended", 0x04: "Safety"}
            session = session_types.get(data[1], f"0x{data[1]:02X}") if len(data) > 1 else "?"
            return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}Diagnostic Session Control{Colors.RESET} - Request {Colors.DATA}{session} session{Colors.RESET}"
            
        elif service == UDSServices.ECU_RESET:
            reset_types = {0x01: "Hard Reset", 0x02: "Key Off/On", 0x03: "Soft Reset", 0x04: "Enable Rapid Power Shutdown"}
            reset = reset_types.get(data[1], f"0x{data[1]:02X}") if len(data) > 1 else "?"
            return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}ECU Reset{Colors.RESET} - {Colors.DATA}{reset}{Colors.RESET}"
            
        elif service == UDSServices.SECURITY_ACCESS:
            if len(data) > 1:
                level = data[1]
                if level & 0x01:  # Odd = request seed
                    return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}Security Access{Colors.RESET} - Request seed for {Colors.DATA}level 0x{level:02X}{Colors.RESET}"
                else:  # Even = send key
                    key_data = _format_hex_data(data[2:])
                    return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}Security Access{Colors.RESET} - Send key for {Colors.DATA}level 0x{level:02X}{Colors.RESET} {Colors.HEX}[{key_data}]{Colors.RESET}"
                    
        elif service == UDSServices.TESTER_PRESENT:
            suppress = "suppress response" if len(data) > 1 and data[1] == 0x80 else "with response"
            return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}Tester Present{Colors.RESET} - {Colors.DATA}{suppress}{Colors.RESET}"
            
        elif service == UDSServices.READ_DATA_BY_IDENTIFIER:
            if len(data) >= 3:
                did = (data[1] << 8) | data[2]
                did_names = {
                    0xF186: "Active Diagnostic Session",
                    0xF187: "Vehicle Manufacturer Spare Part Number",
                    0xF188: "Vehicle Manufacturer ECU Software Number",
                    0xF189: "Vehicle Manufacturer ECU Software Version",
                    0xF18A: "System Supplier Identifier",
                    0xF18C: "ECU Serial Number",
                    0xF190: "VIN",
                    0xF191: "Vehicle Manufacturer ECU Hardware Number",
                    0xF192: "System Supplier ECU Hardware Number",
                    0xF193: "System Supplier ECU Hardware Version",
                    0xF194: "System Supplier ECU Software Number",
                    0xF195: "System Supplier ECU Software Version",
                    0xF197: "System Name",
                    0xF198: "Repair Shop Code",
                    0xF199: "Programming Date"
                }
                did_name = did_names.get(did, f"DID 0x{did:04X}")
                return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}Read Data By Identifier{Colors.RESET} - {Colors.DATA}{did_name}{Colors.RESET}"
                
        elif service == UDSServices.WRITE_DATA_BY_IDENTIFIER:
            if len(data) >= 3:
                did = (data[1] << 8) | data[2]
                return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}Write Data By Identifier{Colors.RESET} - {Colors.DATA}DID 0x{did:04X}{Colors.RESET}"
                
        elif service == UDSServices.ROUTINE_CONTROL:
            if len(data) >= 4:
                subfunction = {0x01: "Start", 0x02: "Stop", 0x03: "Request Results"}.get(data[1], f"0x{data[1]:02X}")
                routine_id = (data[2] << 8) | data[3]
                return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}Routine Control{Colors.RESET} - {Colors.SUBFUNCTION}{subfunction}{Colors.RESET} {Colors.DATA}routine 0x{routine_id:04X}{Colors.RESET}"
                
        elif service == UDSServices.CLEAR_DIAGNOSTIC_INFORMATION:
            group = "All DTCs" if len(data) >= 4 and data[1] == 0xFF and data[2] == 0xFF and data[3] == 0xFF else "Specific group"
            return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}Clear Diagnostic Information{Colors.RESET} - {Colors.DATA}{group}{Colors.RESET}"
            
        elif service == UDSServices.READ_DTC_INFORMATION:
            if len(data) > 1:
                subfunctions = {
                    0x01: "Report number of DTCs by status mask",
                    0x02: "Report DTCs by status mask", 
                    0x03: "Report DTC snapshot",
                    0x04: "Report DTC snapshot by DTC number",
                    0x06: "Report DTC extended data",
                    0x0A: "Report supported DTCs"
                }
                subf = subfunctions.get(data[1], f"Subfunction 0x{data[1]:02X}")
                return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}Read DTC Information{Colors.RESET} - {Colors.DATA}{subf}{Colors.RESET}"
        
        # Generic UDS service
        try:
            service_name = UDSServices(service).name.replace('_', ' ').title()
        except ValueError:
            try:
                service_name = KWPServices(service).name.replace('_', ' ').title()
            except ValueError:
                service_name = f"Service 0x{service:02X}"
        
        hex_data = _format_hex_data(data)
        return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}{service_name}{Colors.RESET} {Colors.HEX}[{hex_data}]{Colors.RESET}"
    
    @staticmethod
    def _interpret_uds_response(ecu_name: str, data: List[int], direction: str) -> str:
        """Interpret UDS positive response"""
        service = data[0] - UDSServices.POSITIVE_RESPONSE_OFFSET
        
        if service == UDSServices.DIAGNOSTIC_SESSION_CONTROL:
            session_types = {0x01: "Default", 0x02: "Programming", 0x03: "Extended", 0x04: "Safety"}
            session = session_types.get(data[1], f"0x{data[1]:02X}") if len(data) > 1 else "?"
            timing = f"P2={data[2]*256+data[3]}ms, P2*={data[4]*256+data[5]}ms" if len(data) >= 6 else ""
            return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SUCCESS}Session Changed{Colors.RESET} - {Colors.DATA}{session} session{Colors.RESET} {Colors.INFO}{timing}{Colors.RESET}"
            
        elif service == UDSServices.SECURITY_ACCESS:
            if len(data) > 1:
                level = data[1]
                if level & 0x01:  # Odd = seed
                    seed = _format_hex_data(data[2:]) if len(data) > 2 else "empty"
                    return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}Security Access{Colors.RESET} - {Colors.DATA}Seed for level 0x{level:02X}{Colors.RESET}: {Colors.HEX}[{seed}]{Colors.RESET}"
                else:  # Even = key accepted
                    return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SUCCESS}Security Access Granted{Colors.RESET} - {Colors.DATA}Level 0x{level:02X}{Colors.RESET}"
                    
        elif service == UDSServices.READ_DATA_BY_IDENTIFIER:
            if len(data) >= 3:
                did = (data[1] << 8) | data[2]
                value = data[3:] if len(data) > 3 else []
                value_str = ''.join(chr(b) if 32 <= b <= 126 else f'\\x{b:02X}' for b in value) if value else "empty"
                if len(value_str) > 40:
                    value_str = value_str[:40] + "..."
                return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}Read Data Response{Colors.RESET} - {Colors.DATA}DID 0x{did:04X}{Colors.RESET}: {Colors.INFO}{value_str}{Colors.RESET}"
                
        elif service == UDSServices.ROUTINE_CONTROL:
            if len(data) >= 4:
                subfunction = {0x01: "Started", 0x02: "Stopped", 0x03: "Results"}.get(data[1], f"0x{data[1]:02X}")
                routine_id = (data[2] << 8) | data[3]
                status = "Success" if len(data) > 4 and data[4] == 0x00 else "Check status byte"
                return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}Routine Control{Colors.RESET} - {Colors.SUBFUNCTION}{subfunction}{Colors.RESET} {Colors.DATA}routine 0x{routine_id:04X}{Colors.RESET} - {Colors.INFO}{status}{Colors.RESET}"
                
        elif service == UDSServices.CONTROL_DTC_SETTING:
            dtc_setting = {0x01: "ON", 0x02: "OFF"}.get(data[1], f"0x{data[1]:02X}") if len(data) > 1 else "?"
            return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SUCCESS}DTC Setting Accepted{Colors.RESET} - {Colors.DATA}DTCs turned {dtc_setting}{Colors.RESET}"
        
        # Generic positive response
        hex_data = _format_hex_data(data[1:]) if len(data) > 1 else ""
        return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SUCCESS}Positive Response{Colors.RESET} to service {Colors.SERVICE}0x{service:02X}{Colors.RESET} {Colors.HEX}[{hex_data}]{Colors.RESET}"
    
    @staticmethod
    def _interpret_negative_response(ecu_name: str, data: List[int], direction: str) -> str:
        """Interpret negative response"""
        if len(data) < 3:
            return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.ERROR}Invalid Negative Response{Colors.RESET}"
            
        service = data[1]
        nrc = data[2]
        
        try:
            nrc_name = NegativeResponseCodes(nrc).name.replace('_', ' ').title()
        except ValueError:
            nrc_name = f"NRC 0x{nrc:02X}"
            
        try:
            service_name = UDSServices(service).name.replace('_', ' ').title()
        except ValueError:
            service_name = f"Service 0x{service:02X}"
            
        # Special handling for response pending
        if nrc == NegativeResponseCodes.REQUEST_CORRECTLY_RECEIVED_RESPONSE_PENDING:
            return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.INFO}Response Pending{Colors.RESET} for {Colors.SERVICE}{service_name}{Colors.RESET}"
            
        return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.FAIL}Negative Response{Colors.RESET} - {Colors.SERVICE}{service_name}{Colors.RESET}: {Colors.ERROR}{nrc_name}{Colors.RESET}"
    
    @staticmethod
    def _interpret_obd2_request(ecu_name: str, data: List[int], direction: str) -> str:
        """Interpret OBD2 request"""
        service = data[0]
        
        if service == OBD2Services.SHOW_CURRENT_DATA:
            if len(data) > 1:
                pid = data[1]
                pid_names = {
                    0x00: "PIDs supported [01-20]",
                    0x01: "Monitor status",
                    0x03: "Fuel system status",
                    0x04: "Engine load",
                    0x05: "Engine coolant temp",
                    0x0C: "Engine RPM",
                    0x0D: "Vehicle speed",
                    0x11: "Throttle position",
                    0x1C: "OBD standards",
                    0x20: "PIDs supported [21-40]",
                    0x40: "PIDs supported [41-60]",
                    0x60: "PIDs supported [61-80]"
                }
                pid_name = pid_names.get(pid, f"PID 0x{pid:02X}")
                return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}OBD2 Current Data{Colors.RESET} - {Colors.DATA}{pid_name}{Colors.RESET}"
                
        elif service == OBD2Services.SHOW_FREEZE_FRAME_DATA:
            if len(data) > 1:
                pid = data[1]
                return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}OBD2 Freeze Frame{Colors.RESET} - {Colors.DATA}PID 0x{pid:02X}{Colors.RESET}"
                
        elif service == OBD2Services.SHOW_STORED_DTCS:
            return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}OBD2 Show Stored DTCs{Colors.RESET}"
            
        elif service == OBD2Services.CLEAR_DTCS:
            return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}OBD2 Clear DTCs{Colors.RESET}"
            
        elif service == OBD2Services.REQUEST_VEHICLE_INFORMATION:
            if len(data) > 1:
                info_types = {
                    0x00: "PIDs supported",
                    0x02: "VIN",
                    0x04: "Calibration ID",
                    0x06: "CVN",
                    0x08: "Performance tracking",
                    0x0A: "ECU name"
                }
                info = info_types.get(data[1], f"Info type 0x{data[1]:02X}")
                return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}OBD2 Vehicle Info{Colors.RESET} - {Colors.DATA}{info}{Colors.RESET}"
        
        # Generic OBD2 service
        try:
            service_name = OBD2Services(service).name.replace('_', ' ').title()
        except ValueError:
            service_name = f"OBD2 Service 0x{service:02X}"
            
        return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}{service_name}{Colors.RESET}"
    
    @staticmethod
    def _interpret_obd2_response(ecu_name: str, data: List[int], direction: str) -> str:
        """Interpret OBD2 response"""
        service = data[0] - 0x40
        
        if service == OBD2Services.SHOW_CURRENT_DATA:
            if len(data) > 1:
                pid = data[1]
                value = data[2:] if len(data) > 2 else []
                value_hex = _format_hex_data(value)
                return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}OBD2 Current Data{Colors.RESET} - {Colors.DATA}PID 0x{pid:02X}{Colors.RESET}: {Colors.HEX}[{value_hex}]{Colors.RESET}"
                
        elif service == OBD2Services.REQUEST_VEHICLE_INFORMATION:
            if len(data) > 1:
                info_type = data[1]
                if info_type == 0x02 and len(data) > 2:  # VIN
                    vin_chars = ''.join(chr(b) if 32 <= b <= 126 else '?' for b in data[3:])
                    return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}OBD2 Vehicle Info{Colors.RESET} - {Colors.DATA}VIN: {vin_chars}{Colors.RESET}"
                    
        # Generic OBD2 response
        hex_data = _format_hex_data(data[1:]) if len(data) > 1 else ""
        return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SUCCESS}OBD2 Response{Colors.RESET} to service {Colors.SERVICE}0x{service:02X}{Colors.RESET} {Colors.HEX}[{hex_data}]{Colors.RESET}"

    @staticmethod
    def _interpret_kwp_request(ecu_name: str, data: List[int], direction: str) -> str:
        """Interpret KWP request"""
        service = data[0]

        if service == KWPServices.START_DIAGNOSTIC_SESSION:
            session_types = {
                0x81: "Default",
                0x82: "Upload",
                0x83: "Download",
                0x84: "Programming",
                0x85: "Development",
                0x86: "Adjustment",
                0x87: "Vehicle Upload",
                0x89: "Vehicle Download",
                0x90: "Customer Service Upload",
                0x91: "Customer Service Download",
                0x92: "End Customer Service Upload",
                0x93: "End Customer Service Download",
            }
            session = session_types.get(data[1], f"0x{data[1]:02X}") if len(data) > 1 else "?"
            return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}KWP Start Diagnostic Session{Colors.RESET} - {Colors.DATA}{session}{Colors.RESET}"

        if service == KWPServices.ECU_RESET:
            reset_type = f"0x{data[1]:02X}" if len(data) > 1 else "?"
            return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}KWP ECU Reset{Colors.RESET} - {Colors.DATA}{reset_type}{Colors.RESET}"

        if service == KWPServices.READ_ECU_IDENTIFICATION:
            identifier = f"0x{data[1]:02X}" if len(data) > 1 else "?"
            return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}KWP Read ECU Identification{Colors.RESET} - {Colors.DATA}ID {identifier}{Colors.RESET}"

        if service == KWPServices.READ_DATA_BY_LOCAL_IDENTIFIER:
            identifier = f"0x{data[1]:02X}" if len(data) > 1 else "?"
            return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}KWP Read Data By Local Identifier{Colors.RESET} - {Colors.DATA}ID {identifier}{Colors.RESET}"

        if service == KWPServices.WRITE_DATA_BY_LOCAL_IDENTIFIER:
            identifier = f"0x{data[1]:02X}" if len(data) > 1 else "?"
            return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}KWP Write Data By Local Identifier{Colors.RESET} - {Colors.DATA}ID {identifier}{Colors.RESET}"

        if service == KWPServices.READ_DTCS_BY_STATUS:
            status = f"0x{data[1]:02X}" if len(data) > 1 else "?"
            return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}KWP Read DTCs By Status{Colors.RESET} - {Colors.DATA}Mask {status}{Colors.RESET}"

        if service == KWPServices.READ_STATUS_OF_DTCS:
            status = f"0x{data[1]:02X}" if len(data) > 1 else "?"
            return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}KWP Read Status Of DTCs{Colors.RESET} - {Colors.DATA}Mask {status}{Colors.RESET}"

        if service == KWPServices.TESTER_PRESENT:
            suppress = "suppress response" if len(data) > 1 and data[1] == 0x80 else "with response"
            return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}KWP Tester Present{Colors.RESET} - {Colors.DATA}{suppress}{Colors.RESET}"

        if service == KWPServices.START_COMMUNICATION:
            return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}KWP Start Communication{Colors.RESET}"

        if service == KWPServices.STOP_COMMUNICATION:
            return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}KWP Stop Communication{Colors.RESET}"

        if service == KWPServices.ACCESS_TIMING_PARAMETERS:
            return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}KWP Access Timing Parameters{Colors.RESET}"

        if service == KWPServices.NETWORK_CONFIGURATION:
            return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}KWP Network Configuration{Colors.RESET}"

        # Generic KWP service
        try:
            service_name = KWPServices(service).name.replace('_', ' ').title()
        except ValueError:
            service_name = f"KWP Service 0x{service:02X}"
        hex_data = _format_hex_data(data)
        return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}{service_name}{Colors.RESET} {Colors.HEX}[{hex_data}]{Colors.RESET}"

    @staticmethod
    def _interpret_kwp_response(ecu_name: str, data: List[int], direction: str) -> str:
        """Interpret KWP positive response"""
        service = data[0] - 0x40

        if service == KWPServices.START_DIAGNOSTIC_SESSION:
            session = f"0x{data[1]:02X}" if len(data) > 1 else "?"
            return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SUCCESS}KWP Session Started{Colors.RESET} - {Colors.DATA}{session}{Colors.RESET}"

        if service == KWPServices.READ_ECU_IDENTIFICATION:
            identifier = f"0x{data[1]:02X}" if len(data) > 1 else "?"
            value = data[2:] if len(data) > 2 else []
            value_str = ''.join(chr(b) if 32 <= b <= 126 else f'\\x{b:02X}' for b in value) if value else "empty"
            if len(value_str) > 40:
                value_str = value_str[:40] + "..."
            return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}KWP ECU Identification{Colors.RESET} - {Colors.DATA}ID {identifier}{Colors.RESET}: {Colors.INFO}{value_str}{Colors.RESET}"

        if service == KWPServices.READ_DATA_BY_LOCAL_IDENTIFIER:
            identifier = f"0x{data[1]:02X}" if len(data) > 1 else "?"
            value = data[2:] if len(data) > 2 else []
            value_hex = _format_hex_data(value)
            return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}KWP Local Identifier Data{Colors.RESET} - {Colors.DATA}ID {identifier}{Colors.RESET} {Colors.HEX}[{value_hex}]{Colors.RESET}"

        if service == KWPServices.READ_DTCS_BY_STATUS:
            dtc_hex = _format_hex_data(data[1:]) if len(data) > 1 else ""
            return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SERVICE}KWP DTCs By Status{Colors.RESET} {Colors.HEX}[{dtc_hex}]{Colors.RESET}"

        # Generic KWP response
        hex_data = _format_hex_data(data[1:]) if len(data) > 1 else ""
        return f"{Colors.ECU}{ecu_name}{Colors.RESET} {direction} {Colors.SUCCESS}KWP Positive Response{Colors.RESET} to service {Colors.SERVICE}0x{service:02X}{Colors.RESET} {Colors.HEX}[{hex_data}]{Colors.RESET}"


# Convenience function for external use
def interpret_diagnostic_message(ecu_name: str, data: List[int], is_request: bool = True, is_broadcast: bool = False) -> str:
    """
    Convenience function to interpret a diagnostic message.
    
    Args:
        ecu_name: Name of the ECU
        data: Message data bytes
        is_request: True if this is a request, False for response
        is_broadcast: True if this is a broadcast frame
        
    Returns:
        Colored string with semantic description
    """
    return DiagnosticInterpreter.interpret_message(ecu_name, data, is_request, is_broadcast)
