from parallels.core.logging import get_logger
from parallels.core.runners.exceptions.non_zero_exit_code import NonZeroExitCodeException
from parallels.plesk import messages

logger = get_logger(__name__)


class BaseCli(object):
    def __init__(self, runner, tracer=None):
        """Basic construct of Plesk commands executed via CLI

        :type runner: parallels.core.utils.plesk_cli_runner.PleskCLIRunnerBase
        :type tracer: parallels.core.utils.tracer.Tracer | None
        """
        self._runner = runner
        self._tracer = tracer

    def set_tracer(self, tracer):
        self._tracer = tracer

    @property
    def name(self):
        raise NotImplementedError()

    @property
    def is_private(self):
        """Flag, should be True if this command line utility is a private Plesk utility located in admin/bin

        :rtype: bool
        """
        return False

    def run(self, is_unchecked=False):
        """Execute Plesk CLI

        :type is_unchecked: bool
        :rtype: int, str, str | str
        """
        try:
            args = self._get_args()
            if self._is_useless():
                logger.fdebug(
                    messages.HOSTING_REPOSITORY_CLI_USELESS,
                    name=self.name, args=args
                )
                return 0, '', '' if is_unchecked else ''
            if len(args) > 0:
                args.append('-ignore-nonexistent-options')
            stdin_content = self._get_stdin_content()
            env = self._get_env()
            if is_unchecked:
                return self._run_unchecked(args, stdin_content, env)
            else:
                return self._run(args, stdin_content, env)
        except Exception as e:
            self._process_exception(e)

    def _run(self, args, stdin_content, env):
        exit_code = 0
        stdout = None
        stderr = None
        try:
            stdout = self._runner.run(
                self.name,
                args,
                stdin_content=stdin_content,
                env=env,
                is_private=self.is_private
            )
        except NonZeroExitCodeException as e:
            exit_code = e.exit_code
            stdout = e.stdout
            stderr = e.stderr
            raise
        except Exception:
            exit_code = 1
            raise
        finally:
            if self._tracer is not None:
                self._tracer.trace_run_plesk_cli(
                    exit_code=exit_code, stdout=stdout, stderr=stderr,
                    utility_name=self.name, args=args, stdin_content=stdin_content, env=env,
                )
        return stdout

    def _run_unchecked(self, args, stdin_content, env):
        exit_code, stdout, stderr = self._runner.run_unchecked(
            self.name,
            args,
            stdin_content=stdin_content,
            env=env,
            is_private=self.is_private
        )
        if self._tracer is not None:
            self._tracer.trace_run_plesk_cli(
                exit_code=exit_code, stdout=stdout, stderr=stderr,
                utility_name=self.name, args=args, stdin_content=stdin_content, env=env,
            )
        return exit_code, stdout, stderr

    def _get_args(self):
        return []

    def _get_stdin_content(self):
        return None

    def _get_env(self):
        return {}

    def _is_useless(self):
        return False

    def _process_exception(self, e):
        raise
