int loadExternalConfig(std::string &path, ExternalConfig &ext)
{
    std::string base_content, proxy = parseProxy(global.proxyConfig), config = fetchFile(path, proxy, global.cacheConfig);
    if(render_template(config, *ext.tpl_args, base_content, global.templatePath) != 0)
        base_content = config;

    try
    {
        YAML::Node yaml = YAML::Load(base_content);
        if(yaml.size() && yaml["custom"].IsDefined())
            return loadExternalYAML(yaml, ext);
        toml::value conf = parseToml(base_content, path);
        if(!conf.is_uninitialized() && toml::find_or<int>(conf, "version", 0))
            return loadExternalTOML(conf, ext);
    }
    catch (YAML::Exception &e)
    {
        //ignore
    }
    catch (toml::exception &e)
    {
        //ignore
    }

    INIReader ini;
    ini.store_isolated_line = true;
    ini.SetIsolatedItemsSection("custom");
    if(ini.Parse(base_content) != INIREADER_EXCEPTION_NONE)
    {
        //std::cerr<<"Load external configuration failed. Reason: "<<ini.GetLastError()<<"\n";
        writeLog(0, "Load external configuration failed. Reason: " + ini.GetLastError(), LOG_LEVEL_ERROR);
        return -1;
    }

    ini.EnterSection("custom");
    if(ini.ItemPrefixExist("custom_proxy_group"))
    {
        string_array vArray;
        ini.GetAll("custom_proxy_group", vArray);
        importItems(vArray, global.APIMode);
        ext.custom_proxy_group = INIBinding::from<ProxyGroupConfig>::from_ini(vArray);
    }
    std::string ruleset_name = ini.ItemPrefixExist("ruleset") ? "ruleset" : "surge_ruleset";
    if(ini.ItemPrefixExist(ruleset_name))
    {
        string_array vArray;
        ini.GetAll(ruleset_name, vArray);
        importItems(vArray, global.APIMode);
        if(global.maxAllowedRulesets && vArray.size() > global.maxAllowedRulesets)
        {
            writeLog(0, "Ruleset count in external config has exceeded limit. ", LOG_LEVEL_WARNING);
            return -1;
        }
        ext.surge_ruleset = INIBinding::from<RulesetConfig>::from_ini(vArray);
    }

    ini.GetIfExist("clash_rule_base", ext.clash_rule_base);
    ini.GetIfExist("surge_rule_base", ext.surge_rule_base);
    ini.GetIfExist("surfboard_rule_base", ext.surfboard_rule_base);
    ini.GetIfExist("mellow_rule_base", ext.mellow_rule_base);
    ini.GetIfExist("quan_rule_base", ext.quan_rule_base);
    ini.GetIfExist("quanx_rule_base", ext.quanx_rule_base);
    ini.GetIfExist("loon_rule_base", ext.loon_rule_base);
    ini.GetIfExist("sssub_rule_base", ext.sssub_rule_base);

    ini.GetBoolIfExist("overwrite_original_rules", ext.overwrite_original_rules);
    ini.GetBoolIfExist("enable_rule_generator", ext.enable_rule_generator);

    if(ini.ItemPrefixExist("rename"))
    {
        string_array vArray;
        ini.GetAll("rename", vArray);
        importItems(vArray, global.APIMode);
        ext.rename = INIBinding::from<RegexMatchConfig>::from_ini(vArray, "@");
    }
    ext.add_emoji = ini.Get("add_emoji");
    ext.remove_old_emoji = ini.Get("remove_old_emoji");
    if(ini.ItemPrefixExist("emoji"))
    {
        string_array vArray;
        ini.GetAll("emoji", vArray);
        importItems(vArray, global.APIMode);
        ext.emoji = INIBinding::from<RegexMatchConfig>::from_ini(vArray, ",");
    }
    if(ini.ItemPrefixExist("include_remarks"))
        ini.GetAll("include_remarks", ext.include);
    if(ini.ItemPrefixExist("exclude_remarks"))
        ini.GetAll("exclude_remarks", ext.exclude);

    if(ini.SectionExist("template") && ext.tpl_args != nullptr)
    {
        ini.EnterSection("template");
        string_multimap tempmap;
        ini.GetItems(tempmap);
        for(auto &x : tempmap)
            ext.tpl_args->local_vars[x.first] = x.second;
    }

    return 0;
}