import struct
from .writer import (
    REC_HEADER_FMT, REC_HEADER_SIZE, CRC_SIZE,
    FILE_HEADER_SIZE, STATUS_LIVE, STATUS_DEAD, crc32,
)


class ParseError(Exception):
    pass


def parse_record_at(mm, offset: int) -> dict:
    """
    Parse a single record from an mmap object at the given byte offset.

    Returns a dict:
        status     : int   (STATUS_LIVE or STATUS_DEAD)
        key        : bytes
        value      : bytes
        key_len    : int
        val_len    : int
        total_size : int   (full byte span of this record)
        valid      : bool  (False = CRC mismatch or truncated)
    """
    size = len(mm)

    if offset + REC_HEADER_SIZE + CRC_SIZE > size:
        raise ParseError(f"Truncated record header at offset {offset}")

    status, key_len, val_len = struct.unpack_from(REC_HEADER_FMT, mm, offset)
    total = REC_HEADER_SIZE + key_len + val_len + CRC_SIZE

    if offset + total > size:
        raise ParseError(f"Incomplete record body at offset {offset}")

    payload     = bytes(mm[offset : offset + REC_HEADER_SIZE + key_len + val_len])
    crc_offset  = offset + REC_HEADER_SIZE + key_len + val_len
    stored_crc  = struct.unpack_from('>I', mm, crc_offset)[0]
    valid       = crc32(payload) == stored_crc

    key   = payload[REC_HEADER_SIZE : REC_HEADER_SIZE + key_len]
    value = payload[REC_HEADER_SIZE + key_len :]

    return {
        'status'    : status,
        'key'       : key,
        'value'     : value,
        'key_len'   : key_len,
        'val_len'   : val_len,
        'total_size': total,
        'valid'     : valid,
    }


def scan_all(mm) -> list:
    """
    Linear scan of the entire mmap from FILE_HEADER_SIZE onward.
    Yields (offset, record_dict) for every parseable record, live or dead.
    Stops on the first unrecoverable parse error.
    """
    results = []
    offset  = FILE_HEADER_SIZE
    size    = len(mm)

    while offset < size:
        try:
            rec = parse_record_at(mm, offset)
            results.append((offset, rec))
            offset += rec['total_size']
        except ParseError:
            break   # truncated tail — stop cleanly

    return results