"""Base class for whmapi/uapi objects"""
from typing import Union
import urllib.parse
import os
import json
from subprocess import run, CalledProcessError, TimeoutExpired
from ._errors import CpAPIExecFail


class CpAPIBase:
    """Base class for whmapi/uapi objects"""

    def __init__(self, path: str):
        self.path = path

    __module__ = 'cpapis'

    @staticmethod
    def _can_exec(path: str):
        """Tests if the current user can exec a script at ``path``"""
        return os.path.isfile(path) and os.access(path, os.X_OK)

    def _exec(
        self,
        *,
        module_args: list[str],
        user: Union[str, None] = None,
        args: Union[dict, None] = None,
        timeout: Union[float, None] = None,
    ) -> dict:
        """uapi/cpapi2/whmapi1 run in similar ways; this executes them

        Raises:
            CpAPIExecFail: command failed to execute or returned invalid json"""
        cmd = [self.path, '--output=json']
        if user:
            cmd.append(f'--user={user}')
        cmd.extend(module_args)
        if args:
            cmd.extend(self._quote_key_vals(args))
        try:
            ret = run(
                cmd,
                capture_output=True,
                encoding='utf-8',
                timeout=timeout,
                check=True,
            )
        except (TimeoutExpired, CalledProcessError) as exc:
            raise CpAPIExecFail(
                msg=f"{type(exc).__name__}: {str(exc)}",
                cmd=cmd,
                stdout=exc.stdout,
                stderr=exc.stderr,
                returncode=exc.returncode,
            ) from exc
        except OSError as exc:
            raise CpAPIExecFail(
                msg=f"{type(exc).__name__}: {str(exc)}",
                cmd=cmd,
                stdout='',
                stderr=str(exc),
                returncode=127,
            ) from exc
        try:
            return json.loads(ret.stdout)
        except ValueError as exc:
            raise CpAPIExecFail(
                msg=f'Invalid JSON returned from {cmd}',
                cmd=cmd,
                stdout=ret.stdout,
                stderr=ret.stderr,
                returncode=ret.returncode,
            ) from exc

    @staticmethod
    def _quote_key_vals(args: dict):
        """cPanel APIs accessed via CLI send key-values in a special
        URI escaped format."""
        quoted = []
        for key, val in args.items():
            val = str(val)
            if val.startswith('@'):
                # values starting with @ are files and are not uri escaped
                quoted.append(f"{urllib.parse.quote(key)}={val}")
            else:
                quoted.append(
                    f"{urllib.parse.quote(key)}={urllib.parse.quote(val)}"
                )
        return quoted
