#!/usr/bin/env bash
#
#  srvmetrics — instalador do agente de métricas (CPU / RAM / Disco)
#  Uso:
#      curl -fsSL https://script-monitor.flowia.chat | sudo bash
#      curl -fsSL https://script-monitor.flowia.chat | sudo bash -s -- --port 9847
#      sudo bash <(curl -fsSL https://script-monitor.flowia.chat) --uninstall
#
#  Este arquivo é gerado por build.sh — edite src/installer.sh e src/monitor_agent.py.
#
set -Eeuo pipefail

VERSION="1.0.0"
AGENT_PAYLOAD_B64="IyEvdXNyL2Jpbi9lbnYgcHl0aG9uMwojIC0qLSBjb2Rpbmc6IHV0Zi04IC0qLQoiIiIKc3J2bWV0cmljcyBhZ2VudCDigJQgZXhww7VlIG3DqXRyaWNhcyBkbyBzZXJ2aWRvciAoQ1BVLCBSQU0sIERpc2NvKSB2aWEgSFRUUC4KClNlbSBkZXBlbmTDqm5jaWFzIGV4dGVybmFzOiBhcGVuYXMgYSBzdGRsaWIgZG8gUHl0aG9uIDMuOCsuCkzDqiB0dWRvIGRlIC9wcm9jIGUgZGUgb3Muc3RhdHZmcygpLCBlbnTDo28gZnVuY2lvbmEgZW0gcXVhbHF1ZXIgVWJ1bnR1L0RlYmlhbi4KCkVuZHBvaW50czoKICAgIEdFVCAvICAgICAgICAgICAgICAgICAgICDihpIgaWRlbnRpZmljYcOnw6NvIGRvIHNlcnZpw6dvIChww7pibGljbykKICAgIEdFVCAvaGVhbHRoICAgICAgICAgICAgICDihpIgbGl2ZW5lc3MgY2hlY2sgKHDDumJsaWNvKQogICAgR0VUIC9tZXRyaWNzICAgICAgICAgICAgIOKGkiBtw6l0cmljYXMgZW0gSlNPTiAgICAgICAgICAgIChyZXF1ZXIgdG9rZW4pCiAgICBHRVQgL21ldHJpY3MvcHJvbWV0aGV1cyAg4oaSIG3DqXRyaWNhcyBmb3JtYXRvIFByb21ldGhldXMgKHJlcXVlciB0b2tlbikKCkF1dGVudGljYcOnw6NvOgogICAgQXV0aG9yaXphdGlvbjogQmVhcmVyIDx0b2tlbj4KICAgIFgtQXV0aC1Ub2tlbjogPHRva2VuPgoiIiIKCmltcG9ydCBobWFjCmltcG9ydCBqc29uCmltcG9ydCBvcwppbXBvcnQgcGxhdGZvcm0KaW1wb3J0IHNvY2tldAppbXBvcnQgc29ja2V0c2VydmVyCmltcG9ydCBzeXMKaW1wb3J0IHRocmVhZGluZwppbXBvcnQgdGltZQpmcm9tIGNvbGxlY3Rpb25zIGltcG9ydCBkZXF1ZQpmcm9tIGh0dHAuc2VydmVyIGltcG9ydCBCYXNlSFRUUFJlcXVlc3RIYW5kbGVyLCBUaHJlYWRpbmdIVFRQU2VydmVyCmZyb20gdXJsbGliLnBhcnNlIGltcG9ydCB1cmxwYXJzZSwgcGFyc2VfcXMKClZFUlNJT04gPSAiMS4wLjAiCkRFRkFVTFRfQ09ORklHID0gIi9ldGMvc3J2bWV0cmljcy9jb25maWcuanNvbiIKCiMgU2lzdGVtYXMgZGUgYXJxdWl2b3MgdmlydHVhaXMvZWbDqm1lcm9zIHF1ZSBuw6NvIGludGVyZXNzYW0gY29tbyAiZGlzY28iLgpQU0VVRE9fRlMgPSB7CiAgICAiYXV0b2ZzIiwgImJwZiIsICJiaW5mbXRfbWlzYyIsICJjZ3JvdXAiLCAiY2dyb3VwMiIsICJjb25maWdmcyIsICJkZWJ1Z2ZzIiwKICAgICJkZXZwdHMiLCAiZGV2dG1wZnMiLCAiZWZpdmFyZnMiLCAiZnVzZS5ndmZzZC1mdXNlIiwgImZ1c2VjdGwiLCAiaHVnZXRsYmZzIiwKICAgICJtcXVldWUiLCAibnNmcyIsICJvdmVybGF5IiwgInByb2MiLCAicHN0b3JlIiwgInJhbWZzIiwgInJwY19waXBlZnMiLAogICAgInNlY3VyaXR5ZnMiLCAic2VsaW51eGZzIiwgInNxdWFzaGZzIiwgInN5c2ZzIiwgInRtcGZzIiwgInRyYWNlZnMiLAp9CgojIEludGVyZmFjZXMgdmlydHVhaXMgKGRvY2tlciwgazhzLCBsaWJ2aXJ0KSBwb2x1ZW0gYSBzYcOtZGE7IHVzZSA/bmV0PWFsbCBwYXJhIHbDqi1sYXMuClZJUlRVQUxfSUZBQ0VfUFJFRklYRVMgPSAoCiAgICAiYnItIiwgImNhbGkiLCAiY25pIiwgImRvY2tlciIsICJmbGFubmVsIiwgImt1YmUiLCAidmV0aCIsICJ2aXJiciIsICJ2eGxhbiIsCikKCiMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiMgQ29sZXRhIGRlIGRhZG9zIGJydXRvcwojIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAoKCmRlZiBfcmVhZChwYXRoLCBkZWZhdWx0PSIiKToKICAgIHRyeToKICAgICAgICB3aXRoIG9wZW4ocGF0aCwgInIiLCBlbmNvZGluZz0idXRmLTgiLCBlcnJvcnM9InJlcGxhY2UiKSBhcyBmaDoKICAgICAgICAgICAgcmV0dXJuIGZoLnJlYWQoKQogICAgZXhjZXB0IE9TRXJyb3I6CiAgICAgICAgcmV0dXJuIGRlZmF1bHQKCgpkZWYgcmVhZF9jcHVfdGltZXMoKToKICAgICIiIlJldG9ybmEgeydjcHUnOiAoaWRsZV9hbGwsIHRvdGFsKSwgJ2NwdTAnOiAoLi4uKSwgLi4ufSBhIHBhcnRpciBkZSAvcHJvYy9zdGF0LiIiIgogICAgdGltZXMgPSB7fQogICAgZm9yIGxpbmUgaW4gX3JlYWQoIi9wcm9jL3N0YXQiKS5zcGxpdGxpbmVzKCk6CiAgICAgICAgaWYgbm90IGxpbmUuc3RhcnRzd2l0aCgiY3B1Iik6CiAgICAgICAgICAgIGJyZWFrCiAgICAgICAgcGFydHMgPSBsaW5lLnNwbGl0KCkKICAgICAgICBuYW1lID0gcGFydHNbMF0KICAgICAgICB2YWx1ZXMgPSBbaW50KHYpIGZvciB2IGluIHBhcnRzWzE6OV1dIGlmIGxlbihwYXJ0cykgPj0gOSBlbHNlIFtpbnQodikgZm9yIHYgaW4gcGFydHNbMTpdXQogICAgICAgIHdoaWxlIGxlbih2YWx1ZXMpIDwgODoKICAgICAgICAgICAgdmFsdWVzLmFwcGVuZCgwKQogICAgICAgIHVzZXIsIG5pY2UsIHN5c3RlbSwgaWRsZSwgaW93YWl0LCBpcnEsIHNvZnRpcnEsIHN0ZWFsID0gdmFsdWVzWzo4XQogICAgICAgIGlkbGVfYWxsID0gaWRsZSArIGlvd2FpdAogICAgICAgIHRvdGFsID0gdXNlciArIG5pY2UgKyBzeXN0ZW0gKyBpZGxlICsgaW93YWl0ICsgaXJxICsgc29mdGlycSArIHN0ZWFsCiAgICAgICAgdGltZXNbbmFtZV0gPSAoaWRsZV9hbGwsIHRvdGFsLCB7CiAgICAgICAgICAgICJ1c2VyIjogdXNlciwgIm5pY2UiOiBuaWNlLCAic3lzdGVtIjogc3lzdGVtLCAiaWRsZSI6IGlkbGUsCiAgICAgICAgICAgICJpb3dhaXQiOiBpb3dhaXQsICJpcnEiOiBpcnEsICJzb2Z0aXJxIjogc29mdGlycSwgInN0ZWFsIjogc3RlYWwsCiAgICAgICAgfSkKICAgIHJldHVybiB0aW1lcwoKCmRlZiByZWFkX25ldF9jb3VudGVycygpOgogICAgIiIiUmV0b3JuYSB7aWZhY2U6IChyeF9ieXRlcywgdHhfYnl0ZXMpfSBhIHBhcnRpciBkZSAvcHJvYy9uZXQvZGV2LiIiIgogICAgY291bnRlcnMgPSB7fQogICAgZm9yIGxpbmUgaW4gX3JlYWQoIi9wcm9jL25ldC9kZXYiKS5zcGxpdGxpbmVzKClbMjpdOgogICAgICAgIGlmICI6IiBub3QgaW4gbGluZToKICAgICAgICAgICAgY29udGludWUKICAgICAgICBpZmFjZSwgcmVzdCA9IGxpbmUuc3BsaXQoIjoiLCAxKQogICAgICAgIGlmYWNlID0gaWZhY2Uuc3RyaXAoKQogICAgICAgIGlmIGlmYWNlID09ICJsbyI6CiAgICAgICAgICAgIGNvbnRpbnVlCiAgICAgICAgZmllbGRzID0gcmVzdC5zcGxpdCgpCiAgICAgICAgaWYgbGVuKGZpZWxkcykgPj0gOToKICAgICAgICAgICAgY291bnRlcnNbaWZhY2VdID0gKGludChmaWVsZHNbMF0pLCBpbnQoZmllbGRzWzhdKSkKICAgIHJldHVybiBjb3VudGVycwoKCmRlZiBjcHVfbW9kZWwoKToKICAgIGZvciBsaW5lIGluIF9yZWFkKCIvcHJvYy9jcHVpbmZvIikuc3BsaXRsaW5lcygpOgogICAgICAgIGlmIGxpbmUubG93ZXIoKS5zdGFydHN3aXRoKCJtb2RlbCBuYW1lIik6CiAgICAgICAgICAgIHJldHVybiBsaW5lLnNwbGl0KCI6IiwgMSlbMV0uc3RyaXAoKQogICAgcmV0dXJuIHBsYXRmb3JtLnByb2Nlc3NvcigpIG9yICJ1bmtub3duIgoKCmRlZiByZWFkX21lbWluZm8oKToKICAgIGluZm8gPSB7fQogICAgZm9yIGxpbmUgaW4gX3JlYWQoIi9wcm9jL21lbWluZm8iKS5zcGxpdGxpbmVzKCk6CiAgICAgICAgaWYgIjoiIG5vdCBpbiBsaW5lOgogICAgICAgICAgICBjb250aW51ZQogICAgICAgIGtleSwgdmFsdWUgPSBsaW5lLnNwbGl0KCI6IiwgMSkKICAgICAgICBwYXJ0cyA9IHZhbHVlLnNwbGl0KCkKICAgICAgICBpZiBwYXJ0czoKICAgICAgICAgICAgdHJ5OgogICAgICAgICAgICAgICAgaW5mb1trZXkuc3RyaXAoKV0gPSBpbnQocGFydHNbMF0pICogMTAyNCAgIyBrQiDihpIgYnl0ZXMKICAgICAgICAgICAgZXhjZXB0IFZhbHVlRXJyb3I6CiAgICAgICAgICAgICAgICBwYXNzCiAgICByZXR1cm4gaW5mbwoKCmRlZiBjb2xsZWN0X21lbW9yeSgpOgogICAgbSA9IHJlYWRfbWVtaW5mbygpCiAgICB0b3RhbCA9IG0uZ2V0KCJNZW1Ub3RhbCIsIDApCiAgICBhdmFpbGFibGUgPSBtLmdldCgiTWVtQXZhaWxhYmxlIiwgbS5nZXQoIk1lbUZyZWUiLCAwKSkKICAgIGZyZWUgPSBtLmdldCgiTWVtRnJlZSIsIDApCiAgICBidWZmZXJzID0gbS5nZXQoIkJ1ZmZlcnMiLCAwKQogICAgY2FjaGVkID0gbS5nZXQoIkNhY2hlZCIsIDApICsgbS5nZXQoIlNSZWNsYWltYWJsZSIsIDApCiAgICB1c2VkID0gbWF4KHRvdGFsIC0gYXZhaWxhYmxlLCAwKQoKICAgIHN3YXBfdG90YWwgPSBtLmdldCgiU3dhcFRvdGFsIiwgMCkKICAgIHN3YXBfZnJlZSA9IG0uZ2V0KCJTd2FwRnJlZSIsIDApCiAgICBzd2FwX3VzZWQgPSBtYXgoc3dhcF90b3RhbCAtIHN3YXBfZnJlZSwgMCkKCiAgICByZXR1cm4gewogICAgICAgICJ0b3RhbF9ieXRlcyI6IHRvdGFsLAogICAgICAgICJ1c2VkX2J5dGVzIjogdXNlZCwKICAgICAgICAiZnJlZV9ieXRlcyI6IGZyZWUsCiAgICAgICAgImF2YWlsYWJsZV9ieXRlcyI6IGF2YWlsYWJsZSwKICAgICAgICAiYnVmZmVyc19ieXRlcyI6IGJ1ZmZlcnMsCiAgICAgICAgImNhY2hlZF9ieXRlcyI6IGNhY2hlZCwKICAgICAgICAidXNlZF9wZXJjZW50IjogcGN0KHVzZWQsIHRvdGFsKSwKICAgICAgICAic3dhcCI6IHsKICAgICAgICAgICAgInRvdGFsX2J5dGVzIjogc3dhcF90b3RhbCwKICAgICAgICAgICAgInVzZWRfYnl0ZXMiOiBzd2FwX3VzZWQsCiAgICAgICAgICAgICJmcmVlX2J5dGVzIjogc3dhcF9mcmVlLAogICAgICAgICAgICAidXNlZF9wZXJjZW50IjogcGN0KHN3YXBfdXNlZCwgc3dhcF90b3RhbCksCiAgICAgICAgfSwKICAgIH0KCgpkZWYgY29sbGVjdF9kaXNrcygpOgogICAgc2Vlbl9kZXZpY2VzID0gc2V0KCkKICAgIGRpc2tzID0gW10KICAgIGZvciBsaW5lIGluIF9yZWFkKCIvcHJvYy9tb3VudHMiKS5zcGxpdGxpbmVzKCk6CiAgICAgICAgcGFydHMgPSBsaW5lLnNwbGl0KCkKICAgICAgICBpZiBsZW4ocGFydHMpIDwgMzoKICAgICAgICAgICAgY29udGludWUKICAgICAgICBkZXZpY2UsIG1vdW50cG9pbnQsIGZzdHlwZSA9IHBhcnRzWzBdLCBwYXJ0c1sxXS5yZXBsYWNlKCJcXDA0MCIsICIgIiksIHBhcnRzWzJdCiAgICAgICAgaWYgZnN0eXBlIGluIFBTRVVET19GUyBvciBmc3R5cGUuc3RhcnRzd2l0aCgiZnVzZS4iKToKICAgICAgICAgICAgY29udGludWUKICAgICAgICBpZiBub3QgZGV2aWNlLnN0YXJ0c3dpdGgoIi8iKSBhbmQgZnN0eXBlIG5vdCBpbiAoInpmcyIsICJidHJmcyIsICJuZnMiLCAibmZzNCIpOgogICAgICAgICAgICBjb250aW51ZQogICAgICAgIHRyeToKICAgICAgICAgICAgc3QgPSBvcy5zdGF0dmZzKG1vdW50cG9pbnQpCiAgICAgICAgZXhjZXB0IE9TRXJyb3I6CiAgICAgICAgICAgIGNvbnRpbnVlCiAgICAgICAgaWYgc3QuZl9ibG9ja3MgPT0gMDoKICAgICAgICAgICAgY29udGludWUKICAgICAgICBrZXkgPSAoZGV2aWNlLCBzdC5mX2ZzaWQgb3IgbW91bnRwb2ludCkKICAgICAgICBpZiBrZXkgaW4gc2Vlbl9kZXZpY2VzOgogICAgICAgICAgICBjb250aW51ZQogICAgICAgIHNlZW5fZGV2aWNlcy5hZGQoa2V5KQoKICAgICAgICB0b3RhbCA9IHN0LmZfYmxvY2tzICogc3QuZl9mcnNpemUKICAgICAgICBmcmVlID0gc3QuZl9iZnJlZSAqIHN0LmZfZnJzaXplCiAgICAgICAgYXZhaWwgPSBzdC5mX2JhdmFpbCAqIHN0LmZfZnJzaXplCiAgICAgICAgdXNlZCA9IHRvdGFsIC0gZnJlZQogICAgICAgICMgcGVyY2VudHVhbCAiZXN0aWxvIGRmIjogZGVzY29uc2lkZXJhIG9zIGJsb2NvcyByZXNlcnZhZG9zIHBhcmEgbyByb290CiAgICAgICAgdXNhYmxlID0gdXNlZCArIGF2YWlsCiAgICAgICAgZGlza3MuYXBwZW5kKHsKICAgICAgICAgICAgImRldmljZSI6IGRldmljZSwKICAgICAgICAgICAgIm1vdW50cG9pbnQiOiBtb3VudHBvaW50LAogICAgICAgICAgICAiZnN0eXBlIjogZnN0eXBlLAogICAgICAgICAgICAidG90YWxfYnl0ZXMiOiB0b3RhbCwKICAgICAgICAgICAgInVzZWRfYnl0ZXMiOiB1c2VkLAogICAgICAgICAgICAiZnJlZV9ieXRlcyI6IGF2YWlsLAogICAgICAgICAgICAidXNlZF9wZXJjZW50IjogcGN0KHVzZWQsIHVzYWJsZSksCiAgICAgICAgICAgICJpbm9kZXNfdG90YWwiOiBzdC5mX2ZpbGVzLAogICAgICAgICAgICAiaW5vZGVzX2ZyZWUiOiBzdC5mX2ZmcmVlLAogICAgICAgICAgICAiaW5vZGVzX3VzZWRfcGVyY2VudCI6IHBjdChzdC5mX2ZpbGVzIC0gc3QuZl9mZnJlZSwgc3QuZl9maWxlcyksCiAgICAgICAgfSkKICAgIGRpc2tzLnNvcnQoa2V5PWxhbWJkYSBkOiBkWyJtb3VudHBvaW50Il0pCiAgICByZXR1cm4gZGlza3MKCgpkZWYgY29sbGVjdF90b3BfcHJvY2Vzc2VzKGxpbWl0PTUpOgogICAgIiIiVG9wIHByb2Nlc3NvcyBwb3IgUlNTIGUgcG9yIHRlbXBvIGRlIENQVSBhY3VtdWxhZG8uIiIiCiAgICBwcm9jcyA9IFtdCiAgICBwYWdlX3NpemUgPSBvcy5zeXNjb25mKCJTQ19QQUdFX1NJWkUiKQogICAgY2xvY2tfdGlja3MgPSBvcy5zeXNjb25mKCJTQ19DTEtfVENLIikKICAgIGZvciBwaWQgaW4gb3MubGlzdGRpcigiL3Byb2MiKToKICAgICAgICBpZiBub3QgcGlkLmlzZGlnaXQoKToKICAgICAgICAgICAgY29udGludWUKICAgICAgICBzdGF0ID0gX3JlYWQoIi9wcm9jLyVzL3N0YXQiICUgcGlkKQogICAgICAgIGlmIG5vdCBzdGF0OgogICAgICAgICAgICBjb250aW51ZQogICAgICAgIHRyeToKICAgICAgICAgICAgIyBvIGNvbW0gcG9kZSBjb250ZXIgZXNwYcOnb3MgZSBwYXLDqm50ZXNlczogcmVjb3J0YSBwZWxvIMO6bHRpbW8gJyknCiAgICAgICAgICAgIGhlYWQsIF8sIHRhaWwgPSBzdGF0LnJwYXJ0aXRpb24oIikiKQogICAgICAgICAgICBuYW1lID0gaGVhZFtoZWFkLmZpbmQoIigiKSArIDE6XQogICAgICAgICAgICBmaWVsZHMgPSB0YWlsLnNwbGl0KCkKICAgICAgICAgICAgdXRpbWUsIHN0aW1lID0gaW50KGZpZWxkc1sxMV0pLCBpbnQoZmllbGRzWzEyXSkKICAgICAgICAgICAgcnNzX3BhZ2VzID0gaW50KGZpZWxkc1syMV0pCiAgICAgICAgZXhjZXB0IChWYWx1ZUVycm9yLCBJbmRleEVycm9yKToKICAgICAgICAgICAgY29udGludWUKICAgICAgICBwcm9jcy5hcHBlbmQoewogICAgICAgICAgICAicGlkIjogaW50KHBpZCksCiAgICAgICAgICAgICJuYW1lIjogbmFtZSwKICAgICAgICAgICAgImNwdV9zZWNvbmRzIjogcm91bmQoKHV0aW1lICsgc3RpbWUpIC8gY2xvY2tfdGlja3MsIDIpLAogICAgICAgICAgICAibWVtb3J5X2J5dGVzIjogcnNzX3BhZ2VzICogcGFnZV9zaXplLAogICAgICAgIH0pCiAgICBieV9tZW0gPSBzb3J0ZWQocHJvY3MsIGtleT1sYW1iZGEgcDogcFsibWVtb3J5X2J5dGVzIl0sIHJldmVyc2U9VHJ1ZSlbOmxpbWl0XQogICAgYnlfY3B1ID0gc29ydGVkKHByb2NzLCBrZXk9bGFtYmRhIHA6IHBbImNwdV9zZWNvbmRzIl0sIHJldmVyc2U9VHJ1ZSlbOmxpbWl0XQogICAgcmV0dXJuIHsiYnlfbWVtb3J5IjogYnlfbWVtLCAiYnlfY3B1X3RpbWUiOiBieV9jcHUsICJ0b3RhbCI6IGxlbihwcm9jcyl9CgoKZGVmIHBjdChwYXJ0LCB3aG9sZSk6CiAgICBpZiBub3Qgd2hvbGU6CiAgICAgICAgcmV0dXJuIDAuMAogICAgcmV0dXJuIHJvdW5kKHBhcnQgKiAxMDAuMCAvIHdob2xlLCAyKQoKCiMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiMgQW1vc3RyYWRvciBlbSBiYWNrZ3JvdW5kIChDUFUgZSByZWRlIHByZWNpc2FtIGRlIGRlbHRhIGVudHJlIGRvaXMgaW5zdGFudGVzKQojIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAoKCmNsYXNzIFNhbXBsZXIodGhyZWFkaW5nLlRocmVhZCk6CiAgICBkYWVtb24gPSBUcnVlCgogICAgZGVmIF9faW5pdF9fKHNlbGYsIGludGVydmFsPTIuMCk6CiAgICAgICAgc3VwZXIoKS5fX2luaXRfXyhuYW1lPSJzcnZtZXRyaWNzLXNhbXBsZXIiKQogICAgICAgIHNlbGYuaW50ZXJ2YWwgPSBpbnRlcnZhbAogICAgICAgIHNlbGYuX2xvY2sgPSB0aHJlYWRpbmcuTG9jaygpCiAgICAgICAgc2VsZi5fY3B1ID0geyJ1c2FnZV9wZXJjZW50IjogMC4wLCAicGVyX2NvcmVfcGVyY2VudCI6IFtdLCAiYnJlYWtkb3duX3BlcmNlbnQiOiB7fX0KICAgICAgICBzZWxmLl9uZXQgPSB7fQogICAgICAgIHNlbGYuX3ByZXZfY3B1ID0gcmVhZF9jcHVfdGltZXMoKQogICAgICAgIHNlbGYuX3ByZXZfbmV0ID0gcmVhZF9uZXRfY291bnRlcnMoKQogICAgICAgIHNlbGYuX3ByZXZfdHMgPSB0aW1lLm1vbm90b25pYygpCiAgICAgICAgc2VsZi5fcmVhZHkgPSB0aHJlYWRpbmcuRXZlbnQoKQoKICAgIGRlZiBydW4oc2VsZik6CiAgICAgICAgd2hpbGUgVHJ1ZToKICAgICAgICAgICAgdGltZS5zbGVlcChzZWxmLmludGVydmFsKQogICAgICAgICAgICB0cnk6CiAgICAgICAgICAgICAgICBzZWxmLl90aWNrKCkKICAgICAgICAgICAgZXhjZXB0IEV4Y2VwdGlvbiBhcyBleGM6ICAjIG8gYWdlbnRlIG51bmNhIGRldmUgbW9ycmVyIHBvciB1bSB0aWNrIHJ1aW0KICAgICAgICAgICAgICAgIGxvZygic2FtcGxlciBlcnJvcjogJXMiICUgZXhjKQoKICAgIGRlZiBfdGljayhzZWxmKToKICAgICAgICBub3cgPSB0aW1lLm1vbm90b25pYygpCiAgICAgICAgY3B1X25vdyA9IHJlYWRfY3B1X3RpbWVzKCkKICAgICAgICBuZXRfbm93ID0gcmVhZF9uZXRfY291bnRlcnMoKQogICAgICAgIGVsYXBzZWQgPSBtYXgobm93IC0gc2VsZi5fcHJldl90cywgMWUtNikKCiAgICAgICAgcGVyX2NvcmUgPSBbXQogICAgICAgIG92ZXJhbGwgPSAwLjAKICAgICAgICBicmVha2Rvd24gPSB7fQogICAgICAgIGZvciBuYW1lLCAoaWRsZV9hbGwsIHRvdGFsLCByYXcpIGluIHNvcnRlZChjcHVfbm93Lml0ZW1zKCkpOgogICAgICAgICAgICBwcmV2ID0gc2VsZi5fcHJldl9jcHUuZ2V0KG5hbWUpCiAgICAgICAgICAgIGlmIG5vdCBwcmV2OgogICAgICAgICAgICAgICAgY29udGludWUKICAgICAgICAgICAgZF90b3RhbCA9IHRvdGFsIC0gcHJldlsxXQogICAgICAgICAgICBkX2lkbGUgPSBpZGxlX2FsbCAtIHByZXZbMF0KICAgICAgICAgICAgdXNhZ2UgPSAwLjAgaWYgZF90b3RhbCA8PSAwIGVsc2Ugcm91bmQoKGRfdG90YWwgLSBkX2lkbGUpICogMTAwLjAgLyBkX3RvdGFsLCAyKQogICAgICAgICAgICBpZiBuYW1lID09ICJjcHUiOgogICAgICAgICAgICAgICAgb3ZlcmFsbCA9IHVzYWdlCiAgICAgICAgICAgICAgICBpZiBkX3RvdGFsID4gMDoKICAgICAgICAgICAgICAgICAgICBicmVha2Rvd24gPSB7CiAgICAgICAgICAgICAgICAgICAgICAgIGs6IHJvdW5kKChyYXdba10gLSBwcmV2WzJdW2tdKSAqIDEwMC4wIC8gZF90b3RhbCwgMikKICAgICAgICAgICAgICAgICAgICAgICAgZm9yIGsgaW4gcmF3CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICBlbHNlOgogICAgICAgICAgICAgICAgcGVyX2NvcmUuYXBwZW5kKHVzYWdlKQoKICAgICAgICBuZXQgPSB7fQogICAgICAgIGZvciBpZmFjZSwgKHJ4LCB0eCkgaW4gbmV0X25vdy5pdGVtcygpOgogICAgICAgICAgICBwcmV2ID0gc2VsZi5fcHJldl9uZXQuZ2V0KGlmYWNlKQogICAgICAgICAgICBpZiBub3QgcHJldjoKICAgICAgICAgICAgICAgIGNvbnRpbnVlCiAgICAgICAgICAgIG5ldFtpZmFjZV0gPSB7CiAgICAgICAgICAgICAgICAicnhfYnl0ZXMiOiByeCwKICAgICAgICAgICAgICAgICJ0eF9ieXRlcyI6IHR4LAogICAgICAgICAgICAgICAgInJ4X2J5dGVzX3Blcl9zZWMiOiByb3VuZChtYXgocnggLSBwcmV2WzBdLCAwKSAvIGVsYXBzZWQsIDIpLAogICAgICAgICAgICAgICAgInR4X2J5dGVzX3Blcl9zZWMiOiByb3VuZChtYXgodHggLSBwcmV2WzFdLCAwKSAvIGVsYXBzZWQsIDIpLAogICAgICAgICAgICB9CgogICAgICAgIHdpdGggc2VsZi5fbG9jazoKICAgICAgICAgICAgc2VsZi5fY3B1ID0gewogICAgICAgICAgICAgICAgInVzYWdlX3BlcmNlbnQiOiBvdmVyYWxsLAogICAgICAgICAgICAgICAgInBlcl9jb3JlX3BlcmNlbnQiOiBwZXJfY29yZSwKICAgICAgICAgICAgICAgICJicmVha2Rvd25fcGVyY2VudCI6IGJyZWFrZG93biwKICAgICAgICAgICAgfQogICAgICAgICAgICBzZWxmLl9uZXQgPSBuZXQKICAgICAgICBzZWxmLl9wcmV2X2NwdSwgc2VsZi5fcHJldl9uZXQsIHNlbGYuX3ByZXZfdHMgPSBjcHVfbm93LCBuZXRfbm93LCBub3cKICAgICAgICBzZWxmLl9yZWFkeS5zZXQoKQoKICAgIGRlZiB3YWl0X3JlYWR5KHNlbGYsIHRpbWVvdXQpOgogICAgICAgICIiIk5hIHByaW1laXJhIHJlcXVpc2nDp8OjbyBwb2RlIG7Do28gaGF2ZXIgZGVsdGEgYWluZGE6IGZvcsOnYSB1bWEgYW1vc3RyYSBjdXJ0YS4iIiIKICAgICAgICBpZiBzZWxmLl9yZWFkeS5pc19zZXQoKToKICAgICAgICAgICAgcmV0dXJuCiAgICAgICAgaWYgbm90IHNlbGYuX3JlYWR5LndhaXQodGltZW91dCk6CiAgICAgICAgICAgIHRyeToKICAgICAgICAgICAgICAgIHNlbGYuX3RpY2soKQogICAgICAgICAgICBleGNlcHQgRXhjZXB0aW9uOgogICAgICAgICAgICAgICAgcGFzcwoKICAgIGRlZiBzbmFwc2hvdChzZWxmKToKICAgICAgICBzZWxmLndhaXRfcmVhZHkoMC42KQogICAgICAgIHdpdGggc2VsZi5fbG9jazoKICAgICAgICAgICAgcmV0dXJuIGRpY3Qoc2VsZi5fY3B1KSwgZGljdChzZWxmLl9uZXQpCgoKIyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKIyBNb250YWdlbSBkbyBwYXlsb2FkCiMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACgoKZGVmIGJ1aWxkX21ldHJpY3Moc2FtcGxlciwgdG9wX2xpbWl0PTAsIGFsbF9pbnRlcmZhY2VzPUZhbHNlKToKICAgIGNwdSwgbmV0ID0gc2FtcGxlci5zbmFwc2hvdCgpCiAgICBpZiBub3QgYWxsX2ludGVyZmFjZXM6CiAgICAgICAgbmV0ID0ge2k6IHYgZm9yIGksIHYgaW4gbmV0Lml0ZW1zKCkgaWYgbm90IGkuc3RhcnRzd2l0aChWSVJUVUFMX0lGQUNFX1BSRUZJWEVTKX0KICAgIHVwdGltZSA9IGZsb2F0KChfcmVhZCgiL3Byb2MvdXB0aW1lIiwgIjAgMCIpLnNwbGl0KCkgb3IgWyIwIl0pWzBdKQogICAgbG9hZDEsIGxvYWQ1LCBsb2FkMTUgPSAoX3JlYWQoIi9wcm9jL2xvYWRhdmciLCAiMCAwIDAiKS5zcGxpdCgpICsgWyIwIiwgIjAiLCAiMCJdKVs6M10KICAgIGNvcmVzID0gb3MuY3B1X2NvdW50KCkgb3IgMQoKICAgIHBheWxvYWQgPSB7CiAgICAgICAgInNjaGVtYV92ZXJzaW9uIjogMSwKICAgICAgICAiYWdlbnRfdmVyc2lvbiI6IFZFUlNJT04sCiAgICAgICAgInRpbWVzdGFtcCI6IGludCh0aW1lLnRpbWUoKSksCiAgICAgICAgImhvc3QiOiB7CiAgICAgICAgICAgICJob3N0bmFtZSI6IHNvY2tldC5nZXRob3N0bmFtZSgpLAogICAgICAgICAgICAib3MiOiBvc19wcmV0dHlfbmFtZSgpLAogICAgICAgICAgICAia2VybmVsIjogcGxhdGZvcm0ucmVsZWFzZSgpLAogICAgICAgICAgICAiYXJjaGl0ZWN0dXJlIjogcGxhdGZvcm0ubWFjaGluZSgpLAogICAgICAgICAgICAidXB0aW1lX3NlY29uZHMiOiBpbnQodXB0aW1lKSwKICAgICAgICAgICAgImJvb3RfdGltZSI6IGludCh0aW1lLnRpbWUoKSAtIHVwdGltZSksCiAgICAgICAgfSwKICAgICAgICAiY3B1IjogewogICAgICAgICAgICAidXNhZ2VfcGVyY2VudCI6IGNwdVsidXNhZ2VfcGVyY2VudCJdLAogICAgICAgICAgICAiY29yZXMiOiBjb3JlcywKICAgICAgICAgICAgIm1vZGVsIjogY3B1X21vZGVsKCksCiAgICAgICAgICAgICJwZXJfY29yZV9wZXJjZW50IjogY3B1WyJwZXJfY29yZV9wZXJjZW50Il0sCiAgICAgICAgICAgICJicmVha2Rvd25fcGVyY2VudCI6IGNwdVsiYnJlYWtkb3duX3BlcmNlbnQiXSwKICAgICAgICAgICAgImxvYWRfYXZlcmFnZSI6IHsKICAgICAgICAgICAgICAgICIxbSI6IGZsb2F0KGxvYWQxKSwgIjVtIjogZmxvYXQobG9hZDUpLCAiMTVtIjogZmxvYXQobG9hZDE1KSwKICAgICAgICAgICAgICAgICIxbV9wZXJfY29yZSI6IHJvdW5kKGZsb2F0KGxvYWQxKSAvIGNvcmVzLCAzKSwKICAgICAgICAgICAgfSwKICAgICAgICB9LAogICAgICAgICJtZW1vcnkiOiBjb2xsZWN0X21lbW9yeSgpLAogICAgICAgICJkaXNrcyI6IGNvbGxlY3RfZGlza3MoKSwKICAgICAgICAibmV0d29yayI6IG5ldCwKICAgIH0KICAgIGlmIHRvcF9saW1pdDoKICAgICAgICBwYXlsb2FkWyJwcm9jZXNzZXMiXSA9IGNvbGxlY3RfdG9wX3Byb2Nlc3Nlcyh0b3BfbGltaXQpCiAgICByZXR1cm4gcGF5bG9hZAoKCmRlZiBvc19wcmV0dHlfbmFtZSgpOgogICAgZm9yIGxpbmUgaW4gX3JlYWQoIi9ldGMvb3MtcmVsZWFzZSIpLnNwbGl0bGluZXMoKToKICAgICAgICBpZiBsaW5lLnN0YXJ0c3dpdGgoIlBSRVRUWV9OQU1FPSIpOgogICAgICAgICAgICByZXR1cm4gbGluZS5zcGxpdCgiPSIsIDEpWzFdLnN0cmlwKCkuc3RyaXAoJyInKQogICAgcmV0dXJuIHBsYXRmb3JtLnBsYXRmb3JtKCkKCgpkZWYgdG9fcHJvbWV0aGV1cyhtKToKICAgIG91dCA9IFtdCiAgICBob3N0ID0gbVsiaG9zdCJdWyJob3N0bmFtZSJdCgogICAgZGVmIGVtaXQobmFtZSwgaGVscF90ZXh0LCBtdHlwZSwgc2FtcGxlcyk6CiAgICAgICAgb3V0LmFwcGVuZCgiIyBIRUxQICVzICVzIiAlIChuYW1lLCBoZWxwX3RleHQpKQogICAgICAgIG91dC5hcHBlbmQoIiMgVFlQRSAlcyAlcyIgJSAobmFtZSwgbXR5cGUpKQogICAgICAgIGZvciBsYWJlbHMsIHZhbHVlIGluIHNhbXBsZXM6CiAgICAgICAgICAgIGxhYmVsX3N0ciA9ICgieyVzfSIgJSBsYWJlbHMpIGlmIGxhYmVscyBlbHNlICIiCiAgICAgICAgICAgIG91dC5hcHBlbmQoIiVzJXMgJXMiICUgKG5hbWUsIGxhYmVsX3N0ciwgdmFsdWUpKQoKICAgIGJhc2UgPSAnaG9zdD0iJXMiJyAlIGVzYyhob3N0KQoKICAgIGVtaXQoInNydm1ldHJpY3NfdXAiLCAiMSBzZSBvIGFnZW50ZSByZXNwb25kZXUiLCAiZ2F1Z2UiLCBbKGJhc2UsIDEpXSkKICAgIGVtaXQoInNydm1ldHJpY3NfY3B1X3VzYWdlX3BlcmNlbnQiLCAiVXNvIHRvdGFsIGRlIENQVSBlbSBwb3JjZW50YWdlbSIsICJnYXVnZSIsCiAgICAgICAgIFsoYmFzZSwgbVsiY3B1Il1bInVzYWdlX3BlcmNlbnQiXSldKQogICAgZW1pdCgic3J2bWV0cmljc19jcHVfY29yZXMiLCAiTsO6bWVybyBkZSBuw7pjbGVvcyBsw7NnaWNvcyIsICJnYXVnZSIsCiAgICAgICAgIFsoYmFzZSwgbVsiY3B1Il1bImNvcmVzIl0pXSkKICAgIGVtaXQoInNydm1ldHJpY3NfY3B1X2NvcmVfdXNhZ2VfcGVyY2VudCIsICJVc28gZGUgQ1BVIHBvciBuw7pjbGVvIiwgImdhdWdlIiwKICAgICAgICAgWygnJXMsY29yZT0iJWQiJyAlIChiYXNlLCBpKSwgdikgZm9yIGksIHYgaW4gZW51bWVyYXRlKG1bImNwdSJdWyJwZXJfY29yZV9wZXJjZW50Il0pXSkKICAgIGVtaXQoInNydm1ldHJpY3NfbG9hZF9hdmVyYWdlIiwgIkxvYWQgYXZlcmFnZSIsICJnYXVnZSIsCiAgICAgICAgIFsoJyVzLHdpbmRvdz0iJXMiJyAlIChiYXNlLCBrKSwgdikgZm9yIGssIHYgaW4gbVsiY3B1Il1bImxvYWRfYXZlcmFnZSJdLml0ZW1zKCkKICAgICAgICAgIGlmIGsgIT0gIjFtX3Blcl9jb3JlIl0pCgogICAgbWVtID0gbVsibWVtb3J5Il0KICAgIGVtaXQoInNydm1ldHJpY3NfbWVtb3J5X2J5dGVzIiwgIk1lbcOzcmlhIFJBTSBlbSBieXRlcyIsICJnYXVnZSIsIFsKICAgICAgICAoJyVzLHN0YXRlPSJ0b3RhbCInICUgYmFzZSwgbWVtWyJ0b3RhbF9ieXRlcyJdKSwKICAgICAgICAoJyVzLHN0YXRlPSJ1c2VkIicgJSBiYXNlLCBtZW1bInVzZWRfYnl0ZXMiXSksCiAgICAgICAgKCclcyxzdGF0ZT0iZnJlZSInICUgYmFzZSwgbWVtWyJmcmVlX2J5dGVzIl0pLAogICAgICAgICgnJXMsc3RhdGU9ImF2YWlsYWJsZSInICUgYmFzZSwgbWVtWyJhdmFpbGFibGVfYnl0ZXMiXSksCiAgICAgICAgKCclcyxzdGF0ZT0iY2FjaGVkIicgJSBiYXNlLCBtZW1bImNhY2hlZF9ieXRlcyJdKSwKICAgICAgICAoJyVzLHN0YXRlPSJidWZmZXJzIicgJSBiYXNlLCBtZW1bImJ1ZmZlcnNfYnl0ZXMiXSksCiAgICBdKQogICAgZW1pdCgic3J2bWV0cmljc19tZW1vcnlfdXNlZF9wZXJjZW50IiwgIlVzbyBkZSBSQU0gZW0gcG9yY2VudGFnZW0iLCAiZ2F1Z2UiLAogICAgICAgICBbKGJhc2UsIG1lbVsidXNlZF9wZXJjZW50Il0pXSkKICAgIGVtaXQoInNydm1ldHJpY3Nfc3dhcF9ieXRlcyIsICJTd2FwIGVtIGJ5dGVzIiwgImdhdWdlIiwgWwogICAgICAgICgnJXMsc3RhdGU9InRvdGFsIicgJSBiYXNlLCBtZW1bInN3YXAiXVsidG90YWxfYnl0ZXMiXSksCiAgICAgICAgKCclcyxzdGF0ZT0idXNlZCInICUgYmFzZSwgbWVtWyJzd2FwIl1bInVzZWRfYnl0ZXMiXSksCiAgICBdKQogICAgZW1pdCgic3J2bWV0cmljc19zd2FwX3VzZWRfcGVyY2VudCIsICJVc28gZGUgc3dhcCBlbSBwb3JjZW50YWdlbSIsICJnYXVnZSIsCiAgICAgICAgIFsoYmFzZSwgbWVtWyJzd2FwIl1bInVzZWRfcGVyY2VudCJdKV0pCgogICAgZGlza19sYWJlbHMgPSBbKCclcyxtb3VudHBvaW50PSIlcyIsZGV2aWNlPSIlcyIsZnN0eXBlPSIlcyInCiAgICAgICAgICAgICAgICAgICAgJSAoYmFzZSwgZXNjKGRbIm1vdW50cG9pbnQiXSksIGVzYyhkWyJkZXZpY2UiXSksIGVzYyhkWyJmc3R5cGUiXSkpLCBkKQogICAgICAgICAgICAgICAgICAgZm9yIGQgaW4gbVsiZGlza3MiXV0KICAgIGVtaXQoInNydm1ldHJpY3NfZGlza190b3RhbF9ieXRlcyIsICJUYW1hbmhvIHRvdGFsIGRvIHNpc3RlbWEgZGUgYXJxdWl2b3MiLCAiZ2F1Z2UiLAogICAgICAgICBbKGwsIGRbInRvdGFsX2J5dGVzIl0pIGZvciBsLCBkIGluIGRpc2tfbGFiZWxzXSkKICAgIGVtaXQoInNydm1ldHJpY3NfZGlza191c2VkX2J5dGVzIiwgIkVzcGHDp28gdXNhZG8iLCAiZ2F1Z2UiLAogICAgICAgICBbKGwsIGRbInVzZWRfYnl0ZXMiXSkgZm9yIGwsIGQgaW4gZGlza19sYWJlbHNdKQogICAgZW1pdCgic3J2bWV0cmljc19kaXNrX2ZyZWVfYnl0ZXMiLCAiRXNwYcOnbyBsaXZyZSBkaXNwb27DrXZlbCIsICJnYXVnZSIsCiAgICAgICAgIFsobCwgZFsiZnJlZV9ieXRlcyJdKSBmb3IgbCwgZCBpbiBkaXNrX2xhYmVsc10pCiAgICBlbWl0KCJzcnZtZXRyaWNzX2Rpc2tfdXNlZF9wZXJjZW50IiwgIlVzbyBkbyBkaXNjbyBlbSBwb3JjZW50YWdlbSIsICJnYXVnZSIsCiAgICAgICAgIFsobCwgZFsidXNlZF9wZXJjZW50Il0pIGZvciBsLCBkIGluIGRpc2tfbGFiZWxzXSkKCiAgICBuZXRfbGFiZWxzID0gWygnJXMsaW50ZXJmYWNlPSIlcyInICUgKGJhc2UsIGVzYyhpKSksIHYpIGZvciBpLCB2IGluIG1bIm5ldHdvcmsiXS5pdGVtcygpXQogICAgZW1pdCgic3J2bWV0cmljc19uZXR3b3JrX3JlY2VpdmVfYnl0ZXNfdG90YWwiLCAiQnl0ZXMgcmVjZWJpZG9zIiwgImNvdW50ZXIiLAogICAgICAgICBbKGwsIHZbInJ4X2J5dGVzIl0pIGZvciBsLCB2IGluIG5ldF9sYWJlbHNdKQogICAgZW1pdCgic3J2bWV0cmljc19uZXR3b3JrX3RyYW5zbWl0X2J5dGVzX3RvdGFsIiwgIkJ5dGVzIHRyYW5zbWl0aWRvcyIsICJjb3VudGVyIiwKICAgICAgICAgWyhsLCB2WyJ0eF9ieXRlcyJdKSBmb3IgbCwgdiBpbiBuZXRfbGFiZWxzXSkKCiAgICBlbWl0KCJzcnZtZXRyaWNzX3VwdGltZV9zZWNvbmRzIiwgIlVwdGltZSBkbyBzZXJ2aWRvciIsICJnYXVnZSIsCiAgICAgICAgIFsoYmFzZSwgbVsiaG9zdCJdWyJ1cHRpbWVfc2Vjb25kcyJdKV0pCiAgICByZXR1cm4gIlxuIi5qb2luKG91dCkgKyAiXG4iCgoKZGVmIGVzYyh2YWx1ZSk6CiAgICByZXR1cm4gc3RyKHZhbHVlKS5yZXBsYWNlKCJcXCIsICJcXFxcIikucmVwbGFjZSgnIicsICdcXCInKS5yZXBsYWNlKCJcbiIsICIgIikKCgojIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAojIEhUVFAKIyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKCgpkZWYgbG9nKG1lc3NhZ2UpOgogICAgc3lzLnN0ZG91dC53cml0ZSgiJXNcbiIgJSBtZXNzYWdlKQogICAgc3lzLnN0ZG91dC5mbHVzaCgpCgoKY2xhc3MgVGhyb3R0bGU6CiAgICAiIiJCbG9xdWVpYSB1bSBJUCBwb3IgdW0gdGVtcG8gZGVwb2lzIGRlIHJlcGV0aWRhcyBmYWxoYXMgZGUgYXV0ZW50aWNhw6fDo28uIiIiCgogICAgZGVmIF9faW5pdF9fKHNlbGYsIG1heF9mYWlsdXJlcz0xMCwgd2luZG93PTYwLjAsIGJsb2NrX2Zvcj0zMDAuMCk6CiAgICAgICAgc2VsZi5tYXhfZmFpbHVyZXMgPSBtYXhfZmFpbHVyZXMKICAgICAgICBzZWxmLndpbmRvdyA9IHdpbmRvdwogICAgICAgIHNlbGYuYmxvY2tfZm9yID0gYmxvY2tfZm9yCiAgICAgICAgc2VsZi5fbG9jayA9IHRocmVhZGluZy5Mb2NrKCkKICAgICAgICBzZWxmLl9mYWlscyA9IHt9CiAgICAgICAgc2VsZi5fYmxvY2tlZCA9IHt9CgogICAgZGVmIGlzX2Jsb2NrZWQoc2VsZiwgaXApOgogICAgICAgIHdpdGggc2VsZi5fbG9jazoKICAgICAgICAgICAgdW50aWwgPSBzZWxmLl9ibG9ja2VkLmdldChpcCkKICAgICAgICAgICAgaWYgdW50aWwgYW5kIHVudGlsID4gdGltZS5tb25vdG9uaWMoKToKICAgICAgICAgICAgICAgIHJldHVybiBUcnVlCiAgICAgICAgICAgIGlmIHVudGlsOgogICAgICAgICAgICAgICAgc2VsZi5fYmxvY2tlZC5wb3AoaXAsIE5vbmUpCiAgICAgICAgICAgIHJldHVybiBGYWxzZQoKICAgIGRlZiByZWdpc3Rlcl9mYWlsdXJlKHNlbGYsIGlwKToKICAgICAgICBub3cgPSB0aW1lLm1vbm90b25pYygpCiAgICAgICAgd2l0aCBzZWxmLl9sb2NrOgogICAgICAgICAgICBidWNrZXQgPSBzZWxmLl9mYWlscy5zZXRkZWZhdWx0KGlwLCBkZXF1ZSgpKQogICAgICAgICAgICBidWNrZXQuYXBwZW5kKG5vdykKICAgICAgICAgICAgd2hpbGUgYnVja2V0IGFuZCBub3cgLSBidWNrZXRbMF0gPiBzZWxmLndpbmRvdzoKICAgICAgICAgICAgICAgIGJ1Y2tldC5wb3BsZWZ0KCkKICAgICAgICAgICAgaWYgbGVuKGJ1Y2tldCkgPj0gc2VsZi5tYXhfZmFpbHVyZXM6CiAgICAgICAgICAgICAgICBzZWxmLl9ibG9ja2VkW2lwXSA9IG5vdyArIHNlbGYuYmxvY2tfZm9yCiAgICAgICAgICAgICAgICBidWNrZXQuY2xlYXIoKQogICAgICAgICAgICAgICAgcmV0dXJuIFRydWUKICAgICAgICAgICAgcmV0dXJuIEZhbHNlCgogICAgZGVmIHJlZ2lzdGVyX3N1Y2Nlc3Moc2VsZiwgaXApOgogICAgICAgIHdpdGggc2VsZi5fbG9jazoKICAgICAgICAgICAgc2VsZi5fZmFpbHMucG9wKGlwLCBOb25lKQoKCmNsYXNzIEhhbmRsZXIoQmFzZUhUVFBSZXF1ZXN0SGFuZGxlcik6CiAgICBzZXJ2ZXJfdmVyc2lvbiA9ICJzcnZtZXRyaWNzLyVzIiAlIFZFUlNJT04KICAgIHN5c192ZXJzaW9uID0gIiIKICAgIHByb3RvY29sX3ZlcnNpb24gPSAiSFRUUC8xLjEiCgogICAgIyBhdHJpYnV0b3MgaW5qZXRhZG9zIHBlbG8gc2Vydmlkb3IKICAgIHRva2VuID0gIiIKICAgIHNhbXBsZXIgPSBOb25lCiAgICB0aHJvdHRsZSA9IE5vbmUKCiAgICAjIOKUgOKUgCBoZWxwZXJzIGRlIHJlc3Bvc3RhIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAogICAgZGVmIF9zZW5kKHNlbGYsIGNvZGUsIGJvZHksIGNvbnRlbnRfdHlwZT0iYXBwbGljYXRpb24vanNvbjsgY2hhcnNldD11dGYtOCIsIGV4dHJhPU5vbmUpOgogICAgICAgIGlmIGlzaW5zdGFuY2UoYm9keSwgc3RyKToKICAgICAgICAgICAgYm9keSA9IGJvZHkuZW5jb2RlKCJ1dGYtOCIpCiAgICAgICAgc2VsZi5zZW5kX3Jlc3BvbnNlKGNvZGUpCiAgICAgICAgc2VsZi5zZW5kX2hlYWRlcigiQ29udGVudC1UeXBlIiwgY29udGVudF90eXBlKQogICAgICAgIHNlbGYuc2VuZF9oZWFkZXIoIkNvbnRlbnQtTGVuZ3RoIiwgc3RyKGxlbihib2R5KSkpCiAgICAgICAgc2VsZi5zZW5kX2hlYWRlcigiQ2FjaGUtQ29udHJvbCIsICJuby1zdG9yZSIpCiAgICAgICAgc2VsZi5zZW5kX2hlYWRlcigiWC1Db250ZW50LVR5cGUtT3B0aW9ucyIsICJub3NuaWZmIikKICAgICAgICBmb3Iga2V5LCB2YWx1ZSBpbiAoZXh0cmEgb3Ige30pLml0ZW1zKCk6CiAgICAgICAgICAgIHNlbGYuc2VuZF9oZWFkZXIoa2V5LCB2YWx1ZSkKICAgICAgICBzZWxmLmVuZF9oZWFkZXJzKCkKICAgICAgICBpZiBzZWxmLmNvbW1hbmQgIT0gIkhFQUQiOgogICAgICAgICAgICBzZWxmLndmaWxlLndyaXRlKGJvZHkpCgogICAgZGVmIF9qc29uKHNlbGYsIGNvZGUsIG9iaiwgZXh0cmE9Tm9uZSk6CiAgICAgICAgc2VsZi5fc2VuZChjb2RlLCBqc29uLmR1bXBzKG9iaiwgZW5zdXJlX2FzY2lpPUZhbHNlLCBpbmRlbnQ9MikgKyAiXG4iLCBleHRyYT1leHRyYSkKCiAgICBkZWYgX2Vycm9yKHNlbGYsIGNvZGUsIG1lc3NhZ2UsIGV4dHJhPU5vbmUpOgogICAgICAgIHNlbGYuX2pzb24oY29kZSwgeyJlcnJvciI6IG1lc3NhZ2UsICJzdGF0dXMiOiBjb2RlfSwgZXh0cmE9ZXh0cmEpCgogICAgIyDilIDilIAgYXV0ZW50aWNhw6fDo28g4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiAgICBkZWYgX2NsaWVudF9pcChzZWxmKToKICAgICAgICByZXR1cm4gc2VsZi5jbGllbnRfYWRkcmVzc1swXSBpZiBzZWxmLmNsaWVudF9hZGRyZXNzIGVsc2UgIi0iCgogICAgZGVmIF9hdXRob3JpemVkKHNlbGYpOgogICAgICAgIHByb3ZpZGVkID0gIiIKICAgICAgICBoZWFkZXIgPSBzZWxmLmhlYWRlcnMuZ2V0KCJBdXRob3JpemF0aW9uIiwgIiIpCiAgICAgICAgaWYgaGVhZGVyLmxvd2VyKCkuc3RhcnRzd2l0aCgiYmVhcmVyICIpOgogICAgICAgICAgICBwcm92aWRlZCA9IGhlYWRlcls3Ol0uc3RyaXAoKQogICAgICAgIGVsaWYgc2VsZi5oZWFkZXJzLmdldCgiWC1BdXRoLVRva2VuIik6CiAgICAgICAgICAgIHByb3ZpZGVkID0gc2VsZi5oZWFkZXJzLmdldCgiWC1BdXRoLVRva2VuIikuc3RyaXAoKQogICAgICAgIHJldHVybiBib29sKHByb3ZpZGVkKSBhbmQgaG1hYy5jb21wYXJlX2RpZ2VzdChwcm92aWRlZCwgc2VsZi50b2tlbikKCiAgICAjIOKUgOKUgCByb3RhcyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKICAgIGRlZiBkb19HRVQoc2VsZik6CiAgICAgICAgc2VsZi5faGFuZGxlKCkKCiAgICBkZWYgZG9fSEVBRChzZWxmKToKICAgICAgICBzZWxmLl9oYW5kbGUoKQoKICAgIGRlZiBfaGFuZGxlKHNlbGYpOgogICAgICAgIHBhcnNlZCA9IHVybHBhcnNlKHNlbGYucGF0aCkKICAgICAgICByb3V0ZSA9IHBhcnNlZC5wYXRoLnJzdHJpcCgiLyIpIG9yICIvIgogICAgICAgIHF1ZXJ5ID0gcGFyc2VfcXMocGFyc2VkLnF1ZXJ5KQogICAgICAgIGlwID0gc2VsZi5fY2xpZW50X2lwKCkKCiAgICAgICAgaWYgcm91dGUgPT0gIi9oZWFsdGgiOgogICAgICAgICAgICByZXR1cm4gc2VsZi5fanNvbigyMDAsIHsic3RhdHVzIjogIm9rIiwgImFnZW50X3ZlcnNpb24iOiBWRVJTSU9OfSkKICAgICAgICBpZiByb3V0ZSA9PSAiLyI6CiAgICAgICAgICAgIHJldHVybiBzZWxmLl9qc29uKDIwMCwgewogICAgICAgICAgICAgICAgInNlcnZpY2UiOiAic3J2bWV0cmljcyIsCiAgICAgICAgICAgICAgICAiYWdlbnRfdmVyc2lvbiI6IFZFUlNJT04sCiAgICAgICAgICAgICAgICAiZW5kcG9pbnRzIjogWyIvbWV0cmljcyIsICIvbWV0cmljcy9wcm9tZXRoZXVzIiwgIi9oZWFsdGgiXSwKICAgICAgICAgICAgICAgICJhdXRoIjogIkF1dGhvcml6YXRpb246IEJlYXJlciA8dG9rZW4+IiwKICAgICAgICAgICAgfSkKCiAgICAgICAgaWYgcm91dGUgbm90IGluICgiL21ldHJpY3MiLCAiL21ldHJpY3MvcHJvbWV0aGV1cyIpOgogICAgICAgICAgICByZXR1cm4gc2VsZi5fZXJyb3IoNDA0LCAibm90IGZvdW5kIikKCiAgICAgICAgaWYgc2VsZi50aHJvdHRsZS5pc19ibG9ja2VkKGlwKToKICAgICAgICAgICAgcmV0dXJuIHNlbGYuX2Vycm9yKDQyOSwgInRvbyBtYW55IGZhaWxlZCBhdHRlbXB0cyIsIHsiUmV0cnktQWZ0ZXIiOiAiMzAwIn0pCgogICAgICAgIGlmIG5vdCBzZWxmLl9hdXRob3JpemVkKCk6CiAgICAgICAgICAgIGJsb2NrZWQgPSBzZWxmLnRocm90dGxlLnJlZ2lzdGVyX2ZhaWx1cmUoaXApCiAgICAgICAgICAgIGxvZygiYXV0aCBmYWlsZWQgZnJvbSAlcyVzIiAlIChpcCwgIiAoYmxvcXVlYWRvKSIgaWYgYmxvY2tlZCBlbHNlICIiKSkKICAgICAgICAgICAgcmV0dXJuIHNlbGYuX2Vycm9yKDQwMSwgInVuYXV0aG9yaXplZCIsIHsiV1dXLUF1dGhlbnRpY2F0ZSI6ICdCZWFyZXIgcmVhbG09InNydm1ldHJpY3MiJ30pCiAgICAgICAgc2VsZi50aHJvdHRsZS5yZWdpc3Rlcl9zdWNjZXNzKGlwKQoKICAgICAgICB0cnk6CiAgICAgICAgICAgIHRvcCA9IGludChxdWVyeS5nZXQoInRvcCIsIFsiMCJdKVswXSkKICAgICAgICBleGNlcHQgVmFsdWVFcnJvcjoKICAgICAgICAgICAgdG9wID0gMAogICAgICAgIHRvcCA9IG1heCgwLCBtaW4odG9wLCA1MCkpCiAgICAgICAgYWxsX2lmYWNlcyA9IHF1ZXJ5LmdldCgibmV0IiwgWyIiXSlbMF0ubG93ZXIoKSBpbiAoImFsbCIsICIxIiwgInRydWUiKQoKICAgICAgICB0cnk6CiAgICAgICAgICAgIG1ldHJpY3MgPSBidWlsZF9tZXRyaWNzKHNlbGYuc2FtcGxlciwgdG9wLCBhbGxfaWZhY2VzKQogICAgICAgIGV4Y2VwdCBFeGNlcHRpb24gYXMgZXhjOgogICAgICAgICAgICBsb2coImVycm8gYW8gY29sZXRhciBtw6l0cmljYXM6ICVzIiAlIGV4YykKICAgICAgICAgICAgcmV0dXJuIHNlbGYuX2Vycm9yKDUwMCwgImZhaWxlZCB0byBjb2xsZWN0IG1ldHJpY3MiKQoKICAgICAgICBpZiByb3V0ZSA9PSAiL21ldHJpY3MvcHJvbWV0aGV1cyI6CiAgICAgICAgICAgIHJldHVybiBzZWxmLl9zZW5kKDIwMCwgdG9fcHJvbWV0aGV1cyhtZXRyaWNzKSwgInRleHQvcGxhaW47IHZlcnNpb249MC4wLjQ7IGNoYXJzZXQ9dXRmLTgiKQogICAgICAgIHJldHVybiBzZWxmLl9qc29uKDIwMCwgbWV0cmljcykKCiAgICBkZWYgbG9nX21lc3NhZ2Uoc2VsZiwgZm10LCAqYXJncyk6ICAjIGpvdXJuYWxkIGrDoSBjYXJpbWJhIGRhdGEvaG9yYQogICAgICAgIGxvZygiJXMgJXMiICUgKHNlbGYuX2NsaWVudF9pcCgpLCBmbXQgJSBhcmdzKSkKCgpjbGFzcyBTZXJ2ZXIoVGhyZWFkaW5nSFRUUFNlcnZlcik6CiAgICBkYWVtb25fdGhyZWFkcyA9IFRydWUKICAgIGFsbG93X3JldXNlX2FkZHJlc3MgPSBUcnVlCiAgICByZXF1ZXN0X3F1ZXVlX3NpemUgPSA2NAoKCiMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACiMgQm9vdHN0cmFwCiMg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACgoKZGVmIGxvYWRfY29uZmlnKHBhdGgpOgogICAgdHJ5OgogICAgICAgIHdpdGggb3BlbihwYXRoLCAiciIsIGVuY29kaW5nPSJ1dGYtOCIpIGFzIGZoOgogICAgICAgICAgICBjZmcgPSBqc29uLmxvYWQoZmgpCiAgICBleGNlcHQgRmlsZU5vdEZvdW5kRXJyb3I6CiAgICAgICAgc3lzLmV4aXQoInNydm1ldHJpY3M6IGFycXVpdm8gZGUgY29uZmlndXJhw6fDo28gbsOjbyBlbmNvbnRyYWRvOiAlcyIgJSBwYXRoKQogICAgZXhjZXB0IChPU0Vycm9yLCBWYWx1ZUVycm9yKSBhcyBleGM6CiAgICAgICAgc3lzLmV4aXQoInNydm1ldHJpY3M6IGNvbmZpZ3VyYcOnw6NvIGludsOhbGlkYSAoJXMpOiAlcyIgJSAocGF0aCwgZXhjKSkKCiAgICB0b2tlbiA9IHN0cihjZmcuZ2V0KCJ0b2tlbiIsICIiKSkuc3RyaXAoKQogICAgaWYgbGVuKHRva2VuKSA8IDE2OgogICAgICAgIHN5cy5leGl0KCJzcnZtZXRyaWNzOiAndG9rZW4nIGF1c2VudGUgb3UgY3VydG8gZGVtYWlzIGVtICVzIiAlIHBhdGgpCiAgICByZXR1cm4gewogICAgICAgICJiaW5kIjogc3RyKGNmZy5nZXQoImJpbmQiLCAiMC4wLjAuMCIpKSwKICAgICAgICAicG9ydCI6IGludChjZmcuZ2V0KCJwb3J0IiwgOTg0NykpLAogICAgICAgICJ0b2tlbiI6IHRva2VuLAogICAgICAgICJzYW1wbGVfaW50ZXJ2YWwiOiBmbG9hdChjZmcuZ2V0KCJzYW1wbGVfaW50ZXJ2YWwiLCAyLjApKSwKICAgIH0KCgpkZWYgbWFpbigpOgogICAgY29uZmlnX3BhdGggPSBzeXMuYXJndlsxXSBpZiBsZW4oc3lzLmFyZ3YpID4gMSBlbHNlIG9zLmVudmlyb24uZ2V0KCJTUlZNRVRSSUNTX0NPTkZJRyIsIERFRkFVTFRfQ09ORklHKQogICAgY2ZnID0gbG9hZF9jb25maWcoY29uZmlnX3BhdGgpCgogICAgc2FtcGxlciA9IFNhbXBsZXIoY2ZnWyJzYW1wbGVfaW50ZXJ2YWwiXSkKICAgIHNhbXBsZXIuc3RhcnQoKQoKICAgIEhhbmRsZXIudG9rZW4gPSBjZmdbInRva2VuIl0KICAgIEhhbmRsZXIuc2FtcGxlciA9IHNhbXBsZXIKICAgIEhhbmRsZXIudGhyb3R0bGUgPSBUaHJvdHRsZSgpCgogICAgaWYgIjoiIGluIGNmZ1siYmluZCJdOgogICAgICAgIFNlcnZlci5hZGRyZXNzX2ZhbWlseSA9IHNvY2tldC5BRl9JTkVUNgoKICAgIGh0dHBkID0gU2VydmVyKChjZmdbImJpbmQiXSwgY2ZnWyJwb3J0Il0pLCBIYW5kbGVyKQogICAgbG9nKCJzcnZtZXRyaWNzICVzIG91dmluZG8gZW0gJXM6JWQiICUgKFZFUlNJT04sIGNmZ1siYmluZCJdLCBjZmdbInBvcnQiXSkpCiAgICB0cnk6CiAgICAgICAgaHR0cGQuc2VydmVfZm9yZXZlcigpCiAgICBleGNlcHQgS2V5Ym9hcmRJbnRlcnJ1cHQ6CiAgICAgICAgcGFzcwogICAgZmluYWxseToKICAgICAgICBodHRwZC5zZXJ2ZXJfY2xvc2UoKQogICAgICAgIGxvZygic3J2bWV0cmljcyBlbmNlcnJhZG8iKQoKCmlmIF9fbmFtZV9fID09ICJfX21haW5fXyI6CiAgICBtYWluKCkK"
LOGO_B64="Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ogo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Cjo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6OjoKOjo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ogo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Oi0tPSsrKiojIyUlJSUlJSMjIyoqKys9LS06Oi0tPSsrKysrPT0tOjo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Cjo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6LS06Oi0rKiMlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSo9Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6OjoKOjo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Oj0jJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlIz06Ojo6Ojo6Ojo6Ojo6Ojo6Ogo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6OislJSUlJSUlJSUlJSUlJSUlJSUlIyoqKys9PT09PSsrKiMjJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSM9Ojo6Ojo6Ojo6Ojo6Ojo6Cjo6Ojo6Ojo6Ojo6Ojo6Ojo6OjotLT0jJSUlJSUlJSUlJSUlJSMrOjo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Oj0rLTo6Ojo6Ojo6Oi0rIyUlJSUlJSUqOjo6Ojo6Ojo6Ojo6OjoKOjo6Ojo6Ojo6Ojo6Ojo6Oi0rJSUlJSUlJSUlJSo9JSUjKy06Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6PSMlJSUlJSUqLTo6Ojo6Ojo6Ojo6Ogo6Ojo6Ojo6Ojo6Ojo6Ojo9IyUlJSUlJSUlJSU9OiMjLTo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6OislJSUlJSUrOjo6Ojo6Ojo6Ojo6Cjo6Ojo6Ojo6Ojo6Ojo6OiolJSUlJSUlIyMjKzo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6OiolJSUlJSUtOjo6Ojo6Ojo6OjoKOjo6Ojo6Ojo6Ojo6Ojo6KiUlJSUlJSUjLTo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6LSUlJSUlJT06Ojo6Ojo6Ojo6Ogo6Ojo6Ojo6Ojo6Ojo6Oi0qJSUlJSUlJSotOjo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6OjotJSUlJSUlPTo6Ojo6Ojo6Ojo6Cjo6Ojo6Ojo6Ojo6Ojo6KiUlJSUlJSo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6OiolJSUlJSUtOjo6Ojo6Ojo6OjoKOjo6Ojo6Ojo6Ojo6Oj0lJSUlJSUqOjo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6OjorJSUlJSUlKzo6Ojo6Ojo6Ojo6Ogo6Ojo6Ojo6Ojo6Ojo6KyUlJSUlJTo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6LT0jIyo6Ojo6Ojo6KiUlJSUlJSotOjo6Ojo6Ojo6Ojo6Cjo6Ojo6Ojo6Ojo6OjoqJSUlJSUjOjo6Ojo6Ojo6Ojo6LSMlJSUlJSUlJSUlJSUlIyMjKioqKiojIyMlJSUlJSUlJSVAQEBAJT06Ojo6KyUlJSUlJSUqLTo6Ojo6Ojo6Ojo6OjoKOjo6Ojo6Ojo6Ojo6OiolJSUlJSo6Ojo6Ojo6Ojo6Ojo9QEBAJSUlIyMjIyMjJSUlQEBAQEBAQEBAQEBAQCUlJSUjIyMjJSUlIzo6OjoqJSUlJSUlKzo6Ojo6Ojo6Ojo6Ojo6Ogo6Ojo6Ojo6Ojo6Ojo6KiUlJSUlIzo6Ojo6Ojo6Ojo6Oi0lIystOjo6Oi06Ojo6OisjJUBAQEBAQEBAQEAlKy06Ojo6Ojo6OjorLTo6OiolJSUlJSo6Ojo6Ojo6Ojo6Ojo6Ojo6Cjo6Ojo6Ojo6Ojo6OjorJSUlJSUlOjo6Ojo6Ojo6Ojo6LT06Oj0qJSVAQEBAJSM9OjotJUBAQEBAQEAjLTo6PSolJUBAQCUqKzo6Ojo9IyUlJSUlIy06Ojo6Ojo6Ojo6Ojo6OjoKOjo6Ojo6Ojo6Ojo6OislJSUlJSUtOjo6Ojo6Ojo6Ojo6Oj0lQEAlKi0tOi0tKyUlKzo6K0BAQEBAKzo6KyUqLS06LS0qJUAlJSs6OislJSUlJSUlJSUrLTo6Ojo6Ojo6Ojo6Ogo6Ojo6Ojo6Ojo6OislJSUlJSUlJSo6Ojo6Ojo6Ojo6Ojo9JUBAIy06OjotKiUjLSMlKjo6KkBAQCotOiMlOjo6Oi0jJSo6JUBAJSo6OiolJSUlJSUlJSUlKzo6Ojo6Ojo6Ojo6Cjo6Ojo6Ojo6OjolJSUlJSUlJSMqLTo6Ojo6Ojo6Ojo6LSVAQCU9Ojo6LSMlJSUlPSNAOjo6Ojo6OjotJTo6Ojo9IyUlJSU6JUBAJT06Ojo6KyMlJSUlJSUlJTo6Ojo6Ojo6OjoKOjo6Ojo6Ojo6JSUlJSUlJSotOjotPT0tOjo6Ojo6LTotJUBAIy06Ojo6OjorQCU9I0AtOjorKistOj0lOjo6Ojo6OiolQC0lQEAlPTotPS06OjorJSUlJSUlJTo6Ojo6Ojo6Ogo6Ojo6Ojo6OiolJSUlJSU9OjorJUBAQCUlKi06Oi0jOjoqQEAlPTo6Ojo6OitAJSslJTo6KyUlIz06OiU6Ojo6Ojo6KiVAPSVAQCotOiolQCUrOjo9JSUlJSUlKjo6Ojo6Ojo6Cjo6Ojo6OjotIyUlJSUlKzo6KiUqIystKiUlKi06OiorOi0qQCUjLTo6OjorJSUjJSU6Oi0lQEArLS06Oio6Ojo6OiolQCMlQCUjLTo9PT0jKiUqOjorJSUlJSUlLTo6Ojo6OjoKOjo6Ojo6Oi0jJSUlJSUrOi0jJSVAJSUrLSolKzotJSUqOjotI0AlJSMlJSVAQCU9OjorJUBAQCUlKjo6Oi0qIyUlJUBAQEAjLTo6OjoqJSVAJSM6Oj0lJSUlJSU9Ojo6Ojo6Ogo6Ojo6Ojo6LSMlJSUlJSs6OiolQEAjPSMjKyNAQEBAQEAlKzo6Ojo9KioqPTo6Ojo9JUBAQEBAQEAlLS0qLTo6Ojo6Ojo6OjotPS06PSVAQEAlIzo6KyUlJSUlJS06Ojo6Ojo6Cjo6Ojo6Ojo6IyUlJSUlIy06LSMlQCMjQEAlJUBAQEBAQEBAJSUqPT09PT09PSolJUBAQEAlIysrLT0rJUAlJSUqKz09PSolJUAjLTo9JUBAQCMtOj0jJSUlJSUjOjo6Ojo6OjoKOjo6Ojo6OjotJSUlJSUlIy06OislJSUlQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAJSVAQEBAQEBAQEBAQEBAQEBAQEBAQCMtOj0lQCUrOjotIyUlJSUlJS06Ojo6Ojo6Ogo6Ojo6Ojo6Ojo9JSUlJSUlJSo6OjotPT09PT0jQEBAQEBAQEBAQEBAQEBAJSsrJSUlJSUlJSUlJSUlJSUlJSMlQEBAQEBAQEBAIy06Oi06Ojo6KiUlJSUlJSU9Ojo6Ojo6Ojo6Cjo6Ojo6Ojo6OjotJSUlJSUlJSUlIy06Ojo6Oj0lQEBAQEBAQEBAQEBAJSUqJSMtOjotLT0qIyUlJSUlJSUlJSUlQEBAQEBAQEAjOjo6Oj0jJSUlJSUlJSUlLTo6Ojo6Ojo6OjoKOjo6Ojo6Ojo6Ojo6KyUlJSUlJSUlJSUlJSs6OiMlQEBAQEBAQEBAQEBAQEBAQEBAQEAlJSUlJSUlJSUlQEBAQEBAQEBAQEBAJSs6OiolJSUlJSUlJSUlKzo6Ojo6Ojo6Ojo6Ogo6Ojo6Ojo6Ojo6Ojo6Oj0jJSUlJSUlJSUlIy06OiNAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQCUjOjorJSUlJSUlJSUjPTo6Ojo6Ojo6Ojo6Ojo6Cjo6Ojo6Ojo6Ojo6Ojo6Ojo6PSsqJSUlJSUlIy06OipAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAIzo6PSMlJSUlJSM9Ojo6Ojo6Ojo6Ojo6Ojo6OjoKOjo6Ojo6Ojo6Ojo6Ojo6Ojo6OjotJSUlJSUlJT06Oi0qQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQCs6Oi0jJSUlJSUlLTo6Ojo6Ojo6Ojo6Ojo6Ojo6Ogo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6OjotJSUlJSUlJSUtOjo6KyVAQEBAQEBAQEBAQEBAQEBAQCUlJUBAQEBAQEBAQEBAQCs6OjorJSUlJSUlJS06Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Cjo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6KiUlJSUlJSUlKy06Ojo9IyUlQEBAQCUrKyMlJSMrOjo6KyMjIz0tIyUlKi06OjorJSUlJSUlJSM6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6OjoKOjo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6PSMlJSUlJSUlJSUrPTo6OjotPSojIz06Ojo6Ojo6Ojo6Ojo6OjotOjo6Oj0jJSUlJSUlJSUrOjo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ogo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Oi0qJSUlJSUlJSUlJSMqKz06Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Oi09KiMlJSUlJSUlJSUqLTo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Cjo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6PSMlJSUlJSUlJSUlJSUlJSMjKiorOi06Ojo6Ojo9IyUlJSUlJSUlJSUlJSUrOjo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6OjoKOjo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6OjotKiUlJSUlJSUlJSUlJSUlJSUlJSUqKyolJSUlJSUlJSUlJSUlJSs6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ogo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6OjotKyMjJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSUlJSMrLTo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Cjo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Oi09KyoqIyUlJSUlJSUlJSUlJSMqLTo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6OjoKOjo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo9KyojJSMqKy06Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ogo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Cjo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6OjoKOjo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ogo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Ojo6Cg=="

