import hashlib
from pathlib import Path
from typing import Union

from clarabot_crypto.context.account_signature import AccountSignatureConfigLoadArgs, ACCOUNT_SIGNATURE_CRYPTO_CONTEXT
from clarabot_crypto.keyring.account_base import AbstractPeerAccountKeyring
from clarabot_nano_common.common.protocol.nano_request import NanoResponseError
from clarabot_nano_common.utility.serialize import msgpack_loads


def write_buffered_fs_get_file(
    path: Union[str, Path],  # path where "wrote" number of bytes have been written to
    resp: dict,  # next FS-GET response payload
    buf: list[bytes],  # file-chunks currently being staged for verification
    wrote: int,  # number of bytes we've already written (and verified prior)
    nano_kr: AbstractPeerAccountKeyring,
) -> int:  # return the amount of new bytes written
    """
    MAC-buffered incremental file verify and writing utility.
    """
    try:
        if "ls" in resp:  # handle "old" directory listing as error
            raise NanoResponseError("Expected to get file-data for write, but got directory listing")
        elif "fs" in resp:  # file-size only in initial part
            with open(path, "ab+") as f:
                pos = f.tell()
                if pos != wrote:
                    raise NanoResponseError("Failed to continue writing file (initial)")
        else:
            buf.append(resp["fc"])
            if isinstance(mac := resp.get("MAC"), bytes):
                if not isinstance(ds := resp.get("MACS"), bytes):
                    raise NanoResponseError("FS-GET response is missing MAC-signature")
                try:
                    ACCOUNT_SIGNATURE_CRYPTO_CONTEXT.load(AccountSignatureConfigLoadArgs(ds, nano_kr, mac))
                except Exception:
                    raise NanoResponseError("Failed to verify FS-GET response MAC")
                mac_data = msgpack_loads(mac)
                if sum(len(d) for d in buf) != mac_data["c"]:
                    raise NanoResponseError("FS-GET MAC count does not match buffer size")
                h = hashlib.sha512(mac_data["s"])
                for d in buf:
                    h.update(d)
                if mac_data["f"] != h.digest()[:32]:
                    raise NanoResponseError("Failed to verify FS-GET response MAC digest")
                with open(path, "ab+") as f:
                    pos = f.tell()
                    if pos != wrote:
                        raise NanoResponseError("Failed to continue writing file")
                    for d in buf:
                        f.write(d)
                buf.clear()
                return mac_data["c"]
    except NanoResponseError:
        raise
    except OSError:
        raise NanoResponseError("Failed to write chunks of FS-GET")
    except Exception:
        raise NanoResponseError("Failed to process FS-GET response MAC")
    return 0
