"""
Utility functions for OCPP UDP client
"""
import json
import logging
import uuid
from datetime import datetime, timezone
from typing import Any, Dict, Optional

logger = logging.getLogger(__name__)


def generate_message_id() -> str:
    """Generate a unique message ID"""
    return str(uuid.uuid4())


def get_current_timestamp() -> str:
    """Get current timestamp in ISO 8601 format"""
    return datetime.now(timezone.utc).isoformat()


def parse_ocpp_message(message: str) -> Optional[Dict[str, Any]]:
    """Parse an OCPP message from JSON string"""
    try:
        # First try to parse as JSON array (OCPP format)
        data = json.loads(message)
        
        if isinstance(data, list) and len(data) >= 2:
            # OCPP message format: [messageTypeId, uniqueId, ...]
            message_type_id = data[0]
            unique_id = data[1]
            
            if message_type_id == 2 and len(data) >= 4:
                # CALL: [2, "uniqueId", "action", payload]
                return {
                    "messageTypeId": message_type_id,
                    "uniqueId": unique_id,
                    "action": data[2],
                    "payload": data[3]
                }
            elif message_type_id == 3 and len(data) >= 3:
                # CALLRESULT: [3, "uniqueId", payload]
                return {
                    "messageTypeId": message_type_id,
                    "uniqueId": unique_id,
                    "payload": data[2]
                }
            elif message_type_id == 4 and len(data) >= 5:
                # CALLERROR: [4, "uniqueId", "errorCode", "errorDescription", errorDetails]
                return {
                    "messageTypeId": message_type_id,
                    "uniqueId": unique_id,
                    "errorCode": data[2],
                    "errorDescription": data[3],
                    "errorDetails": data[4]
                }
        
        # If not OCPP array format, try as regular JSON object
        elif isinstance(data, dict):
            logger.debug("Parsed as regular JSON object")
            return data
            
        logger.warning(f"Invalid OCPP message format: {message}")
        return None
        
    except json.JSONDecodeError as e:
        logger.warning(f"Failed to parse message as JSON: {e}")
        return None
    except Exception as e:
        logger.error(f"Unexpected error parsing message: {e}")
        return None


def format_ocpp_message(message_data: Dict[str, Any]) -> str:
    """Format an OCPP message as JSON string"""
    try:
        # Convert dict format back to OCPP array format
        message_type_id = message_data.get("messageTypeId")
        unique_id = message_data.get("uniqueId")
        
        if message_type_id == 2:  # CALL
            ocpp_array = [
                message_type_id,
                unique_id,
                message_data.get("action"),
                message_data.get("payload", {})
            ]
        elif message_type_id == 3:  # CALLRESULT
            ocpp_array = [
                message_type_id,
                unique_id,
                message_data.get("payload", {})
            ]
        elif message_type_id == 4:  # CALLERROR
            ocpp_array = [
                message_type_id,
                unique_id,
                message_data.get("errorCode"),
                message_data.get("errorDescription"),
                message_data.get("errorDetails", {})
            ]
        else:
            # Fallback to regular JSON formatting
            return json.dumps(message_data, separators=(',', ':'))
            
        return json.dumps(ocpp_array, separators=(',', ':'))
        
    except Exception as e:
        logger.error(f"Failed to format OCPP message: {e}")
        return json.dumps({"error": "formatting_failed"}, separators=(',', ':'))


def validate_message_structure(message: Dict[str, Any]) -> bool:
    """Basic validation of OCPP message structure"""
    required_fields = ["messageTypeId", "uniqueId"]
    
    for field in required_fields:
        if field not in message:
            logger.warning(f"Missing required field in message: {field}")
            return False
            
    return True


def sanitize_string(text: str, max_length: int = 255) -> str:
    """Sanitize a string for safe storage/transmission"""
    if not isinstance(text, str):
        text = str(text)
        
    # Remove control characters and limit length
    sanitized = ''.join(char for char in text if ord(char) >= 32)
    return sanitized[:max_length]


class MessageBuilder:
    """Helper class for building OCPP messages"""
    
    @staticmethod
    def create_call(action: str, payload: Dict[str, Any]) -> Dict[str, Any]:
        """Create a CALL message"""
        return {
            "messageTypeId": 2,  # CALL
            "uniqueId": generate_message_id(),
            "action": action,
            "payload": payload
        }
        
    @staticmethod
    def create_call_result(unique_id: str, payload: Dict[str, Any]) -> Dict[str, Any]:
        """Create a CALLRESULT message"""
        return {
            "messageTypeId": 3,  # CALLRESULT
            "uniqueId": unique_id,
            "payload": payload
        }
        
    @staticmethod
    def create_call_error(unique_id: str, error_code: str, error_description: str, error_details: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
        """Create a CALLERROR message"""
        return {
            "messageTypeId": 4,  # CALLERROR
            "uniqueId": unique_id,
            "errorCode": error_code,
            "errorDescription": error_description,
            "errorDetails": error_details or {}
        }