# ── Caminhos ────────────────────────────────────────────────────────────────
SERVICE_NAME="srvmetrics"
SERVICE_USER="srvmetrics"
INSTALL_DIR="/opt/srvmetrics"
CONFIG_DIR="/etc/srvmetrics"
CONFIG_FILE="${CONFIG_DIR}/config.json"
UNIT_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
CLI_PATH="/usr/local/bin/srvmetrics"
CREDENTIALS_FILE="/root/srvmetrics-acesso.txt"

# ── Opções (env vars ou flags) ──────────────────────────────────────────────
PORT="${SRVMETRICS_PORT:-9847}"
PORT_EXPLICIT=0
[[ -n "${SRVMETRICS_PORT:-}" ]] && PORT_EXPLICIT=1
ASSUME_YES="${SRVMETRICS_YES:-0}"
BIND="${SRVMETRICS_BIND:-0.0.0.0}"
TOKEN="${SRVMETRICS_TOKEN:-}"
ALLOW_FROM="${SRVMETRICS_ALLOW_FROM:-}"   # ex.: 203.0.113.10 ou 10.0.0.0/8
CONFIGURE_FIREWALL=1
REGEN_TOKEN=0
NEEDS_NET_BIND_CAP=0
ACTION="install"

# ── Estilo ──────────────────────────────────────────────────────────────────
if [[ -t 1 && -z "${NO_COLOR:-}" && "${TERM:-dumb}" != "dumb" ]]; then
  BOLD=$'\033[1m'; DIM=$'\033[2m'; RESET=$'\033[0m'
  RED=$'\033[31m'; GREEN=$'\033[32m'; YELLOW=$'\033[33m'
  BLUE=$'\033[34m'; CYAN=$'\033[36m'; GREY=$'\033[90m'
