#!/usr/bin/env python3
"""
Quectel EG25-G HTTP(S) Communication Script
Implements all examples from the application note with comprehensive error handling
Version: 1.0
"""

import serial
import time
import sys
import logging
from typing import Tuple, Optional, Dict
from enum import Enum

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.FileHandler('quectel_eg25g.log'),
        logging.StreamHandler(sys.stdout)
    ]
)
logger = logging.getLogger(__name__)


class ErrorCode(Enum):
    """Module error codes with detailed descriptions"""
    SUCCESS = 0
    UNKNOWN_ERROR = 701
    TIMEOUT = 702
    HTTP_BUSY = 703
    UART_BUSY = 704
    NO_REQUEST = 705
    NETWORK_BUSY = 706
    NETWORK_OPEN_FAILED = 707
    NETWORK_NO_CONFIG = 708
    NETWORK_DEACTIVATED = 709
    NETWORK_ERROR = 710
    URL_ERROR = 711
    EMPTY_URL = 712
    IP_ERROR = 713
    DNS_ERROR = 714
    SOCKET_CREATE_ERROR = 715
    SOCKET_CONNECT_ERROR = 716
    SOCKET_READ_ERROR = 717
    SOCKET_WRITE_ERROR = 718
    SOCKET_CLOSED = 719
    DATA_ENCODE_ERROR = 720
    DATA_DECODE_ERROR = 721
    READ_TIMEOUT = 722
    RESPONSE_FAILED = 723
    INCOMING_CALL_BUSY = 724
    VOICE_CALL_BUSY = 725
    INPUT_TIMEOUT = 726
    WAIT_DATA_TIMEOUT = 727
    WAIT_RESPONSE_TIMEOUT = 728
    MEMORY_FAILED = 729
    INVALID_PARAMETER = 730


# Comprehensive error descriptions and solutions
ERROR_DESCRIPTIONS = {
    0: {"description": "Operation completed successfully", "solution": "No action required"},
    701: {"description": "Unknown error occurred", "solution": "Check module logs, restart module if issue persists"},
    702: {"description": "Operation timed out", "solution": "Increase timeout value or check network connectivity"},
    703: {"description": "HTTP service is busy", "solution": "Wait a moment and retry the request"},
    704: {"description": "UART interface is busy", "solution": "Wait for current operation to complete before sending new commands"},
    705: {"description": "No HTTP request has been sent", "solution": "Send an HTTP request before trying to read the response"},
    706: {"description": "Network service is busy", "solution": "Wait for network operations to complete and retry"},
    707: {"description": "Failed to open network connection", "solution": "Check APN settings and network registration status"},
    708: {"description": "Network context not configured", "solution": "Configure PDP context with AT+QICSGP before activating"},
    709: {"description": "Network context has been deactivated", "solution": "Reactivate PDP context with AT+QIACT"},
    710: {"description": "General network error", "solution": "Check signal strength and network registration"},
    711: {"description": "Invalid URL format", "solution": "Ensure URL starts with http:// or https:// and is properly formatted"},
    712: {"description": "URL is empty", "solution": "Provide a valid URL before sending the request"},
    713: {"description": "IP address error", "solution": "Check PDP context activation and IP assignment"},
    714: {"description": "DNS resolution failed", "solution": "Check DNS settings or use IP address instead of hostname"},
    715: {"description": "Failed to create socket", "solution": "Check available memory and network stack status"},
    716: {"description": "Failed to connect socket to server", "solution": "Verify server is accessible and port is correct"},
    717: {"description": "Error reading data from socket", "solution": "Check network stability and retry the request"},
    718: {"description": "Error writing data to socket", "solution": "Check network connection and data size"},
    719: {"description": "Socket connection closed unexpectedly", "solution": "Server may have closed connection, retry the request"},
    720: {"description": "Failed to encode data", "solution": "Check data format and encoding settings"},
    721: {"description": "Failed to decode received data", "solution": "Check response data format and encoding"},
    722: {"description": "Timeout while reading response", "solution": "Increase read timeout or check server response time"},
    723: {"description": "Failed to receive complete response", "solution": "Check network stability and server status"},
    724: {"description": "Incoming call interrupting data service", "solution": "Wait for call to end or reject incoming calls"},
    725: {"description": "Voice call is active", "solution": "End voice call before using data services"},
    726: {"description": "Timeout waiting for data input", "solution": "Send data faster or increase input timeout"},
    727: {"description": "Timeout waiting for data transmission", "solution": "Check data size and network speed"},
    728: {"description": "Timeout waiting for server response", "solution": "Increase response timeout or check server status"},
    729: {"description": "Memory allocation failed", "solution": "Free up memory or reduce data size"},
    730: {"description": "Invalid parameter in AT command", "solution": "Check command syntax and parameter values"}
}


class HTTPResponseCode(Enum):
    """HTTP response codes with descriptions"""
    # 2xx Success
    OK = 200
    CREATED = 201
    ACCEPTED = 202
    NO_CONTENT = 204
    
    # 3xx Redirection
    MOVED_PERMANENTLY = 301
    FOUND = 302
    NOT_MODIFIED = 304
    
    # 4xx Client Error
    BAD_REQUEST = 400
    UNAUTHORIZED = 401
    FORBIDDEN = 403
    NOT_FOUND = 404
    METHOD_NOT_ALLOWED = 405
    REQUEST_TIMEOUT = 408
    CONFLICT = 409
    LENGTH_REQUIRED = 411
    REQUEST_ENTITY_TOO_LARGE = 413
    UNSUPPORTED_MEDIA_TYPE = 415
    TOO_MANY_REQUESTS = 429
    
    # 5xx Server Error
    INTERNAL_SERVER_ERROR = 500
    NOT_IMPLEMENTED = 501
    BAD_GATEWAY = 502
    SERVICE_UNAVAILABLE = 503
    GATEWAY_TIMEOUT = 504


# HTTP status code descriptions
HTTP_STATUS_DESCRIPTIONS = {
    # 2xx Success
    200: "OK - The request was successful",
    201: "Created - The request was successful and a resource was created",
    202: "Accepted - The request has been accepted for processing",
    204: "No Content - The request was successful but there is no content to return",
    
    # 3xx Redirection
    301: "Moved Permanently - The resource has been moved to a new URL",
    302: "Found - The resource is temporarily at a different URL",
    304: "Not Modified - The resource has not been modified since last request",
    
    # 4xx Client Error
    400: "Bad Request - The request could not be understood by the server",
    401: "Unauthorized - Authentication is required",
    403: "Forbidden - The server understood but refuses to authorize the request",
    404: "Not Found - The requested resource could not be found",
    405: "Method Not Allowed - The HTTP method is not allowed for this resource",
    408: "Request Timeout - The server timed out waiting for the request",
    409: "Conflict - The request conflicts with the current state of the server",
    411: "Length Required - The Content-Length header is required",
    413: "Request Entity Too Large - The request body is too large",
    415: "Unsupported Media Type - The media type of the request is not supported",
    429: "Too Many Requests - Too many requests have been sent in a given time",
    
    # 5xx Server Error
    500: "Internal Server Error - The server encountered an unexpected condition",
    501: "Not Implemented - The server does not support the functionality",
    502: "Bad Gateway - The server received an invalid response from upstream",
    503: "Service Unavailable - The server is currently unavailable",
    504: "Gateway Timeout - The server did not receive a timely response from upstream"
}


