from os import environ
import platform
import subprocess
from datetime import datetime
import pytest


@pytest.fixture()
def silence_node_deprecation_warnings():
    """Ensures @jsii/check-node does not emit any warning that would cause the
    test output assertions to be invalidated.
    """

    variables = [
        "JSII_SILENCE_WARNING_KNOWN_BROKEN_NODE_VERSION",
        "JSII_SILENCE_WARNING_UNTESTED_NODE_VERSION",
        "JSII_SILENCE_WARNING_DEPRECATED_NODE_VERSION",
        "JSII_SILENCE_WARNING_END_OF_LIFE_NODE_VERSION",
    ]

    store = {var: environ.get(var, "") for var in variables}

    for var in variables:
        environ[var] = "1"

    nodeEolAcknowledgement = "Node14 is now end of life (EOL) as of April 30, 2023. Support of EOL runtimes are only guaranteed for 30 days after EOL. By silencing this warning you acknowledge that you are using an EOL version of Node and will upgrade to a supported version as soon as possible."
    environ["JSII_SILENCE_WARNING_END_OF_LIFE_NODE_VERSION"] = nodeEolAcknowledgement

    # Execute the test
    yield

    for var in variables:
        environ[var] = store[var]


class TestInvokeBinScript:
    @pytest.mark.skipif(
        platform.system() == "Windows",
        reason="jsii-pacmak does not generate windows scripts",
    )
    def test_invoke_script(self, silence_node_deprecation_warnings) -> None:
        script_path = f".env/bin/calc"
        result = subprocess.run([script_path], capture_output=True)

        assert result.returncode == 0
        assert result.stdout == b"Hello World!\n"

    @pytest.mark.skipif(
        platform.system() == "Windows",
        reason="jsii-pacmak does not generate windows scripts",
    )
    def test_invoke_script_with_args(self, silence_node_deprecation_warnings) -> None:
        script_path = f".env/bin/calc"
        result = subprocess.run([script_path, "arg1", "arg2"], capture_output=True)

        assert result.returncode == 0
        assert result.stdout == b"Hello World!\n  arguments: arg1, arg2\n"

    @pytest.mark.skipif(
        platform.system() == "Windows",
        reason="jsii-pacmak does not generate windows scripts",
    )
    def test_invoke_script_with_failure(
        self, silence_node_deprecation_warnings
    ) -> None:
        script_path = f".env/bin/calc"
        result = subprocess.run([script_path, "arg1", "fail"], capture_output=True)

        assert result.returncode == 3
        assert result.stdout == b"Hello World!\n  arguments: arg1, fail\n"
        assert result.stderr.startswith(b"error message to stderr\n")

    @pytest.mark.skipif(
        platform.system() == "Windows",
        reason="jsii-pacmak does not generate windows scripts",
    )
    def test_invoke_script_with_line_flush(
        self, silence_node_deprecation_warnings
    ) -> None:
        """Make sure lines are flushed immediately as they are generated, rather than
        buffered to the end
        """
        script_path = f".env/bin/calc"
        proc = subprocess.Popen(
            [script_path, "arg1", "delay"], stdout=subprocess.PIPE, bufsize=1
        )

        timed_lines = []
        last_dt = None
        if proc.stdout is not None:
            for line in iter(proc.stdout.readline, b""):
                last_dt = datetime.utcnow() if last_dt is None else last_dt
                current_dt = datetime.utcnow()

                delay_s = round((current_dt - last_dt).total_seconds())
                timed_lines.append([line, delay_s])

                last_dt = current_dt
        exit_code = proc.wait()

        assert exit_code == 0
        assert timed_lines == [
            [b"Hello World!\n", 0],
            [b"  arguments: arg1, delay\n", 0],
            [b"sleeping 1s 1\n", 0],
            [b"sleeping 1s 2\n", 1],
            [b"sleeping 1s 3\n", 1],
            [b"sleeping 1s 4\n", 1],
            [b"sleeping 1s 5\n", 1],
        ]