else
  BOLD=""; DIM=""; RESET=""; RED=""; GREEN=""; YELLOW=""; BLUE=""; CYAN=""; GREY=""
fi

STEP=0
TOTAL_STEPS=8
LOGO_WIDTH=0
CURRENT_STEP_LABEL=""

RULE="────────────────────────────────────────────────────────────────"

rule() { printf '  %s%s%s\n' "${1:-$GREY}" "$RULE" "$RESET"; }

term_size() {  # imprime "colunas linhas"
  local cols="" rows="" size=""
  if command -v tput >/dev/null 2>&1; then
    cols="$(tput cols 2>/dev/null || true)"
    rows="$(tput lines 2>/dev/null || true)"
  fi
  if ! [[ "$cols" =~ ^[0-9]+$ ]]; then
    size="$(stty size </dev/tty 2>/dev/null || true)"
    rows="${size%% *}"; cols="${size##* }"
  fi
  [[ "$cols" =~ ^[0-9]+$ ]] || cols="${COLUMNS:-80}"
  [[ "$rows" =~ ^[0-9]+$ ]] || rows="${LINES:-24}"
  printf '%s %s' "$cols" "$rows"
}

# Desenha a arte ASCII, reduzida pela metade quando o terminal é estreito
# ou baixo demais, e omitida em terminais muito pequenos.
print_logo() {
  local cols rows art width
  read -r cols rows <<< "$(term_size)"

  (( cols < 46 )) && { LOGO_WIDTH=0; return; }

  art="$(printf '%s' "$LOGO_B64" | base64 -d 2>/dev/null || true)"
  [[ -n "$art" ]] || { LOGO_WIDTH=0; return; }
  # descarta as linhas de fundo puro no topo e no rodapé
  art="$(printf '%s\n' "$art" | sed -e '/[^:]/,$!d' | tac | sed -e '/[^:]/,$!d' | tac)"

  if (( cols >= 102 && rows >= 46 )); then
    width=100
  else
    art="$(printf '%s\n' "$art" | sed -n '1~2p' | sed 's/\(.\)./\1/g')"
    width=50
  fi

  if [[ -n "$CYAN" ]]; then
    local dim_on=$'\033[2m' dim_off=$'\033[22m'
    printf '%s' "$CYAN"
    printf '%s\n' "$art" | sed "s/:\{1,\}/${dim_on}&${dim_off}/g"
    printf '%s' "$RESET"
  else
    printf '%s\n' "$art"
  fi
  LOGO_WIDTH="$width"
}

