from otree.i18n import gettext


class MustCopyError(Exception):
    pass


def _raise_must_copy(*args, **kwargs):
    msg = (
        "Cannot modify a list that originated in Constants. "
        "First, you must make a copy of it, for example: "
        "'my_list = Constants.my_list.copy()' "
        "or "
        "'self.participant.vars['my_list'] = Constants.my_list.copy()'. "
        "This is to prevent accidentally modifying the original list. "
    )
    raise MustCopyError(msg)


class ConstantsList(list):

    __setitem__ = _raise_must_copy
    __delitem__ = _raise_must_copy
    clear = _raise_must_copy
    __iadd__ = _raise_must_copy
    __imul__ = _raise_must_copy
    append = _raise_must_copy
    extend = _raise_must_copy
    insert = _raise_must_copy
    pop = _raise_must_copy
    remove = _raise_must_copy
    reverse = _raise_must_copy
    sort = _raise_must_copy


class BaseConstantsMeta(type):
    def __setattr__(cls, attr, value):
        raise AttributeError("Constants are read-only.")

    def __new__(mcs, name, bases, attrs):

        for k, v in attrs.items():
            if type(v) == list:
                attrs[k] = ConstantsList(v)

        return super().__new__(mcs, name, bases, attrs)


class BaseConstants(metaclass=BaseConstantsMeta):
    pass


def get_roles(Constants) -> list:
    roles = []
    for k, v in Constants.__dict__.items():
        if k.startswith('role_'):
            if not isinstance(v, str):
                # this is especially for legacy apps before the role_* feature was introduced.
                msg = f"{k}: any Constant that starts with 'role_' must be a string, for example: role_sender = 'Sender'"
                raise Exception(msg)
            roles.append(v)
    return roles


def get_role(roles, id_in_group):
    '''this is split apart from get_roles_ as a perf optimization'''
    if roles and len(roles) >= id_in_group:
        return roles[id_in_group - 1]


get_param_truth_value = '1'
admin_secret_code = 'admin_secret_code'
timeout_happened = 'timeout_happened'
participant_label = 'participant_label'
wait_page_http_header = 'oTree-Wait-Page'
redisplay_with_errors_http_header = 'oTree-Redisplay-With-Errors'
field_required_msg = gettext('This field is required.')
AUTO_NAME_BOTS_EXPORT_FOLDER = 'auto_name'
