"""Helpers to save payload preview images (PIL or array-based) for VideoReceiverSFP.
This centralizes preview-saving logic so `sfp_module` can be slimmer.
"""
import os
import logging

try:
    import numpy as np
except Exception:
    np = None

try:
    from PIL import Image
except Exception:
    Image = None

logger = logging.getLogger("VideoReceiverSFP.payload_preview")


def save_payload_preview(module, leader, payload, ity, dbgdir, ts):
    """Save a preview image for the given payload using module's helpers.

    Args:
        module: the SfpConnectorModule instance (used for _decode_leader_payload and DumpManager)
        leader: the parsed ImageLeaderData structure
        payload: raw payload bytes
        ity: integer image type
        dbgdir: dumps directory path
        ts: timestamp string
    """
    try:
        preview_frame = None
        try:
            preview_frame = module._decode_leader_payload(leader, payload)
        except Exception:
            preview_frame = None

        # If we obtained a PIL Image, save via DumpManager or fallback
        if preview_frame is not None and Image is not None and hasattr(preview_frame, 'save'):
            base_name = 'mfd' if ity == 1 else ('sar' if ity == 2 else 'unknown')
            fmt = 'bmp'
            if getattr(module, '_dump_manager', None) is not None:
                try:
                    saved = module._dump_manager.save_preview_from_pil(preview_frame, category=base_name, fmt=fmt)
                    if saved:
                        logging.getLogger().info('VideoReceiverSFP: saved payload preview %s', saved)
                        return
                except Exception:
                    logger.exception('save_payload_preview: DumpManager save_preview_from_pil failed')
            try:
                ts_name = ts + (f"_{int(__import__('time').time() * 1000) % 1000:03d}")
                outpath = os.path.join(dbgdir, f'VideoReceiverSFP_{base_name}_{ts_name}.bmp')
                preview_frame.save(outpath)
                logging.getLogger().info('VideoReceiverSFP: saved payload preview %s', outpath)
                return
            except Exception:
                logger.exception('save_payload_preview: failed saving preview (fallback)')

        # Fallback: array-based approach
        try:
            hd = leader.HEADER_DATA
            dx = int(hd.DX)
            dy = int(hd.DY)
            bpp = int(hd.BPP)
            stride_pixels = int(hd.STRIDE) if int(hd.STRIDE) > 0 else dx
            bytes_per_pixel = 1 if bpp <= 8 else (bpp // 8)

            try:
                pixel_offset = (
                    leader.HEADER_TAG.size()
                    + leader.HEADER_DATA.size()
                    + leader.GEO_TAG.size()
                    + leader.GEO_DATA.size()
                    + leader.RESERVED_TAG.size()
                    + getattr(leader, 'get_reserved_data_size', lambda: 0)()
                    + leader.CM_TAG.size()
                    + getattr(leader, 'get_colour_map_size', lambda: 0)()
                    + leader.PIXEL_TAG.size()
                )
            except Exception:
                try:
                    # as last resort, use ImageLeaderData.size() if available on module
                    from .sfp_module import ImageLeaderData as _ILD
                    pixel_offset = _ILD.size()
                except Exception:
                    pixel_offset = 0

            row_stride_bytes = stride_pixels * bytes_per_pixel
            total_bytes = row_stride_bytes * dy
            pixel_data = payload[pixel_offset:pixel_offset + total_bytes]

            if len(pixel_data) >= total_bytes and np is not None:
                if bytes_per_pixel == 2:
                    arr = np.frombuffer(pixel_data[:total_bytes], dtype=np.uint16)
                    arr = arr.reshape((dy, stride_pixels))[:, :dx]
                    try:
                        norm = module._normalize_and_convert(arr) if hasattr(module, '_normalize_and_convert') else None
                    except Exception:
                        norm = None
                    if norm is None:
                        norm = (arr >> 8).astype(np.uint8)
                else:
                    arr = np.frombuffer(pixel_data[:total_bytes], dtype=np.uint8)
                    arr = arr.reshape((dy, stride_pixels))[:, :dx]
                    try:
                        norm = module._normalize_and_convert(arr) if hasattr(module, '_normalize_and_convert') else None
                    except Exception:
                        norm = None
                    if norm is None:
                        norm = arr.astype(np.uint8)

                try:
                    base_name = 'mfd' if ity == 1 else ('sar' if ity == 2 else 'unknown')
                    fmt = 'bmp'
                    if getattr(module, '_dump_manager', None) is not None:
                        try:
                            # Convert numpy array to PIL Image and use existing DumpManager API
                            from PIL import Image as _PILImage
                            pil_img = _PILImage.fromarray(norm, mode='L')
                            saved = module._dump_manager.save_preview_from_pil(pil_img, category=base_name, fmt=fmt)
                            if saved:
                                logging.getLogger().info('VideoReceiverSFP: saved payload preview %s', saved)
                                return
                        except Exception:
                            logger.exception('save_payload_preview: DumpManager save_preview_from_pil failed for array input')
                    else:
                        from PIL import Image as _PILImage
                        ts_name = ts + (f"_{int(__import__('time').time() * 1000) % 1000:03d}")
                        outpath = os.path.join(dbgdir, f'VideoReceiverSFP_{base_name}_{ts_name}.bmp')
                        pil_img = _PILImage.fromarray(norm, mode='L')
                        pil_img.save(outpath)
                        logging.getLogger().info('VideoReceiverSFP: saved payload preview %s', outpath)
                        return
                except Exception:
                    logger.exception('save_payload_preview: failed saving payload preview (array fallback)')
        except Exception:
            logger.exception('save_payload_preview: error creating payload preview (array fallback)')

    except Exception:
        logger.exception('save_payload_preview: unexpected error')
