#!/usr/bin/env bash
# =============================================================================
# redirects-test.sh — verify every rule in your redirect map actually works
# Source: https://www.kunaldabi.com/301-vs-302-redirections/
# Author: Kunal Singh Dabi · KD Digital · WhatsApp +91 96366 50036
# =============================================================================
# Usage:
#   ./redirects-test.sh redirect-map.csv          # test all rows
#   ./redirects-test.sh --url https://...         # test one URL
#   ./redirects-test.sh redirect-map.csv --html report.html   # html report
#
# Reads the redirect map CSV produced by the kit, hits each old_url with
# curl -I --location, captures the chain (every Location: header along the
# way), and reports:
#   - HTTP status code at each hop (301 / 302 / 200 / 404 / 5xx)
#   - Final URL reached
#   - Number of hops (>2 is a chain — fix)
#   - Whether final URL matches the planned new_url
# Exit code: 0 if all rows green, 1 if any chain/mismatch/error.
# =============================================================================

set -uo pipefail

CSV=""
SINGLE_URL=""
HTML_OUT=""
MAX_HOPS_OK=2

# ---- arg parsing -------------------------------------------------------------
while [[ $# -gt 0 ]]; do
  case "$1" in
    --url)   SINGLE_URL="$2"; shift 2 ;;
    --html)  HTML_OUT="$2"; shift 2 ;;
    -h|--help)
      sed -n '2,/^# ===/p' "$0" | head -n 25
      exit 0 ;;
    *)
      CSV="$1"; shift ;;
  esac
done

# ---- helpers -----------------------------------------------------------------
trace_chain() {
  local url="$1"
  # -s silent, -I head only, -L follow redirects, -o /dev/null write-out our format
  curl -s -I -L "$url" \
    --max-redirs 10 \
    -A "Mozilla/5.0 (kd-redirects-test)" \
    -w '\n---END---\n' 2>&1 \
    | awk '
        /^HTTP\// { code=$2; printf "%s ", code }
        /^[Ll]ocation: / { sub(/[\r\n]/,"",$2); printf "-> %s ", $2 }
        /^---END---$/ { print "" }
      '
}

count_hops() {
  echo "$1" | tr -cd '>' | wc -c
}

extract_final_url() {
  local chain="$1"
  echo "$chain" | grep -oE 'https?://[^ ]+' | tail -1
}

extract_final_code() {
  local chain="$1"
  echo "$chain" | grep -oE '\b[1-5][0-9][0-9]\b' | tail -1
}

# ---- main loop ---------------------------------------------------------------
declare -i total=0 ok=0 chain=0 mismatch=0 err=0
declare -a html_rows

print_row() {
  local old="$1" new_planned="$2" final_url="$3" final_code="$4" hops="$5" status_label="$6"
  printf "%-7s  %4d  %s\n  → %s  (planned: %s)\n\n" \
    "$status_label" "$final_code" "$old" "$final_url" "$new_planned"
  if [[ -n "$HTML_OUT" ]]; then
    local color
    case "$status_label" in
      OK)       color="#16a34a" ;;
      CHAIN)    color="#d97706" ;;
      MISMATCH) color="#dc2626" ;;
      *)        color="#dc2626" ;;
    esac
    html_rows+=("<tr><td style='color:$color;font-weight:700'>$status_label</td><td>$final_code</td><td>$hops</td><td>$old</td><td>$final_url</td><td>$new_planned</td></tr>")
  fi
}

test_one() {
  local old="$1" planned="$2"
  total+=1
  local chain_str
  chain_str=$(trace_chain "$old" 2>&1)
  if [[ -z "$chain_str" ]]; then
    err+=1; print_row "$old" "$planned" "(no response)" 0 0 "ERROR"; return
  fi
  local final_url final_code hops
  final_url=$(extract_final_url "$chain_str")
  final_code=$(extract_final_code "$chain_str")
  hops=$(count_hops "$chain_str")

  if [[ "$final_code" != "200" ]]; then
    err+=1; print_row "$old" "$planned" "$final_url" "$final_code" "$hops" "ERROR"
  elif [[ "$hops" -gt "$MAX_HOPS_OK" ]]; then
    chain+=1; print_row "$old" "$planned" "$final_url" "$final_code" "$hops" "CHAIN"
  elif [[ -n "$planned" && "${final_url%/}" != "${planned%/}" ]]; then
    mismatch+=1; print_row "$old" "$planned" "$final_url" "$final_code" "$hops" "MISMATCH"
  else
    ok+=1; print_row "$old" "$planned" "$final_url" "$final_code" "$hops" "OK"
  fi
}

if [[ -n "$SINGLE_URL" ]]; then
  test_one "$SINGLE_URL" ""
elif [[ -n "$CSV" && -f "$CSV" ]]; then
  while IFS=, read -r old_url new_url _redirect_type _reason _by _date _vs _vd _notes; do
    [[ "$old_url" =~ ^# ]] && continue
    [[ "$old_url" == "old_url" ]] && continue   # skip header
    [[ -z "$old_url" ]] && continue
    test_one "$old_url" "$new_url"
  done < "$CSV"
else
  echo "Usage: $0 redirect-map.csv  OR  $0 --url https://example.com/old/" >&2
  exit 2
fi

# ---- summary -----------------------------------------------------------------
echo "============================================================"
echo "TOTAL: $total  ·  OK: $ok  ·  CHAIN(>${MAX_HOPS_OK}hops): $chain  ·  MISMATCH: $mismatch  ·  ERROR: $err"
echo "============================================================"

if [[ -n "$HTML_OUT" ]]; then
  {
    echo "<!doctype html><html><head><meta charset=utf-8><title>Redirect audit — $(date)</title>"
    echo "<style>body{font-family:system-ui,sans-serif;padding:24px;color:#1a1a1a}h1{color:#0B1340}table{border-collapse:collapse;width:100%}td,th{padding:8px 12px;border-bottom:1px solid #e5e7eb;font-size:14px;text-align:left}th{background:#f4f4f7}tr:hover{background:#fafafa}</style></head><body>"
    echo "<h1>Redirect audit · $(date)</h1>"
    echo "<p>Total: $total &middot; OK: $ok &middot; Chain: $chain &middot; Mismatch: $mismatch &middot; Error: $err</p>"
    echo "<table><thead><tr><th>Status</th><th>Code</th><th>Hops</th><th>From</th><th>To (final)</th><th>Planned</th></tr></thead><tbody>"
    for r in "${html_rows[@]}"; do echo "$r"; done
    echo "</tbody></table>"
    echo "<p style='margin-top:32px;color:#6b7280;font-size:13px'>Generated by redirects-test.sh from kunaldabi.com/301-vs-302-redirections/</p>"
    echo "</body></html>"
  } > "$HTML_OUT"
  echo "HTML report written: $HTML_OUT"
fi

[[ "$chain" -eq 0 && "$mismatch" -eq 0 && "$err" -eq 0 ]] && exit 0 || exit 1
