#
# This file was generated from the asynchronous client at streams/os.py by generate_synchronous_client.sh
# Do not edit this file directly, instead edit the original file and regenerate this file.
#


"""Low-level compatibility layer for file operations."""

import base64
import hashlib
import os
from pathlib import Path
from typing import Literal, overload

from .base import InputStream, OutputStream


class FileInputStream(InputStream):
    def seek(self, offset: int, whence: int = os.SEEK_SET) -> None:
        """Change the stream position."""
        ...


class FileOutputStream(OutputStream):
    def seek(self, offset: int, whence: int = os.SEEK_SET) -> None:
        """Change the stream position."""
        ...

    def truncate(self, size: int) -> None: ...


@overload
def open_file(_fpath: Path, mode: Literal["rb"]) -> FileInputStream: ...


@overload
def open_file(
    _fpath: Path, mode: Literal["wb"] | Literal["r+b"]
) -> FileOutputStream: ...


def open_file(
    _fpath: Path, mode: Literal["rb"] | Literal["wb"] | Literal["r+b"]
) -> FileInputStream | FileOutputStream:
    """Open a file for reading or writing."""
    r: FileInputStream | FileOutputStream = open(_fpath, mode=mode)  # noqa # type: ignore
    return r


def file_stat(_fpath: Path) -> os.stat_result:
    """Get file statistics."""
    return os.stat(_fpath)


def checksum_file(
    file_name: Path, algo: str = "md5", offset: int = 0, count: int | None = None
) -> str:
    """Calculate the checksum of the file."""
    # cpu-bound operation - run in a separate thread synchronously
    with open(file_name, mode="rb") as f:
        if offset > 0:
            f.seek(offset)

        hasher = hashlib.new(algo)
        chunk_size = get_filesystem_block_size(file_name) * 16

        while count != 0:
            chunk = f.read(min(chunk_size, count or chunk_size))
            if not chunk:
                break
            hasher.update(chunk)
            if count is not None:
                count -= len(chunk)

        return base64.b64encode(hasher.digest()).decode("ascii")


def get_filesystem_block_size(file_path: Path) -> int:
    if hasattr(os, "statvfs"):  # Unix-like systems
        statvfs = os.statvfs(str(file_path))
        return statvfs.f_bsize
    else:
        return 4096  # default block size for Windows


__all__ = (
    "open_file",
    "file_stat",
    "FileInputStream",
    "FileOutputStream",
    "checksum_file",
)