class QuectelEG25G:
    """Quectel EG25-G Module Communication Handler"""
    
    def __init__(self, port: str = '/dev/ttyUSB2', baudrate: int = 115200, timeout: int = 10):
        """
        Initialize the Quectel module
        
        Args:
            port: Serial port (e.g., '/dev/ttyUSB2' for Linux, 'COM3' for Windows)
            baudrate: Baud rate for serial communication
            timeout: Default timeout in seconds
        """
        self.port = port
        self.baudrate = baudrate
        self.timeout = timeout
        self.serial = None
        self.context_id = 1
        self.ssl_context_id = 1
        
    def connect(self) -> bool:
        """Establish serial connection"""
        try:
            self.serial = serial.Serial(
                port=self.port,
                baudrate=self.baudrate,
                timeout=self.timeout,
                bytesize=serial.EIGHTBITS,
                parity=serial.PARITY_NONE,
                stopbits=serial.STOPBITS_ONE
            )
            logger.info(f"Connected to {self.port} at {self.baudrate} baud")
            time.sleep(0.5)
            return True
        except serial.SerialException as e:
            logger.error(f"Failed to connect: {e}")
            return False
    
    def disconnect(self):
        """Close serial connection"""
        if self.serial and self.serial.is_open:
            self.serial.close()
            logger.info("Disconnected from module")
    
    def send_command(self, command: str, timeout: Optional[int] = None) -> str:
        """
        Send AT command and read response
        
        Args:
            command: AT command to send
            timeout: Timeout in seconds (uses default if None)
            
        Returns:
            Response from module
        """
        if not self.serial or not self.serial.is_open:
            logger.error("Serial port is not open")
            return ""
        
        if timeout:
            original_timeout = self.serial.timeout
            self.serial.timeout = timeout
        
        try:
            # Clear input buffer
            self.serial.reset_input_buffer()
            
            # Send command
            cmd = command.strip() + '\r\n'
            self.serial.write(cmd.encode())
            logger.debug(f"Sent: {command}")
            
            # Read response
            response = ""
            start_time = time.time()
            timeout_val = timeout or self.timeout
            
            while True:
                if self.serial.in_waiting > 0:
                    data = self.serial.read(self.serial.in_waiting).decode('utf-8', errors='ignore')
                    response += data
                    
                    # Check for standard AT command responses
                    if any(end in response for end in ['OK\r\n', 'ERROR\r\n', 'CONNECT\r\n', '+CME ERROR:', '+CMS ERROR:']):
                        break
                    
                if time.time() - start_time > timeout_val:
                    logger.warning(f"Command timeout after {timeout_val}s: {command}")
                    break
                    
                time.sleep(0.05)  # Reduced sleep time for better responsiveness
            
            # Clean up response for logging
            clean_response = response.strip().replace('\r\n', ' | ')
            logger.debug(f"Response: {clean_response}")
            return response
            
        except serial.SerialException as e:
            logger.error(f"Serial communication error: {e}")
            return ""
        except Exception as e:
            logger.error(f"Unexpected error in send_command: {e}")
            return ""
        finally:
            if timeout:
                self.serial.timeout = original_timeout
    
    def wait_for_urc(self, urc_prefix: str, timeout: int = 60) -> str:
        """
        Wait for Unsolicited Result Code (URC)
        
        Args:
            urc_prefix: Expected URC prefix (e.g., '+QHTTPGET:')
            timeout: Timeout in seconds
            
        Returns:
            URC message
        """
        logger.info(f"Waiting for URC: {urc_prefix}")
        response = ""
        start_time = time.time()
        
        while time.time() - start_time < timeout:
            if self.serial.in_waiting > 0:
                data = self.serial.read(self.serial.in_waiting).decode('utf-8', errors='ignore')
                response += data
                
                if urc_prefix in response:
                    logger.info(f"Received URC: {response.strip()}")
                    return response
            
            time.sleep(0.1)
        
        logger.warning(f"URC timeout: {urc_prefix}")
        return ""
    
    def parse_urc_response(self, urc: str) -> Tuple[int, Optional[int], Optional[int]]:
        """
        Parse URC response to extract error code, HTTP code, and content length
        
        Args:
            urc: URC message
            
        Returns:
            Tuple of (error_code, http_response_code, content_length)
        """
        try:
            # Extract values from URC like "+QHTTPGET: 0,200,1234"
            parts = urc.split(':')[1].strip().split(',')
            err = int(parts[0])
            http_code = int(parts[1]) if len(parts) > 1 else None
            content_len = int(parts[2]) if len(parts) > 2 else None
            return err, http_code, content_len
        except:
            return -1, None, None
    
    def send_data(self, data: str, wait_for_prompt: bool = True) -> bool:
        """
        Send data in transparent mode (after CONNECT)
        
        Args:
            data: Data to send
            wait_for_prompt: Wait for CONNECT prompt
            
        Returns:
            Success status
        """
        if wait_for_prompt:
            # Wait for CONNECT
            time.sleep(0.5)
        
        try:
            self.serial.write(data.encode())
            logger.debug(f"Sent data: {data[:100]}..." if len(data) > 100 else f"Sent data: {data}")
            return True
        except Exception as e:
            logger.error(f"Failed to send data: {e}")
            return False
    
    def exit_data_mode(self) -> bool:
        """Exit data mode using +++"""
        try:
            time.sleep(1)  # Wait 1 second before
            self.serial.write(b'+++')
            time.sleep(1)  # Wait 1 second after
            response = self.serial.read(self.serial.in_waiting).decode('utf-8', errors='ignore')
            return 'OK' in response
        except Exception as e:
            logger.error(f"Failed to exit data mode: {e}")
            return False
    
    # ==================== Basic Module Commands ====================
    
    def test_at(self) -> bool:
        """Test AT command"""
        response = self.send_command('AT')
        return 'OK' in response
    
    def check_sim(self) -> bool:
        """Check SIM card status"""
        response = self.send_command('AT+CPIN?')
        return '+CPIN: READY' in response
    
    def check_registration(self) -> bool:
        """Check network registration"""
        response = self.send_command('AT+CGREG?')
        # Check for registered (home or roaming)
        return '+CGREG: 0,1' in response or '+CGREG: 0,5' in response
    
    def attach_ps(self) -> bool:
        """Attach PS domain"""
        response = self.send_command('AT+CGATT=1', timeout=30)
        return 'OK' in response
    
    def check_ps_attached(self) -> bool:
        """Check if PS domain is attached"""
        response = self.send_command('AT+CGATT?')
        return '+CGATT: 1' in response
    
    # ==================== PDP Context Management ====================
    
    def configure_pdp_context(self, apn: str, username: str = "", password: str = "") -> bool:
        """
        Configure PDP context
        
        Args:
            apn: Access Point Name
            username: Username (optional)
            password: Password (optional)
            
        Returns:
            Success status
        """
        cmd = f'AT+QICSGP={self.context_id},1,"{apn}","{username}","{password}",1'
        response = self.send_command(cmd)
        if 'OK' in response:
            logger.info(f"PDP context {self.context_id} configured with APN: {apn}")
            return True
        return False
    
    def activate_pdp_context(self) -> bool:
        """Activate PDP context"""
        response = self.send_command(f'AT+QIACT={self.context_id}', timeout=150)
        if 'OK' in response:
            logger.info(f"PDP context {self.context_id} activated")
            return True
        return False
    
    def query_pdp_context(self) -> Optional[str]:
        """Query PDP context status and return IP address"""
        response = self.send_command('AT+QIACT?')
        if f'+QIACT: {self.context_id}' in response:
            # Extract IP address
            for line in response.split('\r\n'):
                if f'+QIACT: {self.context_id}' in line:
                    parts = line.split(',')
                    if len(parts) >= 4:
                        ip = parts[3].strip('"')
                        logger.info(f"PDP context {self.context_id} IP: {ip}")
                        return ip
        return None
    
    def deactivate_pdp_context(self) -> bool:
        """Deactivate PDP context"""
        response = self.send_command(f'AT+QIDEACT={self.context_id}', timeout=40)
        if 'OK' in response:
            logger.info(f"PDP context {self.context_id} deactivated")
            return True
        return False
    
    # ==================== HTTP Configuration ====================
    
    def configure_http_context(self, context_id: Optional[int] = None) -> bool:
        """Configure HTTP context ID"""
        cid = context_id or self.context_id
        response = self.send_command(f'AT+QHTTPCFG="contextid",{cid}')
        return 'OK' in response
    
    def configure_http_request_header(self, enable: bool) -> bool:
        """Enable/disable custom HTTP request header"""
        value = 1 if enable else 0
        response = self.send_command(f'AT+QHTTPCFG="requestheader",{value}')
        return 'OK' in response
    
    def configure_http_response_header(self, enable: bool) -> bool:
        """Enable/disable HTTP response header output"""
        value = 1 if enable else 0
        response = self.send_command(f'AT+QHTTPCFG="responseheader",{value}')
        return 'OK' in response
    
    def configure_http_ssl(self, ssl_context_id: Optional[int] = None) -> bool:
        """Configure SSL context ID for HTTPS"""
        ssid = ssl_context_id or self.ssl_context_id
        response = self.send_command(f'AT+QHTTPCFG="sslctxid",{ssid}')
        return 'OK' in response
    
    def configure_http_content_type(self, content_type: int) -> bool:
        """
        Configure HTTP content type
        
        Args:
            content_type: 0=application/x-www-form-urlencoded, 1=text/plain,
                         2=application/octet-stream, 3=multipart/form-data,
                         4=application/json, 5=image/jpeg
        """
        response = self.send_command(f'AT+QHTTPCFG="contenttype",{content_type}')
        return 'OK' in response
    
    # ==================== SSL Configuration ====================
    
    def configure_ssl_version(self, version: int) -> bool:
        """
        Configure SSL version
        
        Args:
            version: 0=SSL3.0, 1=TLS1.0, 2=TLS1.1, 3=TLS1.2, 4=All
        """
        response = self.send_command(f'AT+QSSLCFG="sslversion",{self.ssl_context_id},{version}')
        return 'OK' in response
    
    def configure_ssl_cipher(self, cipher: str) -> bool:
        """Configure SSL cipher suite"""
        response = self.send_command(f'AT+QSSLCFG="ciphersuite",{self.ssl_context_id},{cipher}')
        return 'OK' in response
    
    def configure_ssl_seclevel(self, level: int) -> bool:
        """
        Configure SSL security level
        
        Args:
            level: 0=No authentication, 1=Server authentication,
                  2=Server and client authentication
        """
        response = self.send_command(f'AT+QSSLCFG="seclevel",{self.ssl_context_id},{level}')
        return 'OK' in response
    
    def configure_ssl_cacert(self, cert_path: str) -> bool:
        """Configure CA certificate path"""
        response = self.send_command(f'AT+QSSLCFG="cacert",{self.ssl_context_id},"{cert_path}"')
        return 'OK' in response
    
    def configure_ssl_client_cert(self, cert_path: str) -> bool:
        """Configure client certificate path"""
        response = self.send_command(f'AT+QSSLCFG="clientcert",{self.ssl_context_id},"{cert_path}"')
        return 'OK' in response
    
    def configure_ssl_client_key(self, key_path: str) -> bool:
        """Configure client private key path"""
        response = self.send_command(f'AT+QSSLCFG="clientkey",{self.ssl_context_id},"{key_path}"')
        return 'OK' in response
    
    # ==================== HTTP Operations ====================
    
    def set_http_url(self, url: str, timeout: int = 80) -> bool:
        """
        Set HTTP(S) URL
        
        Args:
            url: URL to access (must start with http:// or https://)
            timeout: Timeout in seconds
            
        Returns:
            Success status
        """
        url_length = len(url)
        response = self.send_command(f'AT+QHTTPURL={url_length},{timeout}')
        
        if 'CONNECT' in response:
            # Send URL data
            if self.send_data(url, wait_for_prompt=False):
                # Wait for OK
                time.sleep(1)
                response = self.serial.read(self.serial.in_waiting).decode('utf-8', errors='ignore')
                if 'OK' in response:
                    logger.info(f"URL set: {url}")
                    return True
        
        return False
    
    def http_get(self, rsptime: int = 80) -> Tuple[int, Optional[int], Optional[int]]:
        """
        Send HTTP GET request
        
        Args:
            rsptime: Response timeout in seconds
            
        Returns:
            Tuple of (error_code, http_response_code, content_length)
        """
        logger.info("Sending HTTP GET request")
        response = self.send_command(f'AT+QHTTPGET={rsptime}')
        
        if 'OK' in response:
            # Wait for URC
            urc = self.wait_for_urc('+QHTTPGET:', rsptime + 10)
            return self.parse_urc_response(urc)
        
        return ErrorCode.UNKNOWN_ERROR.value, None, None
    
    def http_post(self, data: str, input_time: int = 60, rsptime: int = 80) -> Tuple[int, Optional[int], Optional[int]]:
        """
        Send HTTP POST request
        
        Args:
            data: POST data
            input_time: Input timeout in seconds
            rsptime: Response timeout in seconds
            
        Returns:
            Tuple of (error_code, http_response_code, content_length)
        """
        logger.info("Sending HTTP POST request")
        data_length = len(data)
        response = self.send_command(f'AT+QHTTPPOST={data_length},{input_time},{rsptime}')
        
        if 'CONNECT' in response:
            # Send POST data
            if self.send_data(data, wait_for_prompt=False):
                # Wait for OK
                time.sleep(1)
                response = self.serial.read(self.serial.in_waiting).decode('utf-8', errors='ignore')
                if 'OK' in response:
                    # Wait for URC
                    urc = self.wait_for_urc('+QHTTPPOST:', rsptime + 10)
                    return self.parse_urc_response(urc)
        
        return ErrorCode.UNKNOWN_ERROR.value, None, None
    
    def http_post_file(self, filename: str, rsptime: int = 80) -> Tuple[int, Optional[int], Optional[int]]:
        """
        Send HTTP POST request from file
        
        Args:
            filename: File path (e.g., 'RAM:post.txt' or 'UFS:post.txt')
            rsptime: Response timeout in seconds
            
        Returns:
            Tuple of (error_code, http_response_code, content_length)
        """
        logger.info(f"Sending HTTP POST request from file: {filename}")
        response = self.send_command(f'AT+QHTTPPOSTFILE="{filename}",{rsptime}')
        
        if 'OK' in response:
            # Wait for URC
            urc = self.wait_for_urc('+QHTTPPOSTFILE:', rsptime + 10)
            return self.parse_urc_response(urc)
        
        return ErrorCode.UNKNOWN_ERROR.value, None, None
    
    def http_put(self, data: str, input_time: int = 60, rsptime: int = 80) -> Tuple[int, Optional[int], Optional[int]]:
        """
        Send HTTP PUT request
        
        Args:
            data: PUT data
            input_time: Input timeout in seconds
            rsptime: Response timeout in seconds
            
        Returns:
            Tuple of (error_code, http_response_code, content_length)
        """
        logger.info("Sending HTTP PUT request")
        data_length = len(data)
        response = self.send_command(f'AT+QHTTPPUT={data_length},{input_time},{rsptime}')
        
        if 'CONNECT' in response:
            # Send PUT data
            if self.send_data(data, wait_for_prompt=False):
                # Wait for OK
                time.sleep(1)
                response = self.serial.read(self.serial.in_waiting).decode('utf-8', errors='ignore')
                if 'OK' in response:
                    # Wait for URC
                    urc = self.wait_for_urc('+QHTTPPUT:', rsptime + 10)
                    return self.parse_urc_response(urc)
        
        return ErrorCode.UNKNOWN_ERROR.value, None, None
    
    def http_read(self, wait_time: int = 80) -> Optional[str]:
        """
        Read HTTP response via UART
        
        Args:
            wait_time: Wait timeout in seconds
            
        Returns:
            Response data or None
        """
        logger.info("Reading HTTP response")
        response = self.send_command(f'AT+QHTTPREAD={wait_time}')
        
        if 'CONNECT' in response:
            # Read data until OK
            data = ""
            start_time = time.time()
            
            while time.time() - start_time < wait_time:
                if self.serial.in_waiting > 0:
                    chunk = self.serial.read(self.serial.in_waiting).decode('utf-8', errors='ignore')
                    data += chunk
                    
                    if 'OK\r\n+QHTTPREAD: 0' in data or '+QHTTPREAD: 0' in data:
                        logger.info(f"HTTP response read successfully ({len(data)} bytes)")
                        return data
                
                time.sleep(0.1)
        
        return None
    
    def http_read_file(self, filename: str, wait_time: int = 80) -> bool:
        """
        Read HTTP response to file
        
        Args:
            filename: File path (e.g., 'RAM:response.txt')
            wait_time: Wait timeout in seconds
            
        Returns:
            Success status
        """
        logger.info(f"Reading HTTP response to file: {filename}")
        response = self.send_command(f'AT+QHTTPREADFILE="{filename}",{wait_time}')
        
        if 'OK' in response:
            # Wait for URC
            urc = self.wait_for_urc('+QHTTPREADFILE:', wait_time + 10)
            err, _, _ = self.parse_urc_response(urc)
            return err == 0
        
        return False
    
    def http_stop(self) -> bool:
        """Cancel HTTP(S) request"""
        response = self.send_command('AT+QHTTPSTOP', timeout=10)
        return 'OK' in response
    
    # ==================== Error Handling ====================
    
    def get_error_description(self, error_code: int) -> str:
        """Get detailed error description from error code"""
        if error_code in ERROR_DESCRIPTIONS:
            return ERROR_DESCRIPTIONS[error_code]["description"]
        return f"Unknown error code ({error_code})"
    
    def get_error_solution(self, error_code: int) -> str:
        """Get suggested solution for error code"""
        if error_code in ERROR_DESCRIPTIONS:
            return ERROR_DESCRIPTIONS[error_code]["solution"]
        return "Contact technical support for assistance"
    
    def get_http_status_description(self, http_code: int) -> str:
        """Get HTTP status description"""
        if http_code in HTTP_STATUS_DESCRIPTIONS:
            return HTTP_STATUS_DESCRIPTIONS[http_code]
        return f"HTTP {http_code} - Unknown status code"
    
    def handle_error(self, error_code: int) -> bool:
        """
        Handle common errors with automatic recovery
        
        Args:
            error_code: Error code to handle
            
        Returns:
            True if error was handled and should retry
        """
        error_desc = self.get_error_description(error_code)
        error_solution = self.get_error_solution(error_code)
        
        logger.warning(f"Error {error_code}: {error_desc}")
        logger.info(f"Suggested solution: {error_solution}")
        
        if error_code == ErrorCode.DNS_ERROR.value:
            logger.info("Attempting to resolve DNS issue...")
            # Could implement DNS fix here
            return False
        
        elif error_code == ErrorCode.NETWORK_DEACTIVATED.value:
            logger.info("Attempting to reactivate network context...")
            if self.activate_pdp_context():
                logger.info("✓ Network context reactivated successfully")
                return True
            else:
                logger.error("✗ Failed to reactivate network context")
                return False
        
        elif error_code == ErrorCode.SOCKET_READ_ERROR.value:
            logger.info("Socket read error is recoverable - will retry")
            return True
        
        elif error_code in [ErrorCode.NETWORK_BUSY.value, ErrorCode.HTTP_BUSY.value]:
            logger.info("Service is busy - waiting 5 seconds before retry...")
            time.sleep(5)
            return True
        
        elif error_code == ErrorCode.NETWORK_NO_CONFIG.value:
            logger.error("Network context not configured - please run initialization first")
            return False
        
        elif error_code == ErrorCode.URL_ERROR.value:
            logger.error("URL format error - please check the URL format")
            return False
        
        return False


