#!/usr/bin/env bash
# docker-doctor — static linter for Dockerfile + compose + .npmrc.docker drift.
#
# Sibling tool to gitea-doctor. Runs against a repo root and verifies the
# invariants established by docker-build-optimization-roadmap.md Phase A.
#
# Usage:
#   bash scripts/docker-doctor.sh                    # check current repo (cwd)
#   bash scripts/docker-doctor.sh --repo /path       # check given repo
#   bash scripts/docker-doctor.sh --quiet            # only print failures
#   bash scripts/docker-doctor.sh --warn-only        # never exit non-zero
#
# Exit codes:
#   0  all checks pass (warnings allowed)
#   1  one or more error-level checks failed
#   2  bad invocation

set -uo pipefail

REPO=""
QUIET=false
WARN_ONLY=false
while [ $# -gt 0 ]; do
  case "$1" in
    --repo) REPO="${2:-}"; shift ;;
    --quiet) QUIET=true ;;
    --warn-only) WARN_ONLY=true ;;
    -h|--help) sed -n '2,17p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
    *) echo "Unknown arg: $1" >&2; exit 2 ;;
  esac
  shift
done

REPO="${REPO:-$(pwd)}"
if [ ! -d "$REPO" ]; then
  echo "✗ Not a directory: $REPO" >&2; exit 2
fi
cd "$REPO"

fail=0
warn_count=0
say()   { $QUIET || echo "$@"; }
ok()    { say "  ✓ $1"; }
warn()  { echo "  ⚠ $1"; warn_count=$((warn_count + 1)); }
err()   { echo "  ✗ $1"; fail=1; }

say "🐳 docker-doctor — Dockerfile + compose lint  ($(basename "$REPO"))"
say ""

# Discover surfaces
DOCKERFILES=$(find . -maxdepth 3 -name 'Dockerfile' -not -path './node_modules/*' -not -path './.git/*' 2>/dev/null | sort)
COMPOSE_FILES=$(find . -maxdepth 2 -name 'docker-compose*.yml' -not -path './node_modules/*' 2>/dev/null | sort)

if [ -z "$DOCKERFILES" ]; then
  say "  (no Dockerfile found — skipping)"
  exit 0
fi

# ── 1. .npmrc.docker template hygiene (F4, F14) ─────────────────
if [ -f .npmrc.docker ]; then
  if grep -q '\${GITEA_NPM_HOST' .npmrc.docker; then
    ok ".npmrc.docker uses \${GITEA_NPM_HOST} placeholder"
  else
    err ".npmrc.docker: missing \${GITEA_NPM_HOST} placeholder (F4)"
  fi
  if grep -q '\${GITEA_NPM_OWNER' .npmrc.docker; then
    ok ".npmrc.docker uses \${GITEA_NPM_OWNER} placeholder"
  else
    err ".npmrc.docker: missing \${GITEA_NPM_OWNER} placeholder (F14) — owner is hardcoded"
  fi
  if grep -q '_authToken=\${GITEA_NPM_TOKEN' .npmrc.docker; then
    ok ".npmrc.docker uses \${GITEA_NPM_TOKEN} placeholder"
  else
    err ".npmrc.docker: missing \${GITEA_NPM_TOKEN} placeholder for _authToken"
  fi
else
  warn ".npmrc.docker not present (acceptable if no @bytelyst/* deps)"
fi

# ── 2. .gitignore hygiene (B3) ──────────────────────────────────
if [ -f .gitignore ]; then
  if grep -qE '^\.docker-deps/\*?$' .gitignore; then
    ok ".gitignore covers .docker-deps/"
  else
    err ".gitignore missing .docker-deps/* entry (B3)"
  fi
  if grep -qE '^\*\.bak$' .gitignore || grep -qE 'package\.json\.bak' .gitignore; then
    ok ".gitignore covers *.bak (docker-prep artifacts)"
  else
    warn ".gitignore: no *.bak rule (docker-prep.sh artifacts may leak)"
  fi
fi

