import six
from boltons.typeutils import classproperty
from typing import Tuple

from .apierror import APIError


class BaseError(APIError):
    _default_code = 500
    _default_subcode = 0
    _default_msg = ""

    def __init__(self, extra_msg=None, replacement_msg=None, **kwargs):
        message = replacement_msg or self._default_msg
        if extra_msg:
            message += f" ({extra_msg})"
        if kwargs:
            kwargs_msg = ", ".join(
                f"{k}={self._format_kwarg(v)}" for k, v in kwargs.items()
            )
            message += f": {kwargs_msg}"
        params = kwargs.copy()
        params.update(
            code=self._default_code, subcode=self._default_subcode, msg=message
        )
        super(BaseError, self).__init__(**params)

    @staticmethod
    def _format_kwarg(value):
        if isinstance(value, (tuple, list)):
            return f'({", ".join(str(v) for v in value)})'
        elif isinstance(value, six.string_types):
            return value
        return str(value)

    @classproperty
    def codes(self) -> Tuple[int, int]:
        return self._default_code, self._default_subcode