# fmt: off

import sys
import builtins

from contextlib import contextmanager
from collections.abc import Callable
from typing import NamedTuple, TextIO

__all__ = [
    'SystemHooks',
]

################################################################################

type PrintFn = Callable[..., None]

class SystemHooks(NamedTuple):
    """
    Represents the changes of global Python state that must be made during
    a REPL execution.

    Make a desired state with `state = SystemHooks.make(...)`.

    Temporarily apply it with `with state.applied(): ...`

    Permanently apply it with `state.set()`.

    Get the current system state with `SystemHooks.get()`.
    """

    stdin:           TextIO
    stdout:          TextIO
    stderr:          TextIO
    print_fn:        PrintFn
    recursion_limit: int

    @staticmethod
    def make(
            stdin: TextIO,
            stdout: TextIO,
            stderr: TextIO, *,
            print_fn: PrintFn = builtins.print,
            recursion_limit: int = 0) -> 'SystemHooks':

        return SystemHooks(
            stdin, stdout, stderr,
            print_fn,
            recursion_limit,
        )

    def set(self):
        sys.stdin, sys.stdout, sys.stderr, builtins.print, recursion_limit = self
        sys.setrecursionlimit(recursion_limit) if recursion_limit else None

    @staticmethod
    def get() -> 'SystemHooks':
        rec_limit = sys.getrecursionlimit()
        return SystemHooks(
            sys.stdin, sys.stdout, sys.stderr,
            builtins.print,
            rec_limit,
        )

    @contextmanager
    def applied(self):
        old = SystemHooks.get()
        self.set()
        yield
        old.set()