# ── 3. Per-Dockerfile checks ────────────────────────────────────
for df in $DOCKERFILES; do
  say ""
  say "── $df"

  # Detect Python vs Node Dockerfiles to apply the right checks.
  IS_PYTHON=false
  if grep -qE '^FROM[[:space:]]+python:' "$df"; then
    IS_PYTHON=true
    say "  (detected Python Dockerfile — skipping Node-specific checks)"
  fi

  # 3a. Syntax directive (enables BuildKit features)
  if head -1 "$df" | grep -q '^# syntax=docker/dockerfile'; then
    ok "BuildKit syntax directive present"
  else
    warn "missing '# syntax=docker/dockerfile:1.7' directive (A2 cache mounts need it)"
  fi

  # 3b. Base image on approved list (Node OR Python)
  if grep -qE '^FROM (\$\{BASE_IMAGE[^}]*\}|node:22-alpine|node:22-slim|python:3\.[0-9]+(-slim|-alpine)?)' "$df"; then
    ok "approved base image"
  else
    err "non-approved base image — must use node:22-alpine, node:22-slim, python:3.x(-slim/-alpine), or \${BASE_IMAGE} ARG"
  fi

  # Skip pnpm/Node checks for Python Dockerfiles
  if $IS_PYTHON; then
    continue
  fi

  # 3c. corepack — not legacy global pnpm install
  if grep -qE '^RUN[[:space:]]+npm[[:space:]]+install[[:space:]]+-g[[:space:]]+pnpm' "$df"; then
    err "legacy 'npm install -g pnpm' detected — use corepack (A1)"
  fi
  if grep -q 'corepack enable' "$df"; then
    ok "corepack used for pnpm"
  fi

  # 3d. If COPYs .npmrc.docker, must ARG GITEA_NPM_OWNER (F14)
  if grep -q 'COPY .*\.npmrc\.docker' "$df"; then
    if grep -qE '^ARG[[:space:]]+GITEA_NPM_OWNER' "$df"; then
      ok "declares ARG GITEA_NPM_OWNER"
    else
      err "COPYs .npmrc.docker but missing 'ARG GITEA_NPM_OWNER' (F14)"
    fi
    if grep -qE '^ARG[[:space:]]+GITEA_NPM_HOST' "$df"; then
      ok "declares ARG GITEA_NPM_HOST"
    else
      err "COPYs .npmrc.docker but missing 'ARG GITEA_NPM_HOST' (F14)"
    fi
  fi

  # 3e. .docker-deps must use wildcard COPY (A5-2)
  if grep -qE 'COPY[[:space:]]+\.docker-deps/ ' "$df"; then
    err "rigid 'COPY .docker-deps/' — use wildcard 'COPY .docker-deps* /app/.docker-deps/' (A5-2, B3)"
  fi

  # 3f. web Dockerfile: glob config copy (F11, F13)
  if echo "$df" | grep -q 'web/Dockerfile'; then
    if grep -qE 'COPY[[:space:]]+web/(next\.config|tailwind\.config|postcss\.config)\.[a-z]+' "$df"; then
      err "enumerated web config COPY — use glob 'COPY web/*.{json,ts,mjs,js,cjs}' (F11, F13)"
    else
      if grep -qE 'COPY[[:space:]]+web/\*' "$df"; then
        ok "web configs copied via glob"
      else
        warn "web Dockerfile: could not detect config-file COPY pattern — verify manually"
      fi
    fi
  fi
done

# ── 4. Compose-file checks ──────────────────────────────────────
for cf in $COMPOSE_FILES; do
  say ""
  say "── $cf"

  # 4a. Healthcheck uses 127.0.0.1, not localhost (F12)
  if grep -qE 'healthcheck:' "$cf"; then
    if grep -E 'test:|CMD' "$cf" | grep -q 'localhost'; then
      err "healthcheck uses 'localhost' — must use 127.0.0.1 (F12)"
    else
      ok "healthcheck does not use localhost"
    fi
    # 4b. start_period present
    if grep -q 'start_period' "$cf"; then
      ok "healthcheck has start_period"
    else
      warn "healthcheck missing start_period (A9-3) — false fails on cold start"
    fi
  fi

  # 4c. passes GITEA_NPM_OWNER as build arg (F14)
  if grep -q 'build:' "$cf"; then
    if grep -q 'GITEA_NPM_OWNER' "$cf"; then
      ok "compose passes GITEA_NPM_OWNER build arg"
    else
      warn "compose: no GITEA_NPM_OWNER build arg (F14) — relies on Dockerfile default"
    fi
  fi
done

# ── 5. .dockerignore (F1, warn until A3 ADR migrates) ───────────
if [ -f .dockerignore ]; then
  say ""
  say "── .dockerignore"
  if grep -qE '^pnpm-lock\.yaml$' .dockerignore; then
    warn "pnpm-lock.yaml excluded (F1) — acceptable per ADR-0001 until lockfile policy migrates"
  else
    ok "pnpm-lock.yaml not excluded"
  fi
fi

# ── Summary ─────────────────────────────────────────────────────
say ""
if [ $fail -eq 0 ]; then
  say "✅ docker-doctor: PASS  ($warn_count warning(s))"
  exit 0
else
  echo "❌ docker-doctor: FAIL  ($warn_count warning(s))"
  if $WARN_ONLY; then
    echo "   (--warn-only set: exiting 0)"
    exit 0
  fi
  exit 1
fi
