from abc import abstractmethod
from logging import Logger
from pathlib import Path
from typing import Any, Final, Iterable, Iterator, Protocol, Sequence, TypeVar, overload

from numpy.typing import NDArray

_T = TypeVar("_T")


class ProgressHandler(Protocol):
    @overload
    def wrap(self, values: Sequence[_T]) -> Iterator[_T]: ...

    @overload
    def wrap(self, values: Iterable[_T], total: int) -> Iterator[_T]: ...


class FileHandler:
    @abstractmethod
    def create(self, filename: str, content: bytes, text: bool = False): ...

    def image(self, filename: str, image: NDArray[Any]):
        from io import BytesIO

        import imageio.v3 as iio

        io = BytesIO()
        iio.imwrite(io, image, extension=Path(filename).suffix)  # type: ignore
        io.seek(0)

        self.create(filename, io.read(), False)


class BaseSolver:
    def __init__(
        self,
        logger: Logger,
        verbose: bool,
        year: int,
        day: int,
        progress: ProgressHandler,
        files: FileHandler | None = None,
    ):
        self.logger: Final = logger
        self.verbose: Final = verbose
        self.year: Final = year
        self.day: Final = day
        self.progress: Final = progress
        self.files: Final = files

    @abstractmethod
    def solve(self, input: str) -> Iterator[Any] | None: ...