My solution starts from the fact that I don't have permissions in my scenario to perform DELETE FROM pg_enum as I'm getting a permissions error.
Starting from other replies, I created a generic function that can be used to remove a single value from a enum, supporting the update value to release the use of given value
-- https://stackoverflow.com/a/62444685
-- https://stackoverflow.com/a/51073579
create or replace function remove_enum_value(
    type_name text,         -- Name of the type where you need to remove a value from
    value_to_delete text,   -- Specific value of the given type you want to remove
    value_fallback text,    -- Which new value columns will have instead of the value deleted
    column_default text     -- DEFAULT value for the column after type alteration (DEFAULT need to be disabled before changing type, https://stackoverflow.com/a/41149789)
)
RETURNS VOID AS $body$
declare 
    -- Used as temporary type
    _type_name_tmp text := type_name || '_tmp_' || floor(extract(epoch from now()) * 1000);
    -- Used to store statements to execute
    _sql text;
    -- Used to loop tables and switch type from current to temporary
    _column_data record;
    _table_name varchar(255);
    _column_name varchar(255);
   
begin   
    
    --------------------------------------------------------------------------------------------------------------
    
    -- Check: required inputs
    if type_name is null
    then
        raise exception 'Parameter type_name is null';
    end if;
    if value_to_delete is null
    then
        raise exception 'Parameter value_to_delete is null';
    end if;
    
    -- Check: type exists
    IF not EXISTS (SELECT 1 FROM pg_type WHERE typname = type_name) THEN
        raise info 'Type %s does not exists', type_name;
        return;
    END IF;
    
    -- Check: value to delete exists
    if not exists(
        select * 
        FROM pg_enum -- check existing of value to delete
        WHERE enumtypid = (select oid from pg_type where typName=cast(type_name as varchar) limit 1) and enumlabel=cast(value_to_delete as varchar)
    )
    then 
        raise info 'Value to delete % does not exists in type %s', value_to_delete, type_name;
        return;
    end if;
        
    -- Check: fallback value is provided and exists
    if value_fallback is not null and not exists(
        select * 
        FROM pg_enum -- check existing of value to delete
        where
            enumtypid = (select oid from pg_type where typName=cast(type_name as varchar) limit 1)
            and enumlabel=cast(value_fallback as varchar)
    )
    then 
        raise info 'Fallback value % does not exists in type %s', value_fallback, type_name;
        return;
    end if;
    -- Check values are different
    if value_fallback = value_to_delete
    then
        raise info 'Value to delete %s is the same as fallback value %', value_to_delete, value_fallback;
        return;
    end if;
    raise info 'Checks passed, ready to process!';
    --------------------------------------------------------------------------------------------------------------
        
    -- Retrieve current values of type
    _sql := format('
        SELECT string_agg(quote_literal(value), '','')
        FROM unnest(enum_range(NULL::%s)) value
        WHERE value <> ''%s''
    ', type_name, value_to_delete);
    raise info '%', _sql;
    execute _sql into _sql;
    -- Create temporary enum
    _sql := format(
        'CREATE TYPE %s AS ENUM (%s)',
        _type_name_tmp,
        _sql
    );
    raise info '%', _sql;
    execute _sql;
    -- Rename all values from value that need to be deleted to new value (selecting all tables with schemas which has column with enum relation)
    for _column_data in (
        select
            concat(c.table_schema,'.',c.table_name ) as table_name,
            c.column_name
        FROM information_schema.columns c
        where
            c.udt_name = cast(type_name as varchar)
            and c.table_schema=c.udt_schema 
            and data_type = 'USER-DEFINED'
    )
    LOOP
        _sql:= format('UPDATE %1$s set %2$s = %3$L where %2$s=%4$L', _column_data.table_name, _column_data.column_name, value_fallback, value_to_delete);
        raise info 'Update by looping: %', _sql;
        EXECUTE _sql;
    END LOOP;
    -- Switch type from current to temporary
    FOR _column_data in (
        SELECT cols.table_name, cols.column_name
        FROM information_schema.columns cols
        WHERE udt_name = type_name
    )
    LOOP
        _table_name := _column_data.table_name;
        _column_name := _column_data.column_name;
        _sql := format(
            '
                ALTER TABLE %s
                    ALTER COLUMN %s DROP DEFAULT,
                    ALTER COLUMN %s TYPE %s USING %s::text::%s,
                    ALTER COLUMN %s SET DEFAULT %s;
            ',
            _table_name, 
            _column_name, 
            _column_name, _type_name_tmp, _column_name, _type_name_tmp,
            _column_name, (case when column_default is null then null else '''' || column_default || '''::' || _type_name_tmp end)
        );
       
        raise info '%', _sql;
        execute _sql;
    END LOOP;
   
    -- Drop previous type
    _sql := format('DROP TYPE %s;', type_name);
    raise info '%', _sql;
    execute _sql;
    
    -- Rename type to previous name
    _sql := format('ALTER TYPE %s RENAME TO %s;', _type_name_tmp, type_name);
    raise info '%', _sql;
    execute _sql;
       
END $body$
LANGUAGE plpgsql;