#!/bin/bash
set -e

BASE="https://in1.click/mirrors"
API="$BASE/api.php"
MSG_URL="$BASE/messages.json"
SELF_URL="$BASE/cli.sh"

MIRRORS=("mirror.freepbx.org" "mirror1.freepbx.org" "mirror2.freepbx.org")
VERSIONS=("17.0" "16.0")

if [ -t 1 ]; then
  C_RESET="\033[0m"
  C_GREEN="\033[32m"
  C_AMBER="\033[33m"
  C_RED="\033[31m"
  C_BLUE="\033[34m"
  C_DIM="\033[2;37m"
  C_CYAN="\033[36m"
else
  C_RESET=""; C_GREEN=""; C_AMBER=""; C_RED=""; C_BLUE=""; C_DIM=""; C_CYAN=""
fi

say()  { printf "%b\n" "$1"; }
info() { say "${C_CYAN}$1${C_RESET}"; }
dim()  { say "${C_DIM}$1${C_RESET}"; }
warn() { say "${C_AMBER}$1${C_RESET}"; }
fail() { say "${C_RED}$1${C_RESET}"; }
sep()  { say "----------------------------------------"; }

MESSAGES="$(curl -fsS "$MSG_URL" | tr -d '\r')"

unesc() {
  local s="$1"
  s="${s//\\\"/\"}"
  s="${s//\\n/$'\n'}"
  printf "%s" "$s"
}

msg() {
  local key="$1"
  unesc "$(sed -n "s/.*\"$key\"[[:space:]]*:[[:space:]]*\"\(.*\)\".*/\1/p" <<<"$MESSAGES" | head -n1)"
}

msg_deduction() {
  local key="$1"
  unesc "$(sed -n "s/.*\"$key\":[[:space:]]*\"(.*\)\".*/\1/p" <<<"$MESSAGES" | head -n1)"
}

msg_deduct() {
  local key="$1"; shift
  local pattern="\"$key\":[[:space:]]*\""
  local template=$(echo "$MESSAGES" | sed -n "/$pattern/,/\"/p" | sed -n 's/.*":\s*"\(.*\)".*/\1/p' | head -n1)
  render "$template" "$@"
}

msg_rand() {
  local key="$1"
  sed -n "/\"$key\"[[:space:]]*:/,/]/p" <<<"$MESSAGES" \
    | sed -n 's/^[[:space:]]*"\(.*\)",\{0,1\}$/\1/p' \
    | shuf -n1
}

render() {
  local s="$1"; shift
  for kv in "$@"; do
    local k="${kv%%=*}"
    local v="${kv#*=}"
    s="${s//\{$k\}/$v}"
  done
  printf "%s" "$s"
}

j_bool() { sed -n "s/.*\"$2\":[[:space:]]*\([^,}]*\).*/\1/p" <<<"$1" | tr -d '"' | head -n1; }
j_num()  { sed -n "s/.*\"$2\":[[:space:]]*\([^,}]*\).*/\1/p" <<<"$1" | tr -d '"' | head -n1; }
j_str()  { sed -n "s/.*\"$2\":[[:space:]]*\"\([^\"]*\)\".*/\1/p" <<<"$1" | head -n1; }
j_arr()  { sed -n "s/.*\"$2\":[[:space:]]*\(\[[^]]*\]\).*/\1/p" <<<"$1" | head -n1; }

