"""Offline .bin-file backend — the default, hardware-free path.

Everything except the final program-to-adapter step happens here: load a base
image, edit fields, preview, save. Writes to a file are still "writes" but they
are NOT physical, so they are not gated behind hardware escalation (the CLI
distinguishes physical from file writes via `backend.physical`). File overwrites
are protected by `mesh backup` in the CLI, not here.
"""
from __future__ import annotations

import os

from .base import EepromBackend, BackendError
from ..eeprom import MAX_EEPROM_SIZE, SIZES


class BinFileBackend(EepromBackend):
    physical = False
    name = "binfile"

    def __init__(self, path: str, size: int = MAX_EEPROM_SIZE):
        self.path = path
        self.size = size

    def read_image(self) -> bytes:
        if not os.path.exists(self.path):
            raise BackendError(f"no such .bin file: {self.path}")
        with open(self.path, "rb") as fh:
            data = fh.read()
        if len(data) not in SIZES:
            # not fatal — the image model tolerates/pads — but surface it
            pass
        return data

    def write_bytes(self, offset: int, values: bytes) -> None:
        # Read-modify-write the file image.
        if os.path.exists(self.path):
            with open(self.path, "rb") as fh:
                buf = bytearray(fh.read())
        else:
            buf = bytearray(self.size)
        if len(buf) < offset + len(values):
            buf.extend(bytearray(offset + len(values) - len(buf)))
        buf[offset:offset + len(values)] = values
        with open(self.path, "wb") as fh:
            fh.write(buf)

    def write_image(self, data: bytes) -> None:
        with open(self.path, "wb") as fh:
            fh.write(data)