# Centraliza um texto sob a arte (ou na margem padrão, se não houver arte).
centered() {
  local text="$1" pad=2
  (( LOGO_WIDTH > 0 )) && pad=$(( (LOGO_WIDTH - ${#text}) / 2 ))
  (( pad < 2 )) && pad=2
  printf '%*s' "$pad" ''
}

banner() {
  printf '\n'
  print_logo
  printf '\n'
  printf '%s%sServer Monitor%s %sV%s%s\n' "$(centered "Server Monitor V${VERSION%.*}")" \
         "$BOLD" "$RESET" "$BOLD$CYAN" "${VERSION%.*}" "$RESET"
  printf '\n'
  printf '  %ssrvmetrics%s %s· CPU · RAM · Disco · Rede — instalação em Ubuntu/Debian%s\n' \
         "$CYAN" "$RESET" "$DIM" "$RESET"
  rule
  printf '\n'
}

step()  { STEP=$((STEP + 1)); CURRENT_STEP_LABEL="$1"
          printf '  %s[%d/%d]%s %s%s%s\n' "$GREY" "$STEP" "$TOTAL_STEPS" "$RESET" "$BOLD" "$1" "$RESET"; }
info()  { printf '        %s·%s %s\n' "$GREY" "$RESET" "$1"; }
ok()    { printf '        %s✔%s %s\n' "$GREEN" "$RESET" "$1"; }
warn()  { printf '        %s!%s %s\n' "$YELLOW" "$RESET" "$1"; }
skip()  { printf '        %s–%s %s%s%s\n' "$GREY" "$RESET" "$DIM" "$1" "$RESET"; }

die() {
  printf '\n  %s✖ Falha%s%s\n' "$RED$BOLD" "$RESET" "${CURRENT_STEP_LABEL:+ durante: $CURRENT_STEP_LABEL}"
  printf '    %s\n\n' "$1"
  exit "${2:-1}"
}

on_error() {
  local code=$?
  printf '\n  %s✖ O instalador foi interrompido (código %d)%s%s\n\n' \
         "$RED$BOLD" "$code" "$RESET" "${CURRENT_STEP_LABEL:+ em: $CURRENT_STEP_LABEL}"
  exit "$code"
}
trap on_error ERR

# Executa um comando mostrando um spinner; em caso de erro imprime a saída.
spin() {
  local label="$1"; shift
  local tmp; tmp="$(mktemp)"
  if [[ ! -t 1 ]]; then
    if "$@" >"$tmp" 2>&1; then ok "$label"; rm -f "$tmp"; return 0; fi
    printf '%s\n' "$(sed 's/^/          /' "$tmp")"; rm -f "$tmp"; return 1
  fi
  "$@" >"$tmp" 2>&1 &
  local pid=$! frames='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏' i=0
  while kill -0 "$pid" 2>/dev/null; do
    i=$(((i + 1) % 10))
    printf '\r        %s%s%s %s' "$CYAN" "${frames:$i:1}" "$RESET" "$label"
    sleep 0.08
  done
  local rc=0; wait "$pid" || rc=$?
  printf '\r\033[K'
  if [[ $rc -eq 0 ]]; then
    ok "$label"; rm -f "$tmp"; return 0
  fi
  printf '        %s✖%s %s\n' "$RED" "$RESET" "$label"
  sed 's/^/          /' "$tmp"; rm -f "$tmp"; return "$rc"
}

# GET simples: usa curl se existir, senão cai para o python3 (que é requisito).
http_get() {
  local url="$1" auth="${2:-}"
  if command -v curl >/dev/null 2>&1; then
    if [[ -n "$auth" ]]; then
      curl -fsS --max-time 5 -H "Authorization: Bearer ${auth}" "$url" 2>/dev/null
    else
      curl -fsS --max-time 5 "$url" 2>/dev/null
    fi
    return
  fi
  python3 - "$url" "$auth" <<'PYGET'
import sys, urllib.request
url, token = sys.argv[1], sys.argv[2]
req = urllib.request.Request(url)
if token:
    req.add_header("Authorization", "Bearer " + token)
try:
    with urllib.request.urlopen(req, timeout=5) as resp:
        sys.stdout.write(resp.read().decode("utf-8", "replace"))
except Exception:
    sys.exit(1)
PYGET
}

usage() {
  cat <<USAGE
srvmetrics ${VERSION} — instalador do agente de métricas

  --port <n>          Porta pública do agente (pula o menu de seleção)
  -y, --yes           Não pergunta nada: usa a primeira porta livre sugerida
  --bind <addr>       Endereço de escuta (padrão: 0.0.0.0)
  --token <str>       Usa um token específico em vez de gerar um
  --allow <cidr>      Libera no firewall apenas esta origem (padrão: qualquer)
  --regen-token       Gera um token novo mesmo em reinstalação
  --no-firewall       Não mexe no UFW
  --uninstall         Remove completamente o agente
  -h, --help          Esta ajuda

Variáveis de ambiente equivalentes: SRVMETRICS_PORT, SRVMETRICS_BIND,
SRVMETRICS_TOKEN, SRVMETRICS_ALLOW_FROM.
USAGE
}

parse_args() {
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --port)         PORT="${2:?--port exige um valor}"; PORT_EXPLICIT=1; shift 2 ;;
      --bind)         BIND="${2:?--bind exige um valor}"; shift 2 ;;
      --token)        TOKEN="${2:?--token exige um valor}"; shift 2 ;;
      --allow)        ALLOW_FROM="${2:?--allow exige um valor}"; shift 2 ;;
      --regen-token)  REGEN_TOKEN=1; shift ;;
      -y|--yes)       ASSUME_YES=1; shift ;;
      --no-firewall)  CONFIGURE_FIREWALL=0; shift ;;
      --uninstall)    ACTION="uninstall"; shift ;;
      -h|--help)      usage; exit 0 ;;
      *)              usage; die "opção desconhecida: $1" 2 ;;
    esac
  done
}

