#!/usr/bin/env bash
set -euo pipefail

# Master test/security runner.
# - npm audit (high+)
# - lint / type-check / format check / tests
# - Trivy file-system scan (HIGH/CRITICAL)
# - OWASP ZAP baseline (unauthenticated)
#
# Outputs are written under reports/runs/<timestamp>/ and summarized in summary.html.
#
# Env vars:
#   TARGET           - URL to scan with ZAP (default: https://test.lomavuokraus.fi)
#   TRIVY_TARGET     - Path or image to scan (default: current directory)
#   TRIVY_MODE       - "fs" (default) or "image"
#   ZAP_IMAGE        - Override ZAP image (default in zap-baseline.sh)
#   TIMEOUT_MINUTES  - ZAP timeout minutes (default in zap-baseline.sh)

RUN_TS=$(date +"%Y%m%d-%H%M%S")
RUN_DIR="reports/runs/${RUN_TS}"
mkdir -p "$RUN_DIR"

SUMMARY_ROWS=()
SUMMARY_TEXT_ROWS=()
PASS_COUNT=0
FAIL_COUNT=0
SKIP_COUNT=0

log() {
  echo "[$(date +"%H:%M:%S")] $*"
}

record_result() {
  local name="$1"; shift
  local status="$1"; shift
  local detail="$1"; shift
  local detail_text="$1"; shift
  SUMMARY_ROWS+=("<tr><td>${name}</td><td>${status}</td><td>${detail}</td></tr>")
  SUMMARY_TEXT_ROWS+=("${name}: ${status}${detail_text:+ - ${detail_text}}")
  case "$status" in
    PASS) PASS_COUNT=$((PASS_COUNT + 1)) ;;
    FAIL) FAIL_COUNT=$((FAIL_COUNT + 1)) ;;
    SKIP) SKIP_COUNT=$((SKIP_COUNT + 1)) ;;
  esac
}

