from commandlib import run, Command
import hitchpython
from hitchstory import StoryCollection, StorySchema, BaseEngine, exceptions
from hitchrun import expected
from strictyaml import Str, Map, Optional
from pathquery import pathq
import hitchtest
import hitchdoc
from commandlib import python
from hitchrun import hitch_maintenance
from hitchrun import DIR
from hitchrunpy import ExamplePythonCode


class Engine(BaseEngine):
    """Python engine for running tests."""

    schema = StorySchema(
        preconditions=Map({
            Optional("setup"): Str(),
            "code": Str(),
        }),
    )

    def __init__(self, paths, settings):
        self.path = paths
        self.settings = settings

    def set_up(self):
        """Set up your applications and the test environment."""
        self.path.state = self.path.gen.joinpath("state")
        self.path.working_dir = self.path.gen.joinpath("working")

        self.doc = hitchdoc.Recorder(
            hitchdoc.HitchStory(self),
            self.path.gen.joinpath('storydb.sqlite'),
        )

        if self.path.state.exists():
            self.path.state.rmtree(ignore_errors=True)
        self.path.state.mkdir()

        if self.path.working_dir.exists():
            self.path.working_dir.rmtree(ignore_errors=True)
        self.path.working_dir.mkdir()

        self.python_package = hitchpython.PythonPackage(
            self.preconditions.get('python_version', '3.5.0')
        )
        self.python_package.build()

        self.pip = self.python_package.cmd.pip
        self.python = self.python_package.cmd.python

        # Install debugging packages
        with hitchtest.monitor([self.path.key.joinpath("debugrequirements.txt")]) as changed:
            if changed:
                run(self.pip("install", "-r", "debugrequirements.txt").in_dir(self.path.key))

        # Uninstall and reinstall
        with hitchtest.monitor(pathq(self.path.project.joinpath("hitchrunpy"))) as changed:
            if changed:
                self.pip("uninstall", "hitchrunpy", "-y").ignore_errors().run()
                self.pip("install", ".").in_dir(self.path.project).run()

        self.example_python_code = ExamplePythonCode(
            self.preconditions.get('code', '')
        ).with_setup_code(
            self.preconditions.get('setup').replace("/path/to/working_dir", self.path.working_dir)
        )

    def run_code(self):
        self.example_python_code.run(self.path.state, self.python)

    def raises_exception(self, message=None, exception_type=None):
        result = self.example_python_code.expect_exceptions().run(self.path.state, self.python)
        result.exception_was_raised(exception_type, message.strip())

    def file_contains(self, filename, contents):
        assert self.path.working_dir.joinpath(filename).bytes().decode('utf8') == contents

    def pause(self, message="Pause"):
        import IPython
        IPython.embed()


@expected(exceptions.HitchStoryException)
def tdd(*words):
    """
    Run test with words.
    """
    print(
        StoryCollection(
            pathq(DIR.key).ext("story"), Engine(DIR, {"overwrite artefacts": True})
        ).shortcut(*words).play().report()
    )


@expected(exceptions.HitchStoryException)
def testfile(filename):
    """
    Run all stories in filename 'filename'.
    """
    print(
        StoryCollection(
            pathq(DIR.key).ext("story"), Engine(DIR, {"overwrite artefacts": True})
        ).in_filename(filename).ordered_by_name().play().report()
    )


def regression():
    """
    Regression test - run all tests and linter.
    """
    lint()
    print(
        StoryCollection(
            pathq(DIR.key).ext("story"), Engine(DIR, {})
        ).ordered_by_name().play().report()
    )


def lint():
    """
    Lint all code.
    """
    python("-m", "flake8")(
        DIR.project.joinpath("hitchrunpy"),
        "--max-line-length=100",
        "--exclude=__init__.py",
    ).run()
    python("-m", "flake8")(
        DIR.key.joinpath("key.py"),
        "--max-line-length=100",
        "--exclude=__init__.py",
    ).run()
    print("Lint success!")


def hitch(*args):
    """
    Use 'h hitch --help' to get help on these commands.
    """
    hitch_maintenance(*args)


def deploy(version):
    """
    Deploy to pypi as specified version.
    """
    NAME = "hitchrunpy"
    git = Command("git").in_dir(DIR.project)
    version_file = DIR.project.joinpath("VERSION")
    old_version = version_file.bytes().decode('utf8')
    if version_file.bytes().decode("utf8") != version:
        DIR.project.joinpath("VERSION").write_text(version)
        git("add", "VERSION").run()
        git("commit", "-m", "RELEASE: Version {0} -> {1}".format(
            old_version,
            version
        )).run()
        git("push").run()
        git("tag", "-a", version, "-m", "Version {0}".format(version)).run()
        git("push", "origin", version).run()
    else:
        git("push").run()

    # Set __version__ variable in __init__.py, build sdist and put it back
    initpy = DIR.project.joinpath(NAME, "__init__.py")
    original_initpy_contents = initpy.bytes().decode('utf8')
    initpy.write_text(
        original_initpy_contents.replace("DEVELOPMENT_VERSION", version)
    )
    python("setup.py", "sdist").in_dir(DIR.project).run()
    initpy.write_text(original_initpy_contents)

    # Upload to pypi
    python(
        "-m", "twine", "upload", "dist/{0}-{1}.tar.gz".format(NAME, version)
    ).in_dir(DIR.project).run()


def docgen():
    """
    Generate documentation.
    """
    docpath = DIR.project.joinpath("docs")

    if not docpath.exists():
        docpath.mkdir()

    documentation = hitchdoc.Documentation(
        DIR.gen.joinpath('storydb.sqlite'),
        'doctemplates.yml'
    )

    for story in documentation.stories:
        story.write(
            "rst",
            docpath.joinpath("{0}.rst".format(story.slug))
        )

@@ -391,16 +391,54 @@
 thonCode
+, ExpectedExceptionMessageWasDifferent
 %0A%0A%0Aclass
@@ -2577,24 +2577,41 @@
 type=None):%0A
+        try:%0A    
         resu
@@ -2698,24 +2698,28 @@
 on)%0A        
+    
 result.excep
@@ -2766,16 +2766,234 @@
 strip())
+%0A        except ExpectedExceptionMessageWasDifferent as error:%0A            if self.settings.get(%22rewrite%22):%0A                self.current_step.update(message=error.actual_message)%0A            else:%0A                raise
 %0A%0A    de
@@ -3405,35 +3405,23 @@
 (DIR, %7B%22
-ove
 r
+e
 write
- artefacts
 %22: True%7D
@@ -3694,27 +3694,15 @@
 , %7B%22
-ove
 r
+e
 write
- artefacts
 %22: T

REFACTOR : Added story rewriting to the test engine.