# ── Etapas ──────────────────────────────────────────────────────────────────

check_system() {
  step "Verificando requisitos do sistema"

  [[ $EUID -eq 0 ]] || die "execute como root. Ex.: curl -fsSL <url> | sudo bash"

  local os_name="desconhecido"
  if [[ -r /etc/os-release ]]; then
    # shellcheck disable=SC1091
    os_name="$(. /etc/os-release && printf '%s' "${PRETTY_NAME:-$ID}")"
    local id_like; id_like="$(. /etc/os-release && printf '%s %s' "${ID:-}" "${ID_LIKE:-}")"
    if [[ "$id_like" != *ubuntu* && "$id_like" != *debian* ]]; then
      warn "distribuição não testada ($os_name) — seguindo mesmo assim"
    fi
  fi
  info "Sistema: ${os_name} · kernel $(uname -r) · $(uname -m)"

  command -v systemctl >/dev/null 2>&1 || die "systemd não encontrado; este instalador depende dele"

  if ! command -v python3 >/dev/null 2>&1; then
    warn "python3 não encontrado — instalando via apt"
    spin "python3 instalado" bash -c 'export DEBIAN_FRONTEND=noninteractive; apt-get update -qq && apt-get install -y -qq python3' \
      || die "não foi possível instalar o python3"
  fi
  local pyver; pyver="$(python3 -c 'import sys; print("%d.%d" % sys.version_info[:2])')"
  [[ "$(printf '%s\n3.8\n' "$pyver" | sort -V | head -1)" == "3.8" ]] \
    || die "python3 ${pyver} é antigo demais (mínimo: 3.8)"
  info "Python ${pyver} · systemd $(systemctl --version | head -1 | awk '{print $2}')"
  ok "Requisitos atendidos"
}

