# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import os
import sys

import mozpack.path as mozpath

from mozperftest.layers import Layer
from mozperftest.utils import silence


class NodeRunner(Layer):
    name = "node"

    def __init__(self, env, mach_cmd):
        super(NodeRunner, self).__init__(env, mach_cmd)
        self.topsrcdir = mach_cmd.topsrcdir
        self._mach_context = mach_cmd._mach_context
        self.python_path = mach_cmd.virtualenv_manager.python_path

        from mozbuild.nodeutil import find_node_executable

        self.node_path = os.path.abspath(find_node_executable()[0])

    def setup(self):
        """Install the Node.js package."""
        self.verify_node_install()

    def node(self, args, line_handler=None):
        """Invoke node (interactively) with the given arguments."""
        return self.run_process(
            [self.node_path] + args,
            append_env=self.append_env(),
            pass_thru=False,  # Allow user to run Node interactively.
            ensure_exit_code=False,  # Don't throw on non-zero exit code.
            cwd=mozpath.join(self.topsrcdir),
            line_handler=line_handler,
        )

    def append_env(self, append_path=True):
        # Ensure that bare `node` and `npm` in scripts, including post-install
        # scripts, finds the binary we're invoking with.  Without this, it's
        # easy for compiled extensions to get mismatched versions of the Node.js
        # extension API.
        path = os.environ.get("PATH", "").split(os.pathsep) if append_path else []
        node_dir = os.path.dirname(self.node_path)
        path = [node_dir] + path

        return {
            "PATH": os.pathsep.join(path),
            # Bug 1560193: The JS library browsertime uses to execute commands
            # (execa) will muck up the PATH variable and put the directory that
            # node is in first in path. If this is globally-installed node,
            # that means `/usr/bin` will be inserted first which means that we
            # will get `/usr/bin/python` for `python`.
            #
            # Our fork of browsertime supports a `PYTHON` environment variable
            # that points to the exact python executable to use.
            "PYTHON": self.python_path,
        }

    def verify_node_install(self):
        # check if Node is installed
        sys.path.append(mozpath.join(self.topsrcdir, "tools", "lint", "eslint"))
        import setup_helper

        with silence():
            node_valid = setup_helper.check_node_executables_valid()
        if not node_valid:
            # running again to get details printed out
            setup_helper.check_node_executables_valid()
            raise ValueError("Can't find Node. did you run ./mach bootstrap ?")

        return True
