from __future__ import annotations

import logging
import os
import signal
import sys
from io import TextIOWrapper
from pathlib import Path
from types import FrameType
from unittest.mock import MagicMock

from tox.execute import Outcome
from tox.execute.local_sub_process import LocalSubProcessExecutor
from tox.execute.request import ExecuteRequest, StdinSource
from tox.report import NamedBytesIO

logging.basicConfig(level=logging.DEBUG, format="%(relativeCreated)d\t%(levelname).1s\t%(message)s")
bad_process = Path(__file__).parent / "bad_process.py"

executor = LocalSubProcessExecutor(colored=False)
request = ExecuteRequest(
    cmd=[sys.executable, bad_process, sys.argv[1]],
    cwd=Path().absolute(),
    env=os.environ.copy(),
    stdin=StdinSource.API,
    run_id="",
)
out_err = TextIOWrapper(NamedBytesIO("out")), TextIOWrapper(NamedBytesIO("err"))


def show_outcome(outcome: Outcome | None) -> None:
    if outcome is not None:  # pragma: no branch
        print(outcome.exit_code)
        print(repr(outcome.out))
        print(repr(outcome.err))
        print(outcome.elapsed, end="")
        print("done show outcome", file=sys.stderr)


def handler(s: int, f: FrameType | None) -> None:
    logging.info(f"signal {s} at {f}")
    global interrupt_done
    if interrupt_done is False:  # pragma: no branch
        interrupt_done = True
        logging.info(f"interrupt via {status}")
        status.interrupt()
        logging.info(f"interrupt finished via {status}")


interrupt_done = False
signal.signal(signal.SIGINT, handler)
logging.info("PID %d start %r", os.getpid(), request)
tox_env = MagicMock(conf={"suicide_timeout": 0.01, "interrupt_timeout": 0.05, "terminate_timeout": 0.07})
try:
    with executor.call(request, show=False, out_err=out_err, env=tox_env) as status:
        logging.info("wait on %r", status)
        while status.exit_code is None:
            status.wait(timeout=0.01)  # use wait here with timeout to not block the main thread
        logging.info("wait over on %r", status)
    show_outcome(status.outcome)
except Exception as exception:  # pragma: no cover
    logging.exception(exception)  # pragma: no cover
finally:
    logging.info("done")
    logging.shutdown()