# Portas sugeridas ao usuário, na ordem de preferência.
SUGGESTED_PORTS=(9847 9100 9101 9256 8899 9999 19999 7799 8123 9843)

join_list() { local out; out="$(printf '%s, ' "$@")"; printf '%s' "${out%, }"; }

# 0 = livre, 1 = em uso.
port_free() {
  local p="$1"
  if command -v ss >/dev/null 2>&1; then
    ss -ltnH "sport = :${p}" 2>/dev/null | grep -q . && return 1
    return 0
  fi
  python3 - "$p" <<'PYPORT'
import socket, sys
sock = socket.socket()
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
try:
    sock.bind(("0.0.0.0", int(sys.argv[1])))
except OSError:
    sys.exit(1)
finally:
    sock.close()
PYPORT
}

# Existe um terminal para conversar com o usuário? (curl | bash ocupa o stdin)
has_tty() {
  [[ "$ASSUME_YES" == "1" ]] && return 1
  [[ -r /dev/tty && -w /dev/tty ]] || return 1
  { exec 3<>/dev/tty; } 2>/dev/null || return 1
  return 0
}

ask() {
  local prompt="$1" reply=""
  printf '%s' "$prompt" >/dev/tty
  IFS= read -r reply <&3 || reply=""
  printf '%s' "$reply"
}