# ==================== Example Functions ====================

def example_http_get(module: QuectelEG25G, url: str, retry_count: int = 3):
    """Example: HTTP GET request with retry logic"""
    logger.info("=" * 60)
    logger.info("Example: HTTP GET Request")
    logger.info(f"URL: {url}")
    logger.info("=" * 60)
    
    # Validate URL
    if not url.startswith(('http://', 'https://')):
        logger.error("✗ Invalid URL format - must start with http:// or https://")
        logger.info("Tip: Example URL format: http://example.com/api/data")
        return
    
    # Set URL
    logger.info("Setting URL...")
    if not module.set_http_url(url):
        logger.error("✗ Failed to set URL")
        logger.info("Tip: Check that the URL is properly formatted and not too long")
        return
    
    # Send GET request with retry logic
    retry = 0
    while retry < retry_count:
        if retry > 0:
            logger.info(f"Retry attempt {retry}/{retry_count - 1}...")
        
        logger.info("Sending GET request...")
        err, http_code, content_len = module.http_get(rsptime=80)
        
        # Check result
        if err == 0:
            logger.info(f"✓ GET request successful")
            logger.info(f"  HTTP Status: {module.get_http_status_description(http_code)}")
            logger.info(f"  Content Length: {content_len} bytes")
            
            if http_code == 200:
                # Read response
                logger.info("Reading response data...")
                response_data = module.http_read(wait_time=80)
                if response_data:
                    logger.info(f"✓ Response received successfully ({len(response_data)} bytes)")
                    # Print first 500 characters
                    preview = response_data[:500] if len(response_data) > 500 else response_data
                    logger.info(f"Response preview:\n{preview}")
                    if len(response_data) > 500:
                        logger.info("... (response truncated)")
                else:
                    logger.error("✗ Failed to read response data")
                    logger.info("Tip: The server may have closed the connection. Try increasing the read timeout.")
            else:
                logger.warning(f"Server returned non-200 status code")
                # Try to read error response
                error_response = module.http_read(wait_time=30)
                if error_response:
                    logger.info(f"Server error response: {error_response[:200]}")
            break
        else:
            logger.error(f"✗ GET request failed with error code {err}")
            logger.error(f"  Error: {module.get_error_description(err)}")
            logger.info(f"  Solution: {module.get_error_solution(err)}")
            
            # Check if error is retryable
            if module.handle_error(err) and retry < retry_count - 1:
                retry += 1
                time.sleep(2)
            else:
                break