fmt_kb() { awk "BEGIN{printf \"%.1f KB\", $1/1024}"; }
to_int() { awk "BEGIN{printf \"%d\", $1+0}"; }
highlight_tail() {
  local line="$1"
  local matched=false
  
  # Extract tail phrases from messages.json and check each one
  # Green positives (dns_ok, tcp_ok, ver_ok)
  for phrase in "$(msg dns_ok | sed 's/.*\. //')" "$(msg tcp_ok | sed 's/.*\. //')" "$(msg ver_ok | sed 's/.*\. //')"; do
    if [[ "$line" =~ (.*)${phrase}$ ]]; then
      printf "%b\n${C_GREEN}${phrase}${C_RESET}\n\n" "${BASH_REMATCH[1]}"
      matched=true
      break
    fi
  done
  
  # Amber warnings (tcp_slow, ssl_expiring)
  if [[ "$matched" == "false" ]]; then
    for phrase in "$(msg tcp_slow | sed 's/.*\. //')" "$(msg ssl_expiring | sed 's/.*\. //')"; do
      if [[ "$line" =~ (.*)${phrase}$ ]]; then
        printf "%b\n${C_AMBER}${phrase}${C_RESET}\n\n" "${BASH_REMATCH[1]}"
        matched=true
        break
      fi
    done
  fi
  
  # Red failures (dns_fail, tcp_fail, ssl_fail, ver_fail, ver_captcha)
  if [[ "$matched" == "false" ]]; then
    for phrase in "$(msg dns_fail | sed 's/.*\. //')" "$(msg tcp_fail | sed 's/.*\? //' | sed 's/\?$//')" "$(msg ssl_fail | sed 's/.*\. //')" "$(msg ver_fail | sed 's/.*\. //')" "$(msg ver_captcha | sed 's/.*\. //')"; do
      if [[ "$line" =~ (.*)${phrase}$ ]]; then
        printf "%b\n${C_RED}${phrase}${C_RESET}\n\n" "${BASH_REMATCH[1]}"
        matched=true
        break
      fi
    done
  fi
  
  # No match - just print as is
  if [[ "$matched" == "false" ]]; then
    printf "%b\n" "$line"
  fi
}


say "FreePBX Mirror Status (CLI)"
say "for IN1CLICK by 20tele.com"
sep
info "$(msg_rand starting)"
say ""
# Check server baseline before testing mirrors
info "$(msg baseline_check_cli)"
baseline="$(curl -fsS "$API?action=baseline" | tr -d '\r' || true)"

cf1_lat="$(echo "$baseline" | python3 -c "import sys,json;d=json.load(sys.stdin);print(d['cf1']['latency'])" 2>/dev/null || true)"
cf0_lat="$(echo "$baseline" | python3 -c "import sys,json;d=json.load(sys.stdin);print(d['cf0']['latency'])" 2>/dev/null || true)"
gg8_lat="$(echo "$baseline" | python3 -c "import sys,json;d=json.load(sys.stdin);print(d['gg8']['latency'])" 2>/dev/null || true)"
gg4_lat="$(echo "$baseline" | python3 -c "import sys,json;d=json.load(sys.stdin);print(d['gg4']['latency'])" 2>/dev/null || true)"

baseline_sum="0"
baseline_count=0
for lat in "$cf1_lat" "$cf0_lat" "$gg8_lat" "$gg4_lat"; do
  if [[ -n "$lat" && "$lat" != "null" ]]; then
    baseline_sum="$(awk "BEGIN{print ($baseline_sum)+($lat)}")"
    baseline_count=$((baseline_count+1))
  fi
done

avg_baseline="0"
if (( baseline_count > 0 )); then
  avg_baseline="$(awk "BEGIN{printf \"%.1f\", ($baseline_sum)/$baseline_count}")"
fi
avg_baseline_i="$(to_int "$avg_baseline")"

dim "$(render "$(msg baseline_display_cli)" cf1="${cf1_lat:-FAIL}" cf0="${cf0_lat:-FAIL}" gg8="${gg8_lat:-FAIL}" gg4="${gg4_lat:-FAIL}")"
dim "$(render "$(msg baseline_average_cli)" avg="$avg_baseline")"

if (( avg_baseline_i > 500 && baseline_count >= 3 )); then
  fail "$(render "$(msg baseline_critical_cli)" avg="$avg_baseline")"
  say "$(msg baseline_critical_advice_cli)"
  exit 1
elif (( avg_baseline_i > 250 && avg_baseline_i <= 500 )); then
  warn "$(render "$(msg baseline_elevated_cli)" avg="$avg_baseline")"
fi

say ""

total_score=0
v17_std=0
v17_ext=0
v16_std=0
v16_ext=0
got_v17=false
got_v16=false
total_time_sum="0"
time_count=0
ssl_ok=0
healthy=0