# Descobre o IP público real; cai para o IP da rota padrão se não houver saída.
detect_ip() {
  local ip=""
  local url
  for url in https://api.ipify.org https://ifconfig.me/ip https://icanhazip.com; do
    ip="$(http_get "$url" 2>/dev/null | tr -d '[:space:]')"
    if [[ "$ip" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]]; then
      printf '%s' "$ip"
      return 0
    fi
  done
  printf '%s' "$(detect_local_ip)"
}

# IP da interface que sai para a internet (pode ser privado, em NAT).
detect_local_ip() {
  local ip=""
  if command -v ip >/dev/null 2>&1; then
    ip="$(ip route get 1.1.1.1 2>/dev/null | sed -n 's/.*[[:space:]]src[[:space:]]\+\([0-9.]\+\).*/\1/p' | head -1 || true)"
  fi
  [[ -n "$ip" ]] || ip="$(hostname -I 2>/dev/null | awk '{print $1}' || true)"
  printf '%s' "$ip"
}

# Menu interativo de portas livres.
choose_port() {
  local free=() busy=() p
  for p in "${SUGGESTED_PORTS[@]}"; do
    if port_free "$p"; then free+=("$p"); else busy+=("$p"); fi
    [[ ${#free[@]} -ge 6 ]] && break
  done

  [[ ${#free[@]} -gt 0 ]] || die "nenhuma das portas sugeridas está livre. Use --port <n>"
  if [[ ${#busy[@]} -gt 0 ]]; then
    info "já em uso neste servidor: $(join_list "${busy[@]}")"
  fi

  if ! has_tty; then
    PORT="${free[0]}"
    info "portas livres: $(join_list "${free[@]}")"
    info "sem terminal interativo — usando a primeira ($PORT). Use --port <n> para escolher."
    return
  fi

  printf '\n        %sPortas livres neste servidor:%s\n\n' "$BOLD" "$RESET"
  local i=1
  for p in "${free[@]}"; do
    if [[ $i -eq 1 ]]; then
      printf '          %s%d)%s %s%s%s  %s← recomendada%s\n' "$CYAN" "$i" "$RESET" "$BOLD" "$p" "$RESET" "$GREEN" "$RESET"
    else
      printf '          %s%d)%s %s\n' "$CYAN" "$i" "$RESET" "$p"
    fi
    i=$((i + 1))
  done
  printf '          %s%d)%s %sdigitar outra porta%s\n\n' "$CYAN" "$i" "$RESET" "$DIM" "$RESET"

  local choice attempts=0
  while :; do
    attempts=$((attempts + 1))
    [[ $attempts -gt 5 ]] && die "escolha de porta não concluída"
    choice="$(ask "        Escolha [1]: ")"
    choice="${choice:-1}"

    if [[ "$choice" =~ ^[0-9]+$ ]] && [[ "$choice" -ge 1 && "$choice" -lt $i ]]; then
      PORT="${free[$((choice - 1))]}"
      break
    fi
    if [[ "$choice" == "$i" ]]; then
      local custom
      custom="$(ask "        Digite a porta (1-65535): ")"
      if ! [[ "$custom" =~ ^[0-9]+$ ]] || [[ "$custom" -lt 1 || "$custom" -gt 65535 ]]; then
        printf '        %s!%s porta inválida: %s\n' "$YELLOW" "$RESET" "$custom"
        continue
      fi
      if ! port_free "$custom"; then
        printf '        %s!%s a porta %s já está em uso — escolha outra\n' "$YELLOW" "$RESET" "$custom"
        continue
      fi
      PORT="$custom"
      break
    fi
    printf '        %s!%s opção inválida\n' "$YELLOW" "$RESET"
  done
  printf '\n'
}

resolve_settings() {
  step "Escolhendo a porta e gerando a credencial"

  [[ "$PORT" =~ ^[0-9]+$ && "$PORT" -ge 1 && "$PORT" -le 65535 ]] \
    || die "porta inválida: ${PORT}"

  local reinstall=0
  if [[ -f "$CONFIG_FILE" ]]; then
    reinstall=1
    local current_port
    current_port="$(read_config_key port)"
    if [[ $PORT_EXPLICIT -eq 0 && -n "$current_port" ]]; then
      PORT="$current_port"
      info "instalação existente — mantendo a porta ${PORT} (use --port para mudar)"
    fi
    if [[ -z "$TOKEN" && $REGEN_TOKEN -eq 0 ]]; then
      TOKEN="$(read_config_key token)"
      [[ -n "$TOKEN" ]] && info "token existente preservado"
    fi
  fi

  if [[ $reinstall -eq 0 && $PORT_EXPLICIT -eq 0 ]]; then
    choose_port
  elif [[ $reinstall -eq 0 ]] && ! port_free "$PORT"; then
    die "a porta ${PORT} já está em uso. Escolha outra com --port <n>"
  fi

  if [[ -z "$TOKEN" ]]; then
    if command -v openssl >/dev/null 2>&1; then
      TOKEN="$(openssl rand -hex 32)"
    else
      TOKEN="$(python3 -c 'import secrets; print(secrets.token_hex(32))')"
    fi
    info "token de acesso gerado (256 bits)"
  fi
  [[ ${#TOKEN} -ge 16 ]] || die "o token precisa ter ao menos 16 caracteres"

  if [[ "$PORT" -lt 1024 ]]; then
    NEEDS_NET_BIND_CAP=1
    warn "porta privilegiada (<1024) — o serviço receberá CAP_NET_BIND_SERVICE"
  fi
  ok "Porta ${PORT} · escuta em ${BIND}"
}

read_config_key() {
  python3 -c 'import json,sys
try:
    print(json.load(open("/etc/srvmetrics/config.json")).get(sys.argv[1], ""))
except Exception:
    print("")' "$1" 2>/dev/null || true
}

create_user() {
  step "Criando usuário de serviço e diretórios"
  if id -u "$SERVICE_USER" >/dev/null 2>&1; then
    skip "usuário '${SERVICE_USER}' já existe"
  else
    useradd --system --no-create-home --shell /usr/sbin/nologin "$SERVICE_USER"
    ok "usuário de sistema '${SERVICE_USER}' criado"
  fi
  install -d -m 0755 -o root -g root "$INSTALL_DIR"
  install -d -m 0750 -o root -g "$SERVICE_USER" "$CONFIG_DIR"
  ok "diretórios ${INSTALL_DIR} e ${CONFIG_DIR}"
}

install_agent() {
  step "Instalando o agente"
  local tmp; tmp="$(mktemp)"
  printf '%s' "$AGENT_PAYLOAD_B64" | base64 -d > "$tmp" || die "payload do agente corrompido"
  python3 -c 'import ast,sys; ast.parse(open(sys.argv[1]).read())' "$tmp" \
    || die "o agente embutido não é um Python válido"
  install -m 0755 -o root -g root "$tmp" "${INSTALL_DIR}/agent.py"
  rm -f "$tmp"
  ok "agente em ${INSTALL_DIR}/agent.py ($(wc -c < "${INSTALL_DIR}/agent.py") bytes)"

  umask 077
  cat > "$CONFIG_FILE" <<JSON
{
  "bind": "${BIND}",
  "port": ${PORT},
  "token": "${TOKEN}",
  "sample_interval": 2.0
}
JSON
  chown root:"$SERVICE_USER" "$CONFIG_FILE"
  chmod 0640 "$CONFIG_FILE"
  ok "configuração em ${CONFIG_FILE} (somente root e ${SERVICE_USER} leem)"

  install_cli
  ok "utilitário de gerenciamento em ${CLI_PATH}"
}

install_cli() {
  cat > "$CLI_PATH" <<'CLIEOF'
#!/usr/bin/env bash
# srvmetrics — utilitário de gerenciamento do agente de métricas
set -euo pipefail
CONFIG="/etc/srvmetrics/config.json"

[[ -r "$CONFIG" ]] || { echo "srvmetrics: não foi possível ler ${CONFIG} (rode como root)" >&2; exit 1; }
read_cfg() { python3 -c 'import json,sys; print(json.load(open("/etc/srvmetrics/config.json")).get(sys.argv[1],""))' "$1"; }

# HTTP sem depender de curl — o python3 já é requisito do agente.
http_get() {
  python3 - "$1" "${2:-}" <<'PYGET'
import sys, urllib.request
req = urllib.request.Request(sys.argv[1])
if sys.argv[2]:
    req.add_header("Authorization", "Bearer " + sys.argv[2])
try:
    with urllib.request.urlopen(req, timeout=8) as resp:
        sys.stdout.write(resp.read().decode("utf-8", "replace"))
except Exception as exc:
    sys.stderr.write("erro: %s\n" % exc)
    sys.exit(1)
PYGET
}

local_metrics() { http_get "http://127.0.0.1:$(read_cfg port)/metrics?top=5" "$(read_cfg token)"; }

case "${1:-help}" in
  status)  systemctl status srvmetrics --no-pager ;;
  start)   systemctl start srvmetrics && echo "iniciado" ;;
  stop)    systemctl stop srvmetrics && echo "parado" ;;
  restart) systemctl restart srvmetrics && echo "reiniciado" ;;
  logs)    journalctl -u srvmetrics -n "${2:-50}" --no-pager ;;
  token)   read_cfg token ;;
  port)    read_cfg port ;;
  test|json) local_metrics ;;
  stats)
    local_metrics | python3 -c '
import json, sys
m = json.load(sys.stdin)
gb = lambda b: b / 1024 ** 3
bar = lambda p: "█" * int(p / 5) + "·" * (20 - int(p / 5))
h, c, mem = m["host"], m["cpu"], m["memory"]
print("")
print("  %s — %s" % (h["hostname"], h["os"]))
print("  uptime %dd %dh · load %s" % (h["uptime_seconds"] // 86400,
      h["uptime_seconds"] % 86400 // 3600, c["load_average"]["1m"]))
print("")
print("  %-12s %s %5.1f%%  (%d núcleos)" % ("CPU", bar(c["usage_percent"]), c["usage_percent"], c["cores"]))
print("  %-12s %s %5.1f%%  (%.1f / %.1f GB)" % ("RAM", bar(mem["used_percent"]), mem["used_percent"],
      gb(mem["used_bytes"]), gb(mem["total_bytes"])))
for d in m["disks"]:
    print("  %-12s %s %5.1f%%  (%.1f / %.1f GB)" % (d["mountpoint"][:12], bar(d["used_percent"]),
          d["used_percent"], gb(d["used_bytes"]), gb(d["total_bytes"])))
print("")
'
    ;;
  url)
    port="$(read_cfg port)"; token="$(read_cfg token)"
    ip="$(http_get https://api.ipify.org 2>/dev/null || hostname -I | awk '{print $1}')"
    echo "curl -H 'Authorization: Bearer ${token}' http://${ip%%[[:space:]]*}:${port}/metrics"
    ;;
  rotate-token)
    new="$(openssl rand -hex 32 2>/dev/null || python3 -c 'import secrets;print(secrets.token_hex(32))')"
    python3 - "$new" <<'PYROT'
import json, sys
path = "/etc/srvmetrics/config.json"
cfg = json.load(open(path))
cfg["token"] = sys.argv[1]
with open(path, "w") as fh:
    json.dump(cfg, fh, indent=2)
    fh.write("\n")
PYROT
    systemctl restart srvmetrics
    echo "novo token: ${new}"
    ;;
  uninstall)
    systemctl disable --now srvmetrics 2>/dev/null || true
    rm -f /etc/systemd/system/srvmetrics.service
    systemctl daemon-reload
    rm -rf /opt/srvmetrics /etc/srvmetrics
    userdel srvmetrics 2>/dev/null || true
    rm -f "$0"
    echo "srvmetrics removido"
    ;;
  *)
    cat <<USAGE
srvmetrics — agente de métricas do servidor

  srvmetrics stats          resumo legível de CPU / RAM / disco
  srvmetrics test           métricas completas em JSON
  srvmetrics status         estado do serviço
  srvmetrics start|stop|restart
  srvmetrics logs [n]       últimas n linhas de log
  srvmetrics token          mostra o token de acesso
  srvmetrics url            monta o comando curl pronto para uso
  srvmetrics rotate-token   gera um token novo e reinicia o serviço
  srvmetrics uninstall      remove o agente deste servidor
USAGE
    ;;
esac
CLIEOF
  chmod 0755 "$CLI_PATH"
}

configure_service() {
  step "Configurando o serviço systemd"
  local CAP_LINE=""
  [[ $NEEDS_NET_BIND_CAP -eq 1 ]] && CAP_LINE="CAP_NET_BIND_SERVICE"
  cat > "$UNIT_FILE" <<UNIT
[Unit]
Description=srvmetrics — agente de métricas do servidor
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=${SERVICE_USER}
Group=${SERVICE_USER}
ExecStart=/usr/bin/python3 ${INSTALL_DIR}/agent.py ${CONFIG_FILE}
Restart=always
RestartSec=3
StandardOutput=journal
StandardError=journal

# Blindagem: o agente só precisa ler /proc e escutar na porta.
NoNewPrivileges=yes
PrivateDevices=yes
ProtectSystem=strict
ProtectHome=read-only
ProtectKernelTunables=yes
ProtectKernelModules=yes
ProtectControlGroups=yes
RestrictSUIDSGID=yes
RestrictNamespaces=yes
RestrictRealtime=yes
LockPersonality=yes
MemoryDenyWriteExecute=yes
SystemCallFilter=@system-service
SystemCallErrorNumber=EPERM
CapabilityBoundingSet=${CAP_LINE}
AmbientCapabilities=${CAP_LINE}
MemoryMax=128M
TasksMax=64

[Install]
WantedBy=multi-user.target
UNIT
  ok "unit criada em ${UNIT_FILE}"
  spin "systemd recarregado" systemctl daemon-reload
  spin "serviço habilitado no boot" systemctl enable "$SERVICE_NAME"
}

configure_firewall() {
  step "Liberando a porta ${PORT}/tcp no firewall"
  if [[ $CONFIGURE_FIREWALL -eq 0 ]]; then
    skip "ignorado por --no-firewall"
    return
  fi
  if ! command -v ufw >/dev/null 2>&1; then
    warn "UFW não instalado — nenhuma regra local criada"
    info "se houver iptables/nftables, libere a porta ${PORT}/tcp manualmente"
    return
  fi
  if ! ufw status 2>/dev/null | head -1 | grep -qi "active"; then
    warn "UFW instalado mas inativo — a porta já está acessível"
    return
  fi
  if [[ -n "$ALLOW_FROM" ]]; then
    ufw allow from "$ALLOW_FROM" to any port "$PORT" proto tcp comment "srvmetrics" >/dev/null
    ok "acesso liberado apenas para ${ALLOW_FROM}"
  else
    ufw allow "${PORT}/tcp" comment "srvmetrics" >/dev/null
    ok "porta ${PORT}/tcp liberada para qualquer origem"
  fi
}

start_service() {
  step "Iniciando o agente"
  spin "serviço iniciado" systemctl restart "$SERVICE_NAME" \
    || die "o serviço não subiu. Veja: journalctl -u ${SERVICE_NAME} -n 50"

  local deadline=$((SECONDS + 20)) probe_host="127.0.0.1"
  [[ "$BIND" == *:* ]] && probe_host="[::1]"
  while (( SECONDS < deadline )); do
    if http_get "http://${probe_host}:${PORT}/health" >/dev/null 2>&1; then
      ok "respondendo em http://${probe_host}:${PORT}/health"
      break
    fi
    systemctl is-active --quiet "$SERVICE_NAME" \
      || die "o serviço parou logo após iniciar. Veja: journalctl -u ${SERVICE_NAME} -n 50"
    sleep 1
  done
  (( SECONDS < deadline )) || die "o agente não respondeu em 20s. Veja: journalctl -u ${SERVICE_NAME} -n 50"

  if http_get "http://${probe_host}:${PORT}/metrics" "$TOKEN" >/dev/null 2>&1; then
    ok "autenticação e coleta de métricas validadas"
  else
    warn "métricas não validadas — cheque com: srvmetrics test"
  fi
}

summary() {
  step "Concluído"

  local ip local_ip
  ip="$(detect_ip)"
  local_ip="$(detect_local_ip)"
  [[ -n "$ip" ]] || ip="<ip-do-servidor>"
  local url="http://${ip}:${PORT}/metrics"

  local cpu ram disk
  cpu="$(nproc 2>/dev/null || echo '?') vCPU"
  ram="$(free -h 2>/dev/null | awk '/^Mem:/{print $2}' || echo '?') RAM"
  disk="$(df -h / 2>/dev/null | awk 'NR==2{print $2}' || echo '?') em /"

  save_credentials "$url" "$ip"

  printf '\n'
  rule "$GREEN"
  printf '  %s✔  Agente instalado e no ar%s\n' "$BOLD$GREEN" "$RESET"
  rule "$GREEN"
  printf '\n'
  printf '   %sSERVIDOR%s   %s · %s · %s · %s\n' "$DIM" "$RESET" "$(hostname)" "$cpu" "$ram" "$disk"
  printf '\n'
  printf '   %sURL%s        %s%s%s\n' "$DIM" "$RESET" "$BOLD$CYAN" "$url" "$RESET"
  printf '   %sTOKEN%s      %s%s%s\n' "$DIM" "$RESET" "$BOLD$YELLOW" "$TOKEN" "$RESET"
  printf '\n'
  if [[ -n "$local_ip" && "$local_ip" != "$ip" ]]; then
    printf '   %sIP interno %s (o servidor está atrás de NAT — use %s de fora).%s\n' \
           "$DIM" "$local_ip" "$ip" "$RESET"
    printf '\n'
  fi
  printf '   %sTeste agora, de qualquer máquina:%s\n' "$BOLD" "$RESET"
  printf '     %scurl -H "Authorization: Bearer %s" \\%s\n' "$CYAN" "$TOKEN" "$RESET"
  printf '     %s     %s%s\n' "$CYAN" "$url" "$RESET"
  printf '\n'
  printf '   %sEndpoints%s  %s/metrics%s (JSON)  ·  %s/metrics/prometheus%s  ·  %s/health%s (sem token)\n' \
         "$DIM" "$RESET" "$BOLD" "$RESET" "$BOLD" "$RESET" "$BOLD" "$RESET"
  printf '   %sGerenciar%s  srvmetrics stats | status | logs | token | url | rotate-token | uninstall\n' \
         "$DIM" "$RESET"
  printf '   %sSalvo em%s   %s\n' "$DIM" "$RESET" "$CREDENTIALS_FILE"
  printf '\n'
  printf '   %s⚠ Guarde o token: ele é a única credencial de acesso.%s\n' "$YELLOW" "$RESET"
  printf '   %s  Em nuvem (AWS/GCP/Azure/Oracle), libere a porta %s/tcp também no security group.%s\n' \
         "$DIM" "$PORT" "$RESET"
  printf '\n'
}

# Guarda uma cópia da URL e do token para quem fechar o terminal sem copiar.
save_credentials() {
  local url="$1" ip="$2"
  umask 077
  cat > "$CREDENTIALS_FILE" <<CREDS
# srvmetrics — acesso às métricas de $(hostname)
# gerado em $(date '+%Y-%m-%d %H:%M:%S %Z')

URL:   ${url}
TOKEN: ${TOKEN}
IP:    ${ip}
PORTA: ${PORT}

curl -H "Authorization: Bearer ${TOKEN}" ${url}
CREDS
  chmod 0600 "$CREDENTIALS_FILE"
}

do_uninstall() {
  TOTAL_STEPS=4
  banner
  [[ $EUID -eq 0 ]] || die "execute como root"

  step "Parando o serviço"
  if systemctl list-unit-files | grep -q "^${SERVICE_NAME}.service"; then
    systemctl disable --now "$SERVICE_NAME" >/dev/null 2>&1 || true
    ok "serviço parado e desabilitado"
  else
    skip "serviço não estava instalado"
  fi

  step "Removendo arquivos"
  rm -f "$UNIT_FILE" "$CLI_PATH" "$CREDENTIALS_FILE"
  rm -rf "$INSTALL_DIR" "$CONFIG_DIR"
  systemctl daemon-reload
  ok "unit, binários e configuração removidos"

  step "Removendo regra de firewall"
  if command -v ufw >/dev/null 2>&1 && ufw status 2>/dev/null | grep -qi active; then
    while read -r num; do ufw --force delete "$num" >/dev/null 2>&1 || true; done < <(
      ufw status numbered 2>/dev/null | grep -i srvmetrics | sed 's/^\[\s*\([0-9]*\)\].*/\1/' | sort -rn
    )
    ok "regras 'srvmetrics' removidas do UFW"
  else
    skip "UFW inativo ou ausente"
  fi

  step "Removendo usuário de serviço"
  if id -u "$SERVICE_USER" >/dev/null 2>&1; then
    userdel "$SERVICE_USER" 2>/dev/null || true
    ok "usuário '${SERVICE_USER}' removido"
  else
    skip "usuário não existia"
  fi

  printf '\n  %s✔ srvmetrics removido deste servidor.%s\n\n' "$GREEN$BOLD" "$RESET"
}

main() {
  parse_args "$@"
  if [[ "$ACTION" == "uninstall" ]]; then
    do_uninstall
    return
  fi
  banner
  check_system
  resolve_settings
  create_user
  install_agent
  configure_service
  configure_firewall
  start_service
  summary
}

main "$@"