def example_http_post(module: QuectelEG25G, url: str, post_data: str, retry_count: int = 3):
    """Example: HTTP POST request with retry logic"""
    logger.info("=" * 60)
    logger.info("Example: HTTP POST Request")
    logger.info(f"URL: {url}")
    logger.info("=" * 60)
    
    # Validate URL
    if not url.startswith(('http://', 'https://')):
        logger.error("✗ Invalid URL format - must start with http:// or https://")
        logger.info("Tip: Example URL format: http://example.com/api/data")
        return
    
    # Validate POST data
    if not post_data:
        logger.warning("POST data is empty - sending empty POST request")
    else:
        logger.info(f"POST data length: {len(post_data)} bytes")
        if len(post_data) > 100:
            logger.debug(f"POST data preview: {post_data[:100]}...")
        else:
            logger.debug(f"POST data: {post_data}")
    
    # Set URL
    logger.info("Setting URL...")
    if not module.set_http_url(url):
        logger.error("✗ Failed to set URL")
        logger.info("Tip: Check that the URL is properly formatted and not too long")
        return
    
    # Send POST request with retry logic
    retry = 0
    while retry < retry_count:
        if retry > 0:
            logger.info(f"Retry attempt {retry}/{retry_count - 1}...")
        
        logger.info("Sending POST request...")
        err, http_code, content_len = module.http_post(post_data, input_time=60, rsptime=80)
        
        # Check result
        if err == 0:
            logger.info(f"✓ POST request successful")
            logger.info(f"  HTTP Status: {module.get_http_status_description(http_code)}")
            logger.info(f"  Content Length: {content_len} bytes")
            
            if http_code in [200, 201]:
                # Read response
                logger.info("Reading response data...")
                response_data = module.http_read(wait_time=80)
                if response_data:
                    logger.info(f"✓ Response received successfully ({len(response_data)} bytes)")
                    preview = response_data[:500] if len(response_data) > 500 else response_data
                    logger.info(f"Response preview:\n{preview}")
                    if len(response_data) > 500:
                        logger.info("... (response truncated)")
                else:
                    logger.error("✗ Failed to read response data")
                    logger.info("Tip: The server may have closed the connection. Try increasing the read timeout.")
            elif http_code == 400:
                logger.error("✗ Bad Request - check POST data format")
                # Try to read error response
                error_response = module.http_read(wait_time=30)
                if error_response:
                    logger.info(f"Server error details: {error_response[:200]}")
            else:
                logger.warning(f"Server returned non-200 status code")
                # Try to read error response
                error_response = module.http_read(wait_time=30)
                if error_response:
                    logger.info(f"Server response: {error_response[:200]}")
            break
        else:
            logger.error(f"✗ POST request failed with error code {err}")
            logger.error(f"  Error: {module.get_error_description(err)}")
            logger.info(f"  Solution: {module.get_error_solution(err)}")
            
            # Check if error is retryable
            if module.handle_error(err) and retry < retry_count - 1:
                retry += 1
                time.sleep(2)
            else:
                break


