'use strict'

let l;
export default l = async (path, readFile, exec, log) => {

  const load_static_rules = async path => 
    (await readFile(path, 'utf-8'))
      .split('\n')
      .filter(e => e)

  const get_current_rules = async () => 
    (await exec('iptables -S INPUT'))
      .stdout
      .split('\n')
      .filter(e => e.match(/^-A INPUT/g))

  const compute_rules_to_add = (current, target) =>
    target.filter(r => !current.includes(r))

  const compute_rules_to_del = (current, target) =>
    current
      .filter(r => !target.includes(r))
      .map(r => r.replace(/^-A INPUT/g, '-D INPUT'))

  const update_rules = async (current, target) =>
    await Promise.all([
      ...compute_rules_to_del(current, target),
      ...compute_rules_to_add(current, target)
    ].map(r => exec(`iptables ${r}`)))

  const build_target_rules = (tag_list) => 
    tag_list
      .map(t => /^public_port=(\d+)(-(\d+))?\/(udp|tcp)/g.exec(t))
      .filter(t => t)
      .map(t => new Object({ start: t[1], stop: t[3], protocol: t[4] }))
      .map(t => t.stop
        ? `-A INPUT -p ${t.protocol} --match multiport --dports ${t.start}:${t.stop} -j ACCEPT`
        : `-A INPUT -p ${t.protocol} --dport ${t.start} -j ACCEPT`)

  const do_log = (tag_list, r) => {
    //log('[iptables]', tag_list)
    log(`[iptables] ran ${r.length} commands`)
  }
  
  const static_rules = path ? await load_static_rules(path) : []
  log(`[iptables] initialized with ${static_rules.length} static rules`)
  return async tag_list =>
    do_log(
      tag_list,
      await update_rules(
        await get_current_rules(),
        [...static_rules, ...build_target_rules(tag_list)]))
}
