'''
Verify we can dump a `stackscope` tree on a hang.

'''
import os
import platform
import signal

import trio
import tractor

@tractor.context
async def start_n_shield_hang(
    ctx: tractor.Context,
):
    # actor: tractor.Actor = tractor.current_actor()

    # sync to parent-side task
    await ctx.started(os.getpid())

    print('Entering shield sleep..')
    with trio.CancelScope(shield=True):
        await trio.sleep_forever()  # in subactor

    # XXX NOTE ^^^ since this shields, we expect
    # the zombie reaper (aka T800) to engage on
    # SIGINT from the user and eventually hard-kill
    # this subprocess!


async def main(
    from_test: bool = False,
) -> None:

    if platform.system() != 'Darwin':
        tpt = 'uds'
    else:
        # XXX, precisely we can't use pytest's tmp-path generation
        # for tests.. apparently because:
        #
        # > The OSError: AF_UNIX path too long in macOS Python occurs
        # > because the path to the Unix domain socket exceeds the
        # > operating system's maximum path length limit (around 104
        #
        # WHICH IS just, wtf hillarious XD
        tpt = 'tcp'

    async with (
        tractor.open_nursery(
            debug_mode=True,
            enable_stack_on_sig=True,
            loglevel='devx',  # XXX REQUIRED log level!
            enable_transports=[tpt],
            # maybe_enable_greenback=True,
            # ^TODO? maybe a "smarter" way todo all this is how
            # `modden` does with a rtv serialized through the osenv?
        ) as an,
    ):
        ptl: tractor.Portal  = await an.start_actor(
            'hanger',
            enable_modules=[__name__],
            debug_mode=True,
        )
        async with ptl.open_context(
            start_n_shield_hang,
        ) as (ctx, cpid):

            _, proc, _ = an._children[
                ptl.chan.aid.uid
            ]
            assert cpid == proc.pid

            print(
                'Yo my child hanging..?\n'
                # "i'm a user who wants to see a `stackscope` tree!\n"
            )

            # XXX simulate the wrapping test's "user actions"
            # (i.e. if a human didn't run this manually but wants to
            # know what they should do to reproduce test behaviour)
            if from_test:
                print(
                    f'Sending SIGUSR1 to {cpid!r}!\n'
                )
                os.kill(
                    cpid,
                    signal.SIGUSR1,
                )

                # simulate user cancelling program
                await trio.sleep(0.5)
                os.kill(
                    os.getpid(),
                    signal.SIGINT,
                )
            else:
                # actually let user send the ctl-c
                await trio.sleep_forever()  # in root


if __name__ == '__main__':
    trio.run(main)
