(local { : split : merge : hash : base64url } (require :anoia))
(local { : mktree : rmtree } (require :anoia.fs))

(local state-directory (assert (os.getenv "SERVICE_STATE")))
(mktree state-directory)

(fn write-value [name value]
  (let [path (.. state-directory "/" name)]
    (with-open [fout (io.open path :w)]
      (when value (fout:write value)))))

(fn write-value-from-env [name]
  (write-value name (os.getenv (string.upper name))))

(fn parse-address [str]
  (fn parse-extra [s]
    (let [out {}]
      (each [name val (string.gmatch s ",(.-)=([^,]+)")]
        (tset out name val))
      out))
  (let [(address len preferred valid extra)
        (string.match str "(.-)/(%d+),(%d+),(%d+)(.*)$")]
    (merge {: address : len
            :preferred (or preferred "forever")
            :valid (or valid "forever")
            }
           (parse-extra extra))))

(fn write-addresses [prefix addresses]
  (each [_ a (ipairs (split " " addresses))]
    (let [address (parse-address a)
          suffix (base64url (string.pack "n" (hash a)))
          keydir (..
                  prefix
                  (-> address.address
                      (: :gsub "::$" "")
                      (: :gsub ":" "-")))]
      (mktree (.. state-directory "/" keydir))
      (each [k v (pairs address)]
        (write-value (.. keydir "/" k) v)))))

;; we remove state before updating to ensure that consumers don't get
;; a half-updated snapshot
(os.remove (.. state-directory "/state"))

;; remove parsed addresses/prefixes from any previous run
(rmtree (.. state-directory "/prefix"))
(rmtree (.. state-directory "/address"))

(let [wanted
      [
       :addresses
       :aftr
       :cer
       :domains
       :lw406
       :mape
       :mapt
       :ntp_fqdn
       :ntp_ip
       :option_1
       :option_2
       :option_3
       :option_4
       :option_5
       :passthru
       :prefixes
       :ra_addresses
       :ra_dns
       :ra_domains
       :ra_hoplimit
       :ra_mtu
       :ra_reachable
       :ra_retransmit
       :ra_routes
       :rdnss
       :server
       :sip_domain
       :sip_ip
       :sntp_ip
       :sntp_fqdn
       ]]
  (each [_ n (ipairs wanted)]
    (write-value-from-env n))

  (match (os.getenv :ADDRESSES) s (write-addresses "address/" s))
  (match (os.getenv :PREFIXES) s (write-addresses "prefix/" s)))

(let [[ifname state] arg
      ready (match state
              "started" false
              "unbound" false
              "stopped" false
              _ true)]
  (write-value ".lock" (tostring (os.time)))
  (write-value "ifname" ifname)
  (write-value "state" state)
  (os.remove (.. state-directory "/.lock"))
  (when ready
    (with-open [fd (io.open "/proc/self/fd/10" :w)] (fd:write "\n"))))