def example_http_put(module: QuectelEG25G, url: str, put_data: str, retry_count: int = 3):
    """Example: HTTP PUT request with retry logic"""
    logger.info("=" * 60)
    logger.info("Example: HTTP PUT Request")
    logger.info(f"URL: {url}")
    logger.info("=" * 60)
    
    # Validate URL
    if not url.startswith(('http://', 'https://')):
        logger.error("✗ Invalid URL format - must start with http:// or https://")
        logger.info("Tip: Example URL format: http://example.com/api/resource/123")
        return
    
    # Validate PUT data
    if not put_data:
        logger.warning("PUT data is empty - sending empty PUT request")
    else:
        logger.info(f"PUT data length: {len(put_data)} bytes")
        if len(put_data) > 100:
            logger.debug(f"PUT data preview: {put_data[:100]}...")
        else:
            logger.debug(f"PUT data: {put_data}")
    
    # Set URL
    logger.info("Setting URL...")
    if not module.set_http_url(url):
        logger.error("✗ Failed to set URL")
        logger.info("Tip: Check that the URL is properly formatted and not too long")
        return
    
    # Send PUT request with retry logic
    retry = 0
    while retry < retry_count:
        if retry > 0:
            logger.info(f"Retry attempt {retry}/{retry_count - 1}...")
        
        logger.info("Sending PUT request...")
        err, http_code, content_len = module.http_put(put_data, input_time=60, rsptime=80)
        
        # Check result
        if err == 0:
            logger.info(f"✓ PUT request successful")
            logger.info(f"  HTTP Status: {module.get_http_status_description(http_code)}")
            logger.info(f"  Content Length: {content_len} bytes")
            
            if http_code in [200, 201, 204]:
                # Read response if available
                if content_len and content_len > 0:
                    logger.info("Reading response data...")
                    response_data = module.http_read(wait_time=80)
                    if response_data:
                        logger.info(f"✓ Response received successfully ({len(response_data)} bytes)")
                        preview = response_data[:500] if len(response_data) > 500 else response_data
                        logger.info(f"Response preview:\n{preview}")
                        if len(response_data) > 500:
                            logger.info("... (response truncated)")
                else:
                    logger.info("✓ No response body (204 No Content)")
            elif http_code == 404:
                logger.error("✗ Resource not found - check the URL")
            elif http_code == 409:
                logger.error("✗ Conflict - resource state conflict")
            else:
                logger.warning(f"Server returned non-200 status code")
                # Try to read error response
                error_response = module.http_read(wait_time=30)
                if error_response:
                    logger.info(f"Server response: {error_response[:200]}")
            break
        else:
            logger.error(f"✗ PUT request failed with error code {err}")
            logger.error(f"  Error: {module.get_error_description(err)}")
            logger.info(f"  Solution: {module.get_error_solution(err)}")
            
            # Check if error is retryable
            if module.handle_error(err) and retry < retry_count - 1:
                retry += 1
                time.sleep(2)
            else:
                break