update_index() {
  local runs_dir="reports/runs"
  local index_file="reports/index.html"
  local items=""

  if [ -d "$runs_dir" ]; then
    while IFS= read -r run_dir; do
      local meta_file="${runs_dir}/${run_dir}/meta.txt"
      [ -f "$meta_file" ] || continue

      local run_ts target pass fail skip
      run_ts=$(grep -E '^RUN_TS=' "$meta_file" | head -n1 | cut -d= -f2-)
      target=$(grep -E '^TARGET=' "$meta_file" | head -n1 | cut -d= -f2-)
      pass=$(grep -E '^PASS_COUNT=' "$meta_file" | head -n1 | cut -d= -f2-)
      fail=$(grep -E '^FAIL_COUNT=' "$meta_file" | head -n1 | cut -d= -f2-)
      skip=$(grep -E '^SKIP_COUNT=' "$meta_file" | head -n1 | cut -d= -f2-)

      run_ts="${run_ts:-$run_dir}"
      target="${target:-unknown}"
      pass="${pass:-0}"
      fail="${fail:-0}"
      skip="${skip:-0}"

      items+=$'\n'"    <li><div class=\"run\"><a href=\"runs/${run_ts}/summary.html\">${run_ts}</a></div><div class=\"meta\">Target: ${target} | Pass: ${pass} | Fail: ${fail} | Skip: ${skip}</div></li>"
    done < <(ls "$runs_dir" 2>/dev/null | sort -r)
  fi

  cat >"$index_file" <<EOF
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>Lomavuokraus Test Suite Runs</title>
  <style>
    body { font-family: Arial, sans-serif; padding: 16px; background: #0b0d11; color: #e9ecf1; }
    a { color: #7cc7ff; text-decoration: none; }
    a:hover { text-decoration: underline; }
    ul { list-style: none; padding: 0; }
    li { margin-bottom: 10px; padding: 10px; border: 1px solid #1f2937; border-radius: 6px; background: #111827; }
    .run { font-weight: bold; }
    .meta { font-size: 0.95em; color: #cfd6e0; margin-top: 2px; }
  </style>
</head>
<body>
  <h1>Test Suite Runs</h1>
  <ul>
${items:-"    <li>No runs found.</li>"}
  </ul>
</body>
</html>
EOF
}

notify_redmine() {
  local fail_lines=()
  local failures_file="$RUN_DIR/failures.txt"

  for row in "${SUMMARY_TEXT_ROWS[@]}"; do
    if [[ "$row" == *"FAIL"* ]]; then
      fail_lines+=("$row")
    fi
  done

  [ "${#fail_lines[@]}" -gt 0 ] || return

  printf "%s\n" "${fail_lines[@]}" >"$failures_file"

  local tracker="bug"
  for row in "${fail_lines[@]}"; do
    case "$row" in
      npm\ audit:*) tracker="security" ;;
      Trivy*) tracker="security" ;;
      OWASP\ ZAP\ baseline*) tracker="security" ;;
    esac
  done

  if command -v node >/dev/null 2>&1 && [ -x "${BASH_SOURCE%/*}/redmine-report.js" ]; then
    log "Reporting failures to Redmine (${tracker})..."
    if node "${BASH_SOURCE%/*}/redmine-report.js" create-test-issue \
      --suite "run-test-suite" \
      --run "$RUN_TS" \
      --fail-count "$FAIL_COUNT" \
      --failures-file "$failures_file" \
      --summary-file "$SUMMARY_FILE" \
      --target "$TARGET" \
      --tracker "$tracker" \
      --fingerprint-seed "${TARGET}|${fail_lines[*]}"; then
      log "Redmine notification complete."
    else
      log "Redmine notification failed or skipped (see output above)."
    fi
  else
    log "Redmine reporter not available; skipping Redmine notification."
  fi
}

# 1) npm audit
if command -v npm >/dev/null 2>&1; then
  log "Running npm audit (high)..."
  AUDIT_JSON="$RUN_DIR/npm-audit.json"
  AUDIT_TXT="$RUN_DIR/npm-audit.txt"
  if npm audit --audit-level=high --json >"$AUDIT_JSON" 2>"$AUDIT_TXT"; then
    record_result "npm audit" "PASS" "<a href=\"npm-audit.txt\">text</a> | <a href=\"npm-audit.json\">json</a>" "reports: ${AUDIT_TXT}, ${AUDIT_JSON}"
  else
    record_result "npm audit" "FAIL" "<a href=\"npm-audit.txt\">text</a> | <a href=\"npm-audit.json\">json</a>" "reports: ${AUDIT_TXT}, ${AUDIT_JSON}"
  fi
else
  log "npm not found; skipping npm audit"
  record_result "npm audit" "SKIP" "npm not available" "npm not available"
fi

# 2) Lint / type-check / format / tests
run_npm_check() {
  local name="$1"; shift
  local outfile="$RUN_DIR/${name}.txt"

  if ! command -v npm >/dev/null 2>&1; then
    log "npm not found; skipping ${name}"
    record_result "${name}" "SKIP" "npm not available" "npm not available"
    return
  fi

  if npm run 2>/dev/null | grep -qE "^  ${name}$"; then
    log "Running ${name}..."
    if npm run "${name}" >"$outfile" 2>&1; then
      record_result "${name}" "PASS" "<a href=\"${name}.txt\">log</a>" "log: ${outfile}"
    else
      record_result "${name}" "FAIL" "<a href=\"${name}.txt\">log</a>" "log: ${outfile}"
    fi
  else
    log "npm script '${name}' not defined; skipping"
    record_result "${name}" "SKIP" "script not defined" "script not defined"
  fi
}

run_npm_check "lint"
run_npm_check "type-check"
run_npm_check "format:check"
run_npm_check "test"

# 3) Trivy (fs by default)
TRIVY_TARGET="${TRIVY_TARGET:-.}"
TRIVY_MODE="${TRIVY_MODE:-fs}"
if command -v trivy >/dev/null 2>&1; then
  log "Running Trivy (${TRIVY_MODE}) on ${TRIVY_TARGET}..."
  TRIVY_TXT="$RUN_DIR/trivy.txt"
  TRIVY_IGNORE_ARGS=()
  if [ -f ".trivyignore" ]; then
    TRIVY_IGNORE_ARGS+=(--ignorefile .trivyignore)
  fi
  if trivy "${TRIVY_MODE}" --severity HIGH,CRITICAL --timeout 5m "${TRIVY_IGNORE_ARGS[@]}" "$TRIVY_TARGET" >"$TRIVY_TXT"; then
    record_result "Trivy (${TRIVY_MODE})" "PASS" "<a href=\"trivy.txt\">report</a>" "report: ${TRIVY_TXT}"
  else
    record_result "Trivy (${TRIVY_MODE})" "FAIL" "<a href=\"trivy.txt\">report</a>" "report: ${TRIVY_TXT}"
  fi
else
  log "Trivy not found; skipping"
  record_result "Trivy" "SKIP" "trivy not available" "trivy not available"
fi

# 4) OWASP ZAP baseline
TARGET="${TARGET:-https://test.lomavuokraus.fi}"
ZAP_DIR="$RUN_DIR/zap"
mkdir -p "$ZAP_DIR"
log "Running ZAP baseline against ${TARGET}..."
if TARGET="$TARGET" REPORT_DIR="$ZAP_DIR" "${BASH_SOURCE%/*}/zap-baseline.sh"; then
  record_result "OWASP ZAP baseline" "PASS" "<a href=\"zap/zap-report.html\">HTML</a> | <a href=\"zap/zap-report.json\">JSON</a>" "reports: ${ZAP_DIR}/zap-report.html, ${ZAP_DIR}/zap-report.json"
else
  record_result "OWASP ZAP baseline" "FAIL" "<a href=\"zap/zap-report.html\">HTML</a> | <a href=\"zap/zap-report.json\">JSON</a>" "reports: ${ZAP_DIR}/zap-report.html, ${ZAP_DIR}/zap-report.json"
fi

# Summary HTML
SUMMARY_FILE="$RUN_DIR/summary.html"
cat >"$SUMMARY_FILE" <<EOF
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <title>Test Suite Summary - ${RUN_TS}</title>
  <style>
    body { font-family: Arial, sans-serif; padding: 20px; background: #0b0d11; color: #e9ecf1; }
    table { border-collapse: collapse; width: 100%; margin-top: 12px; }
    th, td { border: 1px solid #333; padding: 8px; }
    th { background: #111827; }
    a { color: #7cc7ff; }
  </style>
</head>
<body>
  <h1>Test Suite Summary</h1>
  <div>Run: ${RUN_TS}</div>
  <div>Target: ${TARGET}</div>
  <table>
    <thead><tr><th>Check</th><th>Status</th><th>Details</th></tr></thead>
    <tbody>
      ${SUMMARY_ROWS[*]}
  </tbody>
</table>
</body>
</html>
EOF

META_FILE="$RUN_DIR/meta.txt"
cat >"$META_FILE" <<EOF
RUN_TS=${RUN_TS}
TARGET=${TARGET}
PASS_COUNT=${PASS_COUNT}
FAIL_COUNT=${FAIL_COUNT}
SKIP_COUNT=${SKIP_COUNT}
EOF

if [ "$FAIL_COUNT" -gt 0 ]; then
  notify_redmine
fi

update_index

log "Summary:"
for row in "${SUMMARY_TEXT_ROWS[@]}"; do
  echo "  - ${row}"
done

log "Done. Reports in ${RUN_DIR}"
echo "Open ${SUMMARY_FILE} in a browser for the summary."
