#
# Take a look at the example
# https://docs.pytest.org/en/stable/example/nonpython.html
#
import pytest

from .main import extract_from_file, stdout_io


def pytest_addoption(parser):
    group = parser.getgroup("general")
    group.addoption(
        "--codeblocks", action="store_true", help="enable testing of codeblocks"
    )


def pytest_collect_file(path, parent):
    config = parent.config
    if config.option.codeblocks and path.ext == ".md":
        return MarkdownFile.from_parent(parent, fspath=path)


class MarkdownFile(pytest.File):
    def __init__(self, fspath, parent):
        super().__init__(fspath, parent)

    def collect(self):
        for block in extract_from_file(self.fspath):
            if block.syntax != "python":
                continue
            # https://docs.pytest.org/en/stable/deprecations.html#node-construction-changed-to-node-from-parent
            out = Codeblock.from_parent(parent=self, name=self.name)
            out.obj = block
            yield out


class Codeblock(pytest.Item):
    def __init__(self, name, parent, obj=None):
        super().__init__(name, parent=parent)
        self.obj = obj

    def runtest(self):
        if self.obj.expect_exception:
            with pytest.raises(Exception):
                exec(self.obj.code, {"__MODULE__": "__main__"})
        else:
            with stdout_io() as s:
                try:
                    # https://stackoverflow.com/a/62851176/353337
                    exec(self.obj.code, {"__MODULE__": "__main__"})
                except Exception as e:
                    raise RuntimeError(
                        f"{self.name}, line {self.obj.lineno}:\n```\n"
                        + self.obj.code
                        + "```\n\n"
                        + f"{e}"
                    )

            output = s.getvalue()
            if self.obj.expected_output is not None:
                if self.obj.expected_output != output:
                    raise RuntimeError(
                        f"{self.name}, line {self.obj.lineno}:\n```\n"
                        + f"Expected output\n```\n{self.obj.expected_output}```\n"
                        + f"but got\n```\n{output}```"
                    )

    def repr_failure(self, excinfo):
        """Called when self.runtest() raises an exception."""
        # if isinstance(excinfo.value, CodeblockException):
        return excinfo.value.args[0]
        # if excinfo.errisinstance(RuntimeError):
        #     return excinfo.value.args[0].stdout
        # return super().repr_failure(excinfo)

    def reportinfo(self):
        return (self.fspath, -1, "code block check")