def example_https_get(module: QuectelEG25G, url: str, retry_count: int = 3):
    """Example: HTTPS GET request with retry logic"""
    logger.info("=" * 60)
    logger.info("Example: HTTPS GET Request")
    logger.info(f"URL: {url}")
    logger.info("=" * 60)
    
    # Validate URL
    if not url.startswith('https://'):
        logger.error("✗ URL must use HTTPS protocol")
        logger.info("Tip: Change URL to start with https://")
        return
    
    # Configure SSL
    logger.info("Configuring SSL for HTTPS...")
    logger.info("  SSL Version: TLS 1.0")
    logger.info("  Cipher Suite: RC4-SHA (0x0005)")
    logger.info("  Security Level: No authentication")
    
    if not module.configure_ssl_version(1):  # TLS 1.0
        logger.warning("Failed to set SSL version, using default")
    
    if not module.configure_ssl_cipher('0x0005'):  # RC4-SHA
        logger.warning("Failed to set cipher suite, using default")
        logger.info("Tip: Module will negotiate best available cipher with server")
    
    if not module.configure_ssl_seclevel(0):  # No authentication
        logger.warning("Failed to set SSL security level")
    
    logger.info("✓ SSL configuration complete")
    
    # Set URL
    logger.info("Setting URL...")
    if not module.set_http_url(url):
        logger.error("✗ Failed to set URL")
        return
    
    # Send GET request with retry logic
    retry = 0
    while retry < retry_count:
        if retry > 0:
            logger.info(f"Retry attempt {retry}/{retry_count - 1}...")
        
        logger.info("Sending HTTPS GET request...")
        err, http_code, content_len = module.http_get(rsptime=80)
        
        # Check result
        if err == 0:
            logger.info(f"✓ HTTPS GET request successful")
            logger.info(f"  HTTP Status: {module.get_http_status_description(http_code)}")
            logger.info(f"  Content Length: {content_len} bytes")
            
            if http_code == 200:
                # Read response
                logger.info("Reading response data...")
                response_data = module.http_read(wait_time=80)
                if response_data:
                    logger.info(f"✓ Response received successfully ({len(response_data)} bytes)")
                    preview = response_data[:500] if len(response_data) > 500 else response_data
                    logger.info(f"Response preview:\n{preview}")
                    if len(response_data) > 500:
                        logger.info("... (response truncated)")
                else:
                    logger.error("✗ Failed to read response data")
            else:
                logger.warning(f"Server returned non-200 status code")
                # Try to read error response
                error_response = module.http_read(wait_time=30)
                if error_response:
                    logger.info(f"Server response: {error_response[:200]}")
            break
        else:
            logger.error(f"✗ HTTPS GET request failed with error code {err}")
            logger.error(f"  Error: {module.get_error_description(err)}")
            logger.info(f"  Solution: {module.get_error_solution(err)}")
            
            if err == ErrorCode.SOCKET_CONNECT_ERROR.value:
                logger.info("Additional tips for HTTPS connection errors:")
                logger.info("  - Verify server supports TLS 1.0 or higher")
                logger.info("  - Check if server certificate is valid")
                logger.info("  - Try using a different SSL version (e.g., TLS 1.2)")
            
            # Check if error is retryable
            if module.handle_error(err) and retry < retry_count - 1:
                retry += 1
                time.sleep(2)
            else:
                break


def example_https_post_with_auth(module: QuectelEG25G, url: str, post_data: str, 
                                ca_cert: str = "RAM:cacert.pem",
                                client_cert: str = "RAM:clientcert.pem", 
                                client_key: str = "RAM:clientkey.pem"):
    """Example: HTTPS POST request with client authentication"""
    logger.info("=" * 60)
    logger.info("Example: HTTPS POST Request with Client Authentication")
    logger.info(f"URL: {url}")
    logger.info("=" * 60)
    
    # Validate URL
    if not url.startswith('https://'):
        logger.error("✗ URL must use HTTPS for secure authentication")
        logger.info("Tip: Change URL to start with https://")
        return
    
    # Configure SSL with authentication
    logger.info("Configuring SSL with client authentication...")
    logger.info(f"  CA Certificate: {ca_cert}")
    logger.info(f"  Client Certificate: {client_cert}")
    logger.info(f"  Client Key: {client_key}")
    
    # Configure SSL settings
    if not module.configure_ssl_version(1):  # TLS 1.0
        logger.warning("Failed to set SSL version, using default")
    
    if not module.configure_ssl_cipher('0x0005'):  # RC4-SHA
        logger.warning("Failed to set cipher suite, using default")
    
    if not module.configure_ssl_seclevel(2):  # Server and client authentication
        logger.error("✗ Failed to set SSL security level")
        logger.info("Tip: Ensure SSL context is properly initialized")
        return
    
    # Configure certificates
    logger.info("Loading certificates...")
    if not module.configure_ssl_cacert(ca_cert):
        logger.error(f"✗ Failed to load CA certificate: {ca_cert}")
        logger.info("Tip: Ensure certificate file exists and is in correct format")
        return
    
    if not module.configure_ssl_client_cert(client_cert):
        logger.error(f"✗ Failed to load client certificate: {client_cert}")
        logger.info("Tip: Ensure certificate file exists and is in PEM format")
        return
    
    if not module.configure_ssl_client_key(client_key):
        logger.error(f"✗ Failed to load client key: {client_key}")
        logger.info("Tip: Ensure key file exists and matches the client certificate")
        return
    
    logger.info("✓ SSL configuration complete")
    
    # Set URL
    logger.info("Setting URL...")
    if not module.set_http_url(url):
        logger.error("✗ Failed to set URL")
        return
    
    # Validate POST data
    if not post_data:
        logger.warning("POST data is empty")
    else:
        logger.info(f"POST data length: {len(post_data)} bytes")
        logger.debug(f"POST data preview: {post_data[:100]}...")
    
    # Send POST request
    logger.info("Sending HTTPS POST request...")
    err, http_code, content_len = module.http_post(post_data, input_time=60, rsptime=80)
    
    # Check result
    if err == 0:
        logger.info(f"✓ HTTPS POST request completed successfully")
        logger.info(f"  HTTP Status: {module.get_http_status_description(http_code)}")
        logger.info(f"  Content Length: {content_len} bytes")
        
        if http_code == 200:
            # Read response
            logger.info("Reading response data...")
            response_data = module.http_read(wait_time=80)
            if response_data:
                logger.info(f"✓ Response received successfully ({len(response_data)} bytes)")
                preview = response_data[:500] if len(response_data) > 500 else response_data
                logger.info(f"Response preview:\n{preview}")
                if len(response_data) > 500:
                    logger.info("... (response truncated)")
            else:
                logger.error("✗ Failed to read response data")
        elif http_code == 401:
            logger.error("✗ Authentication failed - check client certificates")
        elif http_code == 403:
            logger.error("✗ Access forbidden - client certificate may not be authorized")
        else:
            logger.warning(f"Server returned non-200 status code")
            # Try to read error response
            error_response = module.http_read(wait_time=30)
            if error_response:
                logger.info(f"Server error response: {error_response[:200]}")
    else:
        logger.error(f"✗ HTTPS POST request failed with error code {err}")
        logger.error(f"  Error: {module.get_error_description(err)}")
        logger.info(f"  Solution: {module.get_error_solution(err)}")
        
        if err == ErrorCode.SOCKET_CONNECT_ERROR.value:
            logger.info("Additional tips for SSL connection errors:")
            logger.info("  - Verify server supports the configured SSL/TLS version")
            logger.info("  - Check if certificates are valid and not expired")
            logger.info("  - Ensure server hostname matches certificate CN/SAN")