for host in "${MIRRORS[@]}"; do
  say ">> $host"
  sep

  score=100
  deductions=()

  dns="$(curl -fsS "$API?action=dns&host=$host" | tr -d '\r' || true)"
  dns_resolves="$(j_bool "$dns" resolves || true)"
  dns_ms="$(j_num "$dns" time_ms || true)"
  dns_ips_raw="$(j_arr "$dns" ips || true)"
  dns_ips="$(echo "${dns_ips_raw:-[]}" | tr -d '[]" ' | sed 's/,/, /g')"

  if [[ "$dns_resolves" == "true" && -n "${dns_ips:-}" ]]; then
    line="$(render "$(msg dns_ok)" host="$host" ips="$dns_ips" ms="$(awk 'BEGIN{printf "%.3f", '"$dns_ms"'/1000}')")"
    highlight_tail "$line"
  else
    line="$(render "$(msg dns_fail)" host="$host")"
    highlight_tail "$line"
    deductions+=("$(msg_deduct dns)")
    score=0
  fi

  ping="$(curl -fsS "$API?action=ping&host=$host" | tr -d '\r' || true)"
  ping_ok="$(j_bool "$ping" reachable || true)"
  ping_ms="$(j_num "$ping" time_ms || true)"
  ping_ms_i="$(to_int "${ping_ms:-0}")"

  if [[ "$ping_ok" == "true" ]]; then
    if (( ping_ms_i > 1000 )); then
      line="$(render "$(msg tcp_slow)" host="$host" ms="$(awk 'BEGIN{printf "%.3f", '"$ping_ms"'/1000}')")"
      highlight_tail "$line"
    else
      line="$(render "$(msg tcp_ok)" host="$host" ms="$(awk 'BEGIN{printf "%.3f", '"$ping_ms"'/1000}')")"
      highlight_tail "$line"
    fi
  else
    fail "$(render "$(msg tcp_fail)" host="$host")"
    highlight_tail "$line"
    deductions+=("$(msg_deduct tcp)")
    score=0
  fi

  ssl="$(curl -fsS "$API?action=ssl&host=$host" | tr -d '\r' || true)"
  ssl_valid="$(j_bool "$ssl" valid || true)"
  ssl_days="$(j_num "$ssl" days_left || true)"
  ssl_days_i="$(to_int "${ssl_days:-0}")"
  ssl_issuer="$(j_str "$ssl" issuer || true)"

  if [[ "$ssl_valid" == "true" ]]; then
    ssl_ok=$((ssl_ok+1))
    if (( ssl_days_i < 30 )); then
      line="$(render "$(msg ssl_expiring)" host="$host" days="$ssl_days")"
      highlight_tail "$line"
      if (( ssl_days_i < 7 )); then
        score=$((score-15))
        deductions+=("$(msg_deduct ssl_urgent days="$ssl_days")")
      else
        score=$((score-5))
        deductions+=("$(msg_deduct ssl_warn days="$ssl_days")")
      fi
    else
      line="$(render "$(msg ssl_ok)" host="$host" issuer="$ssl_issuer" days="$ssl_days")"
      highlight_tail "$line"
    fi
  else
    line="$(render "$(msg ssl_fail)" host="$host" error="invalid")"
    highlight_tail "$line"
    score=$((score-40))
    deductions+=("$(msg_deduct ssl_invalid)")
  fi

  for ver in "${VERSIONS[@]}"; do
    v="$(curl -fsS "$API?action=version&host=$host&version=$ver" | tr -d '\r' || true)"

    http_code="$(sed -n 's/.*"http_code":[[:space:]]*\([0-9][0-9]*\).*/\1/p' <<<"$v" | head -n1)"
    total_time="$(sed -n 's/.*"total_time":[[:space:]]*\([0-9.][0-9.]*\).*/\1/p' <<<"$v" | head -n1)"
    size_download="$(sed -n 's/.*"size_download":[[:space:]]*\([0-9][0-9]*\).*/\1/p' <<<"$v" | head -n1)"
    modules_standard="$(sed -n 's/.*"modules_standard":[[:space:]]*\([0-9][0-9]*\).*/\1/p' <<<"$v" | head -n1)"
    modules_extended="$(sed -n 's/.*"modules_extended":[[:space:]]*\([0-9][0-9]*\).*/\1/p' <<<"$v" | head -n1)"
    xml_valid="$(sed -n 's/.*"valid":[[:space:]]*\([^,}]*\).*/\1/p' <<<"$v" | head -n1 | tr -d '"')"
    is_captcha="$(sed -n 's/.*"is_captcha":[[:space:]]*\([^,}]*\).*/\1/p' <<<"$v" | head -n1 | tr -d '"')"

    http_code="${http_code:-0}"
    total_time="${total_time:-0}"
    size_download="${size_download:-0}"
    modules_standard="${modules_standard:-0}"
    modules_extended="${modules_extended:-0}"
    xml_valid="${xml_valid:-false}"
    is_captcha="${is_captcha:-false}"

    ms_i="$(to_int "$total_time")"
    size_kb="$(fmt_kb "$size_download")"
    vName="${ver%.*}"

    if [[ "$is_captcha" == "true" ]]; then
      line="$(render "$(msg ver_captcha)" host="$host" ver="$vName")"
      highlight_tail "$line"
      score=$((score-20))
      deductions+=("$(msg_deduct captcha ver="$vName")")
    elif [[ "$http_code" == "200" && "$xml_valid" == "true" ]]; then
      line="$(render "$(msg ver_ok)" host="$host" ver="$vName" std="$modules_standard" ext="$modules_extended" ms="$total_time")"
      highlight_tail "$line"
      dim "  XML downloaded (${size_kb}), parsed server-side from Netwise East, London (UK)"

      total_time_sum="$(awk "BEGIN{print ($total_time_sum)+($total_time)}")"
      time_count=$((time_count+1))

      if [[ "$ver" == "17.0" && "$got_v17" == "false" ]]; then
        v17_std=$modules_standard
        v17_ext=$modules_extended
        got_v17=true
      elif [[ "$ver" == "16.0" && "$got_v16" == "false" ]]; then
        v16_std=$modules_standard
        v16_ext=$modules_extended
        got_v16=true
      fi

      # Apply baseline-adjusted speed thresholds (10:1 ratio)
      baseline_adj="$(awk "BEGIN{printf \"%d\", ($avg_baseline_i > 250 ? $avg_baseline_i : 0)}")"
      adjusted_6s=$((6000 + (baseline_adj * 10)))
      adjusted_4s=$((4000 + (baseline_adj * 10)))
      adjusted_2s=$((2000 + (baseline_adj * 10)))

      penalty=0
      reason=""
      
      if (( ms_i > adjusted_6s )); then
        # Progressive: -1pt + -3pts + -1pt per second over threshold
        seconds_over=$(( (ms_i - adjusted_6s) / 1000 ))
        penalty=$(( 1 + 3 + seconds_over ))
        adjusted_6s_s="$(awk "BEGIN{printf \"%.1f\", $adjusted_6s/1000}")"
        total_s="$(awk "BEGIN{printf \"%.2f\", $ms_i/1000}")"
        reason="FreePBX ${vName} took ${total_s}s. Over ${adjusted_6s_s}s: -1pt (>2s), -3pts (>4s), -${seconds_over}pts (${seconds_over}s over ${adjusted_6s_s}s) = -${penalty}pts total."
      elif (( ms_i > adjusted_4s )); then
        penalty=4  # -1pt (>2s) + -3pts (>4s)
        adjusted_4s_s="$(awk "BEGIN{printf \"%.1f\", $adjusted_4s/1000}")"
        total_s="$(awk "BEGIN{printf \"%.2f\", $ms_i/1000}")"
        reason="FreePBX ${vName} took ${total_s}s. Over ${adjusted_4s_s}s: -1pt (>2s), -3pts (>4s) = -4pts total."
      elif (( ms_i > adjusted_2s )); then
        penalty=1
        adjusted_2s_s="$(awk "BEGIN{printf \"%.1f\", $adjusted_2s/1000}")"
        total_s="$(awk "BEGIN{printf \"%.2f\", $ms_i/1000}")"
        reason="FreePBX ${vName} took ${total_s}s. Over ${adjusted_2s_s}s threshold, -1 point."
      elif (( baseline_adj > 0 && ms_i > 2000 )); then
        reason="$(render "$(msg baseline_proportional_note_cli)" ver="$vName" ms="$total_time" baseline="$avg_baseline")"
        dim "  $reason"
      fi
      
      if (( penalty > 0 )); then
        score=$((score - penalty))
        deductions+=("$reason")
      fi
    elif [[ "$http_code" == "200" && "$xml_valid" == "false" ]]; then
      line="$(render "$(msg ver_fail)" host="$host" ver="$vName" code="$http_code")"
      highlight_tail "$line"
      score=$((score-10))
      deductions+=("$(msg_deduct xml_invalid ver="$vName")")
    else
      line="$(render "$(msg ver_fail)" host="$host" ver="$vName" code="$http_code")"
      highlight_tail "$line"
      score=$((score-15))
      deductions+=("$(msg_deduct http ver="$vName" code="$http_code")")
    fi
  done

  (( score < 0 )) && score=0
  say ""

  dim "$host — Starting with a clean 100."
  if [[ ${#deductions[@]} -eq 0 ]]; then
    say "${C_GREEN}$host — Full marks! 100%. Nothing to complain about.${C_RESET}"
  else
    for deduction in "${deductions[@]}"; do
      if [[ "$deduction" == *"showstopper"* || "$deduction" == *"down"* || "$deduction" == *"invalid"* || "$deduction" == *"captcha"* || "$deduction" == *"urgent"* ]]; then
        fail "$host — $deduction"
      else
        warn "$host — $deduction"
      fi
    done
    
    final_line="$host — Final score: ${score}%."
    if (( score >= 75 )); then
      say "${C_GREEN}${final_line}${C_RESET}"
    elif (( score >= 40 )); then
      warn "$final_line"
    else
      fail "$final_line"
    fi
  fi

  if (( score >= 90 )); then
    healthy=$((healthy+1))
  fi

  total_score=$((total_score+score))
  say ""
done

sep
overall=$(( total_score / ${#MIRRORS[@]} ))
avg_ms="0"
if (( time_count > 0 )); then
  avg_ms="$(awk "BEGIN{printf \"%.1f\", ($total_time_sum)/$time_count}")"
fi

# Overall score color
score_color="${C_GREEN}"
if (( overall < 90 )); then
  score_color="${C_AMBER}"
fi
if (( overall < 70 )); then
  score_color="${C_RED}"
fi
say "Overall score: ${score_color}${overall}%${C_RESET}"

# Mirrors healthy color
mirrors_color="${C_GREEN}"
if (( healthy < ${#MIRRORS[@]} )); then
  mirrors_color="${C_AMBER}"
fi
if (( healthy == 0 )); then
  mirrors_color="${C_RED}"
fi
say "Mirrors healthy: ${mirrors_color}${healthy}/${#MIRRORS[@]}${C_RESET}"

# SSL valid color
ssl_color="${C_GREEN}"
if (( ssl_ok < ${#MIRRORS[@]} )); then
  ssl_color="${C_AMBER}"
fi
if (( ssl_ok == 0 )); then
  ssl_color="${C_RED}"
fi
say "SSL valid: ${ssl_color}${ssl_ok}/${#MIRRORS[@]}${C_RESET}"

# Average response color
avg_ms_i="$(to_int "$avg_ms")"
response_color="${C_GREEN}"
if (( avg_ms_i >= 2000 )); then
  response_color="${C_AMBER}"
fi
if (( avg_ms_i >= 4000 )); then
  response_color="${C_RED}"
fi
say "Average response: ${response_color}${avg_ms}ms${C_RESET}"

# v17 modules color
v17_color="${C_GREEN}"
if (( v17_std == 0 || v17_ext == 0 )); then
  v17_color="${C_RED}"
fi
say "v17 modules: ${v17_color}${v17_std} | ${v17_ext}${C_RESET}"

# v16 modules color
v16_color="${C_GREEN}"
if (( v16_std == 0 || v16_ext == 0 )); then
  v16_color="${C_RED}"
fi
say "v16 modules: ${v16_color}${v16_std} | ${v16_ext}${C_RESET}"

# Baseline latency color
baseline_color="${C_GREEN}"
if (( avg_baseline_i >= 250 )); then
  baseline_color="${C_AMBER}"
fi
if (( avg_baseline_i >= 500 )); then
  baseline_color="${C_RED}"
fi
say "Baseline latency: ${baseline_color}${avg_baseline}ms${C_RESET}"
say ""

if (( overall >= 90 )); then
  say "${C_GREEN}$(msg recommendation_good_cli)${C_RESET}"
elif (( overall >= 70 )); then
  warn "$(msg recommendation_warn_cli)"
else
  fail "$(msg recommendation_bad_cli)"
fi

say ""

# ── APT Repository Check (deb.freepbx.org) ──
say ">> deb.freepbx.org (APT repository)"
sep
info "$(msg apt_header_cli)"

apt_host="deb.freepbx.org"

# DNS
apt_dns="$(curl -fsS "$API?action=dns&host=$apt_host" | tr -d '\r' || true)"
apt_dns_ok="$(j_bool "$apt_dns" resolves || true)"
apt_dns_ips="$(j_arr "$apt_dns" ips || true)"
apt_dns_ips_fmt="$(echo "${apt_dns_ips:-[]}" | tr -d '[]" ' | sed 's/,/, /g')"
apt_dns_ms="$(j_num "$apt_dns" time_ms || true)"

if [[ "$apt_dns_ok" == "true" && -n "${apt_dns_ips_fmt:-}" ]]; then
  line="$(render "$(msg dns_ok)" host="$apt_host" ips="$apt_dns_ips_fmt" ms="$(awk 'BEGIN{printf "%.3f", '"$apt_dns_ms"'/1000}')")"
  highlight_tail "$line"
else
  line="$(render "$(msg dns_fail)" host="$apt_host")"
  highlight_tail "$line"
fi

# TCP
apt_ping="$(curl -fsS "$API?action=ping&host=$apt_host" | tr -d '\r' || true)"
apt_ping_ok="$(j_bool "$apt_ping" reachable || true)"
apt_ping_ms="$(j_num "$apt_ping" time_ms || true)"

if [[ "$apt_ping_ok" == "true" ]]; then
  line="$(render "$(msg tcp_ok)" host="$apt_host" ms="$(awk 'BEGIN{printf "%.3f", '"$apt_ping_ms"'/1000}')")"
  highlight_tail "$line"
else
  line="$(render "$(msg tcp_fail)" host="$apt_host")"
  highlight_tail "$line"
fi

# SSL
apt_ssl="$(curl -fsS "$API?action=ssl&host=$apt_host" | tr -d '\r' || true)"
apt_ssl_valid="$(j_bool "$apt_ssl" valid || true)"
apt_ssl_days="$(j_num "$apt_ssl" days_left || true)"
apt_ssl_issuer="$(j_str "$apt_ssl" issuer || true)"

if [[ "$apt_ssl_valid" == "true" ]]; then
  line="$(render "$(msg ssl_ok)" host="$apt_host" issuer="$apt_ssl_issuer" days="$apt_ssl_days")"
  highlight_tail "$line"
else
  line="$(render "$(msg ssl_fail)" host="$apt_host" error="invalid")"
  highlight_tail "$line"
fi

# APT Packages.gz check
apt_result="$(curl -fsS "$API?action=apt&host=$apt_host" | tr -d '\r' || true)"
apt_http="$(j_num "$apt_result" http_code || true)"
apt_time="$(j_num "$apt_result" total_time || true)"
apt_size="$(j_num "$apt_result" size_download || true)"
apt_valid="$(j_bool "$apt_result" valid || true)"
apt_captcha="$(j_bool "$apt_result" is_captcha || true)"

apt_http="${apt_http:-0}"
apt_time="${apt_time:-0}"
apt_size="${apt_size:-0}"
apt_size_kb="$(fmt_kb "$apt_size")"

apt_status="ok"
if [[ "$apt_captcha" == "true" ]]; then
  fail "$(msg apt_captcha)"
  apt_status="captcha"
elif [[ "$apt_http" == "200" && "$apt_valid" == "true" ]]; then
  say "${C_GREEN}$(render "$(msg apt_ok)" code="$apt_http" size="$apt_size_kb" ms="$apt_time")${C_RESET}"
elif [[ "$apt_http" == "200" && "$apt_valid" != "true" ]]; then
  warn "$(msg apt_invalid)"
  apt_status="invalid"
else
  fail "$(render "$(msg apt_fail)" code="$apt_http")"
  apt_status="fail"
fi

say ""
read -r -p "$(msg repeat_prompt)" ans </dev/tty || true

if [[ "${ans:-}" =~ ^[Yy]$ ]]; then
  echo
  curl -fsS "$SELF_URL" | bash
fi

exit 0