def create_node(name: str, phone: str, next: dict = None):
    return {"name": name, "phone": phone, "next": next}


def create_linked_list(data: list[dict]) -> dict:
    """Создание связного списка по массиву словарей.
    
    :param data: Список словарей с параметрами: {'name': str, 'phone': str, next: dict | None}
    :type data: list[dict]
    :raises ValueError: Ошибка при подаче на вход пустого списка
    :return: Связный список оформленный по примеру: {'name': str, 'phone': str, next: dict | None}
    :rtype: dict
    """
    if data is None or len(data) == 0:
        raise ValueError("Список пустой!")

    base = create_node(**data[0])

    current = base
    for value in data[1:]:
        current["next"] = create_node(**value)
        current = current["next"]

    return base


def ll_insert(head: dict, name: str, phone: str) -> dict:
    """Добавление нового или редактирование элемента в связном списке
    
    Если пользователь уже есть в списке, то обновятся его данные (номер телефона). В случае если
    данных нет, то они добавляются в конец.

    :param head: Список словарей с параметрами: {'name': str, 'phone': str, next: dict | None}
    :type head: dict
    :param name: Имя пользователя (не должно повторятся с имеющимися)
    :type name: str
    :param phone: Номер телефона пользователя (не должно повторятся с имеющимися)
    :type phone: str
    :raises ValueError: Ошибка при подаче на вход пустого списка
    :return: Возвращает связный список с обновленными данными
    :rtype: dict
    """
    if head is None:
        raise ValueError("Словарь пустой!")

    current = head
    while current["next"] is not None:
        if current.get("name") == name or current.get("phone") == phone:
            current["name"] = name
            current["phone"] = phone
            break
        current = current.get("next")
    else:
        current["next"] = {"name": name, "phone": phone, "next": None}

    return head


def ll_find(head: dict, name: str) -> str | None:
    """Поиск пользователя в связном списке
    
    Если функция найдёт пользователя по имени, то вернёт его номер телефона. 
    В противном случае будет возвращено значение None.
    
    В случае повторяющихся имен в списке, выведется выше стоящие

    :param head: Список словарей с параметрами: {'name': str, 'phone': str, next: dict | None}
    :type head: dict
    :param name: Имя пользователя
    :type name: str
    :raises ValueError: Ошибка при подаче на вход пустого списка
    :return: Возвращает номер телефона найденного пользователя, иначе None
    :rtype: str | None
    """
    if head is None:
        raise ValueError("Словарь пустой!")

    current = head
    while current is not None:
        if current["name"] == name:
            return current["phone"]
        current = current["next"]
    return None


def ll_delete(head: dict, name: str) -> dict:
    """Удаление пользователя из связного списка

    :param head: Список словарей с параметрами: {'name': str, 'phone': str, next: dict | None}
    :type head: dict
    :param name: Имя пользователя
    :type name: str
    :raises ValueError: Ошибка при подаче на вход пустого списка
    :return: Возвращает связный список с обновленными данными
    :rtype: dict
    """
    if head is None:
        raise ValueError("Словарь пустой!")

    if head.get("name") == name:
        head = head.get("next")
        return head

    current = head
    while current.get("next") is not None:
        if current.get("next").get("name") == name:
            current["next"] = current.get("next").get("next")
            return head
        current = current.get("next")

    return head


def ll_list_all(head: dict) -> list:
    result = []
    current = head
    while current is not None:
        result.append({"name": current.get("name"), "phone": current.get("phone")})
        current = current.get("next")
    return result