def example_http_post_file(module: QuectelEG25G, url: str, filename: str):
    """Example: HTTP POST from file"""
    logger.info("=" * 60)
    logger.info("Example: HTTP POST from File")
    logger.info(f"URL: {url}")
    logger.info(f"File: {filename}")
    logger.info("=" * 60)
    
    # Validate URL
    if not url.startswith(('http://', 'https://')):
        logger.error("✗ Invalid URL format - must start with http:// or https://")
        return
    
    # Validate filename
    if not filename:
        logger.error("✗ Filename is required")
        return
    
    if not filename.startswith(('RAM:', 'UFS:', 'SD:')):
        logger.warning("File path should start with storage type (RAM:, UFS:, or SD:)")
        logger.info("Example: RAM:post_data.txt or UFS:data.json")
    
    logger.info(f"Using file: {filename}")
    logger.info("Note: Ensure the file exists and contains the POST data")
    
    # Set URL
    logger.info("Setting URL...")
    if not module.set_http_url(url):
        logger.error("✗ Failed to set URL")
        logger.info("Tip: Check that the URL is properly formatted")
        return
    
    # Send POST from file
    logger.info("Sending POST request from file...")
    err, http_code, content_len = module.http_post_file(filename, rsptime=80)
    
    # Check result
    if err == 0:
        logger.info(f"✓ POST from file completed successfully")
        logger.info(f"  HTTP Status: {module.get_http_status_description(http_code)}")
        logger.info(f"  Content Length: {content_len} bytes")
        
        if http_code in [200, 201]:
            # Read response to file
            response_file = "RAM:response.txt"
            logger.info(f"Saving response to file: {response_file}")
            
            if module.http_read_file(response_file, wait_time=80):
                logger.info(f"✓ Response saved successfully to {response_file}")
                logger.info("Tip: Use AT+QFREAD command to read the response file")
            else:
                logger.error("✗ Failed to save response to file")
                # Try to read response directly
                logger.info("Attempting to read response directly...")
                response_data = module.http_read(wait_time=80)
                if response_data:
                    logger.info(f"✓ Response received ({len(response_data)} bytes)")
                    preview = response_data[:200]
                    logger.info(f"Response preview:\n{preview}")
        elif http_code == 413:
            logger.error("✗ Request entity too large - file size exceeds server limit")
        else:
            logger.warning(f"Server returned non-200 status code")
            # Try to read error response
            error_response = module.http_read(wait_time=30)
            if error_response:
                logger.info(f"Server response: {error_response[:200]}")
    else:
        logger.error(f"✗ POST from file failed with error code {err}")
        logger.error(f"  Error: {module.get_error_description(err)}")
        logger.info(f"  Solution: {module.get_error_solution(err)}")
        
        if err == ErrorCode.MEMORY_FAILED.value:
            logger.info("Additional tips:")
            logger.info("  - Check if file exists using AT+QFLST command")
            logger.info("  - Verify file is not too large for available memory")
            logger.info("  - Try using a smaller file or UFS storage")


def initialize_module(module: QuectelEG25G, apn: str, username: str = "", password: str = "") -> bool:
    """Initialize and configure the module with comprehensive error handling"""
    logger.info("=" * 60)
    logger.info("Quectel EG25-G Module Initialization")
    logger.info("=" * 60)
    logger.info(f"Configuration:")
    logger.info(f"  APN: {apn}")
    logger.info(f"  Username: {'<provided>' if username else '<none>'}")
    logger.info(f"  Password: {'<provided>' if password else '<none>'}")
    logger.info("=" * 60)
    
    # Validate APN
    if not apn:
        logger.error("✗ APN is required for network connection")
        logger.info("Tip: Contact your carrier for the correct APN settings")
        logger.info("Common APNs:")
        logger.info("  - Sixfab: super")
        logger.info("  - For other carriers, please contact your provider")
        return False
    
    # Test AT communication
    logger.info("Step 1/8: Testing module communication...")
    retry_count = 3
    for i in range(retry_count):
        if module.test_at():
            logger.info("✓ Module communication established")
            break
        logger.warning(f"Communication attempt {i+1}/{retry_count} failed")
        time.sleep(1)
    else:
        logger.error("✗ Cannot communicate with module")
        logger.info("Troubleshooting tips:")
        logger.info("  - Check serial port connection")
        logger.info("  - Verify correct port name and baud rate")
        logger.info("  - Ensure module is powered on")
        logger.info("  - Try resetting the module")
        return False
    
    # Check SIM card
    logger.info("Step 2/8: Checking SIM card status...")
    if not module.check_sim():
        logger.error("✗ SIM card not ready")
        logger.info("Troubleshooting tips:")
        logger.info("  - Ensure SIM card is properly inserted")
        logger.info("  - Check if SIM card is activated")
        logger.info("  - Verify SIM card is not locked (PIN)")
        logger.info("  - Try cleaning SIM card contacts")
        return False
    logger.info("✓ SIM card detected and ready")
    
    # Check network registration
    logger.info("Step 3/8: Registering on network...")
    logger.info("This may take up to 30 seconds...")
    
    registration_timeout = 30  # seconds
    start_time = time.time()
    last_status_time = start_time
    
    while time.time() - start_time < registration_timeout:
        if module.check_registration():
            logger.info("✓ Successfully registered on network")
            break
        
        # Show progress every 5 seconds
        if time.time() - last_status_time >= 5:
            elapsed = int(time.time() - start_time)
            remaining = registration_timeout - elapsed
            logger.info(f"Still searching for network... ({remaining}s remaining)")
            last_status_time = time.time()
        
        time.sleep(1)
    else:
        logger.error("✗ Network registration failed")
        logger.info("Troubleshooting tips:")
        logger.info("  - Check antenna connection")
        logger.info("  - Verify you're in a coverage area")
        logger.info("  - Check if SIM card is active and has service")
        logger.info("  - Try manual network selection with AT+COPS")
        return False
    
    # Attach to packet service
    logger.info("Step 4/8: Attaching to packet service...")
    if not module.check_ps_attached():
        logger.info("Not attached, attempting to attach...")
        if not module.attach_ps():
            logger.error("✗ Failed to attach to packet service")
            logger.info("Troubleshooting tips:")
            logger.info("  - Check if data service is enabled on SIM")
            logger.info("  - Verify carrier supports packet data")
            logger.info("  - Check account has active data plan")
            return False
    logger.info("✓ Packet service attached")
    
    # Configure PDP context
    logger.info("Step 5/8: Configuring PDP context...")
    logger.info(f"Setting APN to: {apn}")
    if not module.configure_pdp_context(apn, username, password):
        logger.error("✗ Failed to configure PDP context")
        logger.info("Troubleshooting tips:")
        logger.info("  - Verify APN settings are correct")
        logger.info("  - Check if username/password are required")
        logger.info("  - Contact carrier for correct APN details")
        return False
    logger.info("✓ PDP context configured successfully")
    
    # Activate PDP context
    logger.info("Step 6/8: Activating data connection...")
    logger.info("This may take up to 150 seconds...")
    
    if not module.activate_pdp_context():
        logger.error("✗ Failed to activate data connection")
        logger.info("Troubleshooting tips:")
        logger.info("  - Check signal strength (AT+CSQ)")
        logger.info("  - Verify APN settings are correct")
        logger.info("  - Ensure data service is not blocked")
        logger.info("  - Check if roaming is required and enabled")
        logger.info("  - Try deactivating and reactivating (AT+QIDEACT)")
        return False
    logger.info("✓ Data connection activated")
    
    # Query and display IP address
    logger.info("Step 7/8: Retrieving IP address...")
    ip = module.query_pdp_context()
    if ip:
        logger.info(f"✓ IP Address assigned: {ip}")
        logger.info("  Network connection is ready for HTTP(S) requests")
    else:
        logger.warning("⚠ Could not retrieve IP address")
        logger.info("  This may be normal - connection might still work")
    
    # Configure HTTP service
    logger.info("Step 8/8: Configuring HTTP service...")
    
    if not module.configure_http_context():
        logger.error("✗ Failed to configure HTTP context")
        logger.info("Tip: Try resetting HTTP service with AT+QHTTPSTOP")
        return False
    logger.info("✓ HTTP context configured")
    
    # Enable response headers for better debugging
    if module.configure_http_response_header(True):
        logger.info("✓ HTTP response headers enabled (for debugging)")
    
    # Configure SSL for HTTPS support
    if module.configure_http_ssl():
        logger.info("✓ SSL/TLS support configured for HTTPS")
    
    # Success summary
    logger.info("=" * 60)
    logger.info("✅ Module initialization completed successfully!")
    logger.info("=" * 60)
    logger.info("Ready to perform HTTP(S) requests:")
    logger.info("  - HTTP GET/POST/PUT supported")
    logger.info("  - HTTPS with SSL/TLS supported")
    logger.info("  - File upload/download supported")
    logger.info("=" * 60)
    
    return True


