            val = self._render_format(fmt, prefix)
            if val not in used:
                return val
        raise ValueError("Could not generate unique invoice number after 50 attempts")

    def _render_format(self, fmt: str, prefix: str) -> str:
        from string import ascii_letters, digits
        upper = string.ascii_uppercase + string.digits
        lower = string.ascii_lowercase + string.digits
        digits_only = string.digits

        def rnd(chars, n):
            return "".join(self.rng.choice(chars) for _ in range(n))

        def repl(match):
            token = match.group(1)
            if token == "prefix":
                return prefix
            if token == "date":
                return datetime.utcnow().strftime("%Y%m%d")
            if token.startswith("R"):  # e.g. R4, R5
                try:
                    n = int(token[1:])
                except ValueError:
                    return token
                return rnd(upper, n)
            if token.startswith("r"):
                try:
                    n = int(token[1:])
                except ValueError:
                    return token
                return rnd(lower, n)
            if token.startswith("d"):
                try:
                    n = int(token[1:])
                except ValueError:
                    return token
                return rnd(digits_only, n)
            if token == "seq4":
                return rnd(digits_only, 4)
            if token.startswith("hash"):
                n = int(token[4:] or 8)
                return secrets.token_hex(n)
            return match.group(0)

        return re.sub(r"\{([A-Za-z0-9_]+)(\d*)\}", repl, fmt)
