# core/payload_router.py
"""
Defines the PayloadRouter class, responsible for directing incoming,
reassembled SFP payloads to the correct application-level handler.
"""

import logging
import json
from typing import Dict, Callable

# Definisci il tipo per un handler, se vuoi essere più esplicito
PayloadHandler = Callable[[bytearray], None]


class PayloadRouter:
    """
    Inspects SFP flow IDs and routes payloads to registered handlers.
    Also contains the implementations for each specific payload handler.
    """

    def __init__(self):
        """Create a PayloadRouter instance.

        The router exposes `get_handlers()` which returns a mapping suitable
        for registration with the transport layer. Handler methods are
        instance methods named `handle_*_payload` and may be overridden or
        extended by subclasses.
        """
        self._log_prefix = "[PayloadRouter]"
        logging.info(f"{self._log_prefix} Initializing...")

    def get_handlers(self) -> Dict[int, PayloadHandler]:
        """Return a mapping of SFP flow IDs to handler callables.

        The returned dictionary is suitable to pass directly to
        `SfpTransport.add_payload_handlers`. Handlers are instance methods
        on this router and follow the signature: handler(payload: bytearray) -> None.
        """
        handlers = {
            ord("M"): self.handle_mfd_image_payload,
            ord("S"): self.handle_sar_image_payload,
            ord("B"): self.handle_binary_data_payload,
            ord("J"): self.handle_json_payload,
            # Aggiungi qui nuovi flussi e handler
        }
        logging.debug(
            f"{self._log_prefix} Providing handlers for flows: {[chr(k) if 32<=k<=126 else k for k in handlers.keys()]}"
        )
        return handlers

    # --- Handler Implementations ---

    def handle_mfd_image_payload(self, payload: bytearray):
        """Handle an MFD image payload.

        Default behaviour: log the arrival and size. Subclasses should
        override to extract image leader/frames and hand them to the
        display/decoder pipeline.
        """
        log_prefix = f"{self._log_prefix} MFD"
        logging.info(
            f"{log_prefix} Received payload. Size: {len(payload)} bytes. Processing..."
        )
        # Qui la logica per interpretare il payload dell'immagine MFD
        # Ad esempio, potresti usare ctypes per mappare una struttura dati:
        # from .sfp_structures import ImageLeaderData
        # image_leader = ImageLeaderData.from_buffer(payload)
        # logging.debug(f"{log_prefix} Frame counter: {image_leader.HEADER_DATA.FCOUNTER}")
        pass

    def handle_sar_image_payload(self, payload: bytearray):
        """Handle a SAR image payload.

        The base implementation logs receipt. Override to perform SAR-specific
        header parsing and decoding.
        """
        log_prefix = f"{self._log_prefix} SAR"
        logging.info(
            f"{log_prefix} Received payload. Size: {len(payload)} bytes. Processing..."
        )
        # Qui la logica per interpretare il payload dell'immagine SAR
        pass

    def handle_binary_data_payload(self, payload: bytearray):
        """Handle a generic binary payload.

        The example implementation demonstrates parsing a 2-byte message id
        and a 4-byte length header. Real handlers should validate sizes and
        endianness and handle malformed inputs gracefully.
        """
        log_prefix = f"{self._log_prefix} BIN"
        logging.info(f"{log_prefix} Received payload. Size: {len(payload)} bytes.")
        try:
            # Esempio: leggi un ID (2 byte), una lunghezza (4 byte) e dei dati
            if len(payload) >= 6:
                message_id = int.from_bytes(payload[0:2], "little")
                data_length = int.from_bytes(payload[2:6], "little")
                actual_data = payload[6 : 6 + data_length]
                logging.info(
                    f"{log_prefix} Parsed binary message: ID={message_id}, Length={data_length}, Data size={len(actual_data)}"
                )
            else:
                logging.warning(
                    f"{log_prefix} Payload too small for expected binary format."
                )
        except Exception as e:
            logging.error(f"{log_prefix} Error parsing binary payload: {e}")

    def handle_json_payload(self, payload: bytearray):
        """Handle a JSON-encoded payload.

        Decodes the payload as UTF-8 and attempts to parse JSON. On success
        the decoded dict can be used for further routing (e.g. by `type`).
        JSON decoding errors are logged and do not raise.
        """
        log_prefix = f"{self._log_prefix} JSON"
        logging.info(f"{log_prefix} Received payload. Size: {len(payload)} bytes.")
        try:
            json_string = payload.decode("utf-8")
            data = json.loads(json_string)
            message_type = data.get("type", "Unknown")
            logging.info(f"{log_prefix} Decoded JSON message of type '{message_type}'.")
            # Qui la logica per processare l'oggetto JSON
            # if message_type == 'status_update':
            #     handle_status_update(data)
        except (UnicodeDecodeError, json.JSONDecodeError) as e:
            logging.error(f"{log_prefix} Error decoding JSON payload: {e}")