def test_all_examples(module: QuectelEG25G):
    """Run all example scenarios"""
    
    # Example URLs (replace with your actual test servers)
    http_get_url = "http://httpbin.org/get"
    http_post_url = "http://httpbin.org/post"
    http_put_url = "http://httpbin.org/put"
    https_get_url = "https://httpbin.org/get"
    https_post_url = "https://httpbin.org/post"
    
    # Test data
    post_data = "param1=value1&param2=value2&test=quectel"
    put_data = "update_field=new_value&timestamp=1234567890"
    
    # Run examples
    try:
        # HTTP GET
        example_http_get(module, http_get_url)
        time.sleep(2)
        
        # HTTP POST
        example_http_post(module, http_post_url, post_data)
        time.sleep(2)
        
        # HTTP PUT
        example_http_put(module, http_put_url, put_data)
        time.sleep(2)
        
        # HTTPS GET
        example_https_get(module, https_get_url)
        time.sleep(2)
        
        # HTTPS POST (uncomment if you have SSL certificates configured)
        # example_https_post_with_auth(module, https_post_url, post_data)
        # time.sleep(2)
        
    except KeyboardInterrupt:
        logger.info("Tests interrupted by user")
    except Exception as e:
        logger.error(f"Error during tests: {e}")


def main():
    """Main function"""
    
    # Configuration
    SERIAL_PORT = '/dev/ttyUSB2'  # Change to your port (COM3 for Windows)
    BAUDRATE = 115200
    APN = 'super'  # Default for Sixfab SIM, change to your carrier's APN if different
    
    # Print banner
    print("\n" + "=" * 70)
    print("  Quectel EG25-G HTTP(S) Communication Script")
    print("  Version 1.0")
    print("=" * 70 + "\n")
    
    # Create module instance
    module = QuectelEG25G(port=SERIAL_PORT, baudrate=BAUDRATE)
    
    try:
        # Connect to module
        logger.info(f"Connecting to module on {SERIAL_PORT}...")
        if not module.connect():
            logger.error("Failed to connect to module")
            return 1
        
        # Initialize module
        if not initialize_module(module, APN):
            logger.error("Module initialization failed")
            return 1
        
        # Run test examples
        test_all_examples(module)
        
        # Cleanup
        logger.info("\nCleaning up...")
        module.deactivate_pdp_context()
        
        logger.info("\n✓ All operations completed successfully!")
        return 0
        
    except KeyboardInterrupt:
        logger.info("\nOperation cancelled by user")
        return 130
        
    except Exception as e:
        logger.error(f"\nUnexpected error: {e}", exc_info=True)
        return 1
        
    finally:
        # Disconnect
        module.disconnect()
        logger.info("Module disconnected")


# ==================== CLI Interface ====================

def interactive_mode():
    """Interactive command-line interface"""
    print("\n" + "=" * 70)
    print("  Quectel EG25-G Interactive Mode")
    print("=" * 70)
    
    # Get configuration
    port = input("\nEnter serial port (default: /dev/ttyUSB2): ").strip() or '/dev/ttyUSB2'
    apn = input("Enter APN (default: super): ").strip() or 'super'
    
    module = QuectelEG25G(port=port)
    
    try:
        if not module.connect():
            print("Failed to connect to module")
            return
        
        if not initialize_module(module, apn):
            print("Module initialization failed")
            return
        
        while True:
            print("\n" + "=" * 70)
            print("Options:")
            print("  1. HTTP GET Request")
            print("  2. HTTP POST Request")
            print("  3. HTTP PUT Request")
            print("  4. HTTPS GET Request")
            print("  5. Check Module Status")
            print("  6. Run All Examples")
            print("  0. Exit")
            print("=" * 70)
            
            choice = input("\nSelect option: ").strip()
            
            if choice == '1':
                url = input("Enter URL: ").strip()
                example_http_get(module, url)
                
            elif choice == '2':
                url = input("Enter URL: ").strip()
                data = input("Enter POST data: ").strip()
                example_http_post(module, url, data)
                
            elif choice == '3':
                url = input("Enter URL: ").strip()
                data = input("Enter PUT data: ").strip()
                example_http_put(module, url, data)
                
            elif choice == '4':
                url = input("Enter URL: ").strip()
                example_https_get(module, url)
                
            elif choice == '5':
                print("\nModule Status:")
                print(f"  SIM: {'Ready' if module.check_sim() else 'Not Ready'}")
                print(f"  Network: {'Registered' if module.check_registration() else 'Not Registered'}")
                print(f"  PS: {'Attached' if module.check_ps_attached() else 'Not Attached'}")
                ip = module.query_pdp_context()
                print(f"  IP Address: {ip if ip else 'Not Available'}")
                
            elif choice == '6':
                test_all_examples(module)
                
            elif choice == '0':
                break
                
            else:
                print("Invalid option")
        
        module.deactivate_pdp_context()
        
    except KeyboardInterrupt:
        print("\nExiting...")
        
    finally:
        module.disconnect()


if __name__ == "__main__":
    import argparse
    
    parser = argparse.ArgumentParser(description='Quectel EG25-G HTTP(S) Communication Script')
    parser.add_argument('-p', '--port', default='/dev/ttyUSB2', help='Serial port (default: /dev/ttyUSB2)')
    parser.add_argument('-b', '--baudrate', type=int, default=115200, help='Baud rate (default: 115200)')
    parser.add_argument('-a', '--apn', default='super', help='APN (default: super)')
    parser.add_argument('-i', '--interactive', action='store_true', help='Run in interactive mode')
    parser.add_argument('-u', '--url', help='URL for quick test')
    parser.add_argument('-m', '--method', choices=['GET', 'POST', 'PUT'], default='GET', help='HTTP method')
    parser.add_argument('-d', '--data', help='Data for POST/PUT request')
    parser.add_argument('-v', '--verbose', action='store_true', help='Enable verbose logging')
    
    args = parser.parse_args()
    
    # Set logging level
    if args.verbose:
        logging.getLogger().setLevel(logging.DEBUG)
    
    # Interactive mode
    if args.interactive:
        interactive_mode()
        sys.exit(0)
    
    # Quick test mode
    if args.url:
        module = QuectelEG25G(port=args.port, baudrate=args.baudrate)
        
        try:
            if module.connect():
                if initialize_module(module, args.apn):
                    if args.method == 'GET':
                        example_http_get(module, args.url)
                    elif args.method == 'POST':
                        data = args.data or "test=data"
                        example_http_post(module, args.url, data)
                    elif args.method == 'PUT':
                        data = args.data or "test=data"
                        example_http_put(module, args.url, data)
                
                module.deactivate_pdp_context()
        finally:
            module.disconnect()
        
        sys.exit(0)
    
    # Default: run all examples
    sys.exit(main())