#!/usr/bin/env bash
# validate-docs-frontmatter.sh - Validate frontmatter on rendered docs pages.
#
# Every docs/**/*.md (excluding docs/internal/ and a hardcoded list mirroring
# src/content.config.ts glob excludes) should have well-formed YAML frontmatter
# with required fields title and description. Optional fields (tags, date)
# validated when present.
#
# Posture: ENFORCING in v2.14.0+ (W10-promoted from advisory). Source-of-truth
# for excluded paths migrated from mkdocs.yml exclude_docs to a hardcoded
# array here (W12 Material deprecation). If src/content.config.ts changes its
# glob excludes, update EXCLUDE_PATHS below to match.
#
# Exit codes:
#   0 - All checked docs pass OR advisory mode (default)
#   1 - Findings AND --strict was passed
#
# Usage:
#   ./scripts/validate-docs-frontmatter.sh
#   ./scripts/validate-docs-frontmatter.sh --strict

set -uo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"

STRICT=false
if [[ "${1:-}" == "--strict" ]]; then
  STRICT=true
fi

echo "=== Docs Frontmatter Validation ==="
echo ""

# Hardcoded exclusion list. Mirrors src/content.config.ts glob excludes
# under docs/. Was previously read from mkdocs.yml exclude_docs in v2.13.x.
# Trailing slash means "directory prefix"; no trailing slash means "exact file".
EXCLUDE_PATHS=(
  "templates/"
  # GitHub-directory landing pages (canonical user-facing pages are sibling
  # index.md / index.mdx); pattern established W4 + W13 B2.5 + W13 FU7
  "workflows/README.md"
  "reference/README.md"
  "skills/README.md"
  "guides/README.md"
  "concepts/README.md"
  "contributing/README.md"
  "getting-started/README.md"
  "showcase/README.md"
  "releases/README.md"
)

# Auto-skip patterns: rendered docs that legitimately may not need frontmatter
AUTO_SKIP_PATTERNS=(
  "templates/*"
  "skills/*"        # auto-generated; description from SKILL.md frontmatter has different length rules
  "workflows/*"     # auto-generated from _workflows/
  "showcase/*"      # auto-generated
  "reference/commands.md"  # auto-generated by generate-skill-pages.py
  "releases/Release_v*.md" # release notes; transitively reachable from releases/index.md
)

is_excluded() {
  local fs_file="$1"
  local exc_path
  for exc_path in "${EXCLUDE_PATHS[@]}"; do
    if [[ "$exc_path" == */ ]]; then
      [[ "$fs_file" == "${exc_path}"* ]] && return 0
    else
      [[ "$fs_file" == "$exc_path" ]] && return 0
    fi
  done

  for pattern in "${AUTO_SKIP_PATTERNS[@]}"; do
    [[ "$fs_file" == $pattern ]] && return 0
  done
  return 1
}

# Collect docs files to validate. Includes both .md and .mdx (v2.14.2 scope
# expansion per Codex P2: src/content.config.ts mounts both extensions, and
# docs/index.mdx is the Starlight homepage; mirrors check-internal-link-validity).
FS_FILES=$(find "$ROOT/docs" \( -name "*.md" -o -name "*.mdx" \) -type f \
  | grep -v "/docs/internal/" \
  | sed "s|$ROOT/docs/||" \
  | sort)

FAIL_COUNT=0
WARN_COUNT=0
CHECKED=0
FINDINGS=()

while IFS= read -r fs_file; do
  [[ -z "$fs_file" ]] && continue
  if is_excluded "$fs_file"; then continue; fi

  full_path="$ROOT/docs/$fs_file"
  CHECKED=$((CHECKED + 1))

  # Read first line and detect frontmatter delimiter
  first_line=$(head -1 "$full_path" 2>/dev/null || echo "")

  if [[ "$first_line" != "---" ]]; then
    FINDINGS+=("docs/$fs_file: missing frontmatter delimiter (first line is not '---')")
    WARN_COUNT=$((WARN_COUNT + 1))
    continue
  fi

  # Find closing delimiter
  end_line=$(awk 'NR>1 && /^---$/ { print NR; exit }' "$full_path")
  if [[ -z "$end_line" ]]; then
    FINDINGS+=("docs/$fs_file: frontmatter has opening '---' but no closing '---'")
    FAIL_COUNT=$((FAIL_COUNT + 1))
    continue
  fi

  # Extract frontmatter block
  frontmatter=$(sed -n "2,$((end_line - 1))p" "$full_path")

  # Check required: title
  title=$(echo "$frontmatter" | grep -m1 '^title:' | sed -E 's/^title:[[:space:]]*//' | sed 's/^"//;s/"$//;s/^'\''//;s/'\''$//')
  if [[ -z "$title" ]]; then
    FINDINGS+=("docs/$fs_file: missing required field 'title'")
    FAIL_COUNT=$((FAIL_COUNT + 1))
  elif [[ ${#title} -gt 80 ]]; then
    FINDINGS+=("docs/$fs_file: title exceeds 80 chars (${#title})")
    FAIL_COUNT=$((FAIL_COUNT + 1))
  fi

  # Check required: description (handles single-line and block-scalar forms)
  description_raw=$(echo "$frontmatter" | grep -m1 '^description:' | sed -E 's/^description:[[:space:]]*//')

  if [[ "$description_raw" =~ ^[\>\|] ]]; then
    # Block scalar (description: >- or description: |): collect indented lines after the marker
    desc_block=$(awk '
      /^description:[[:space:]]*[>|]/ { in_desc=1; next }
      in_desc && /^[a-z]/ { in_desc=0 }
      in_desc { gsub(/^[[:space:]]+/, ""); print }
    ' <<< "$frontmatter" | tr '\n' ' ' | sed -E 's/[[:space:]]+/ /g; s/^[[:space:]]+//; s/[[:space:]]+$//')
    description="$desc_block"
  else
    # Single-line: strip surrounding quotes
    description=$(echo "$description_raw" | sed 's/^"//;s/"$//;s/^'\''//;s/'\''$//')
  fi

  if [[ -z "$description" ]]; then
    FINDINGS+=("docs/$fs_file: missing required field 'description'")
    FAIL_COUNT=$((FAIL_COUNT + 1))
  else
    desc_len=${#description}
    if [[ $desc_len -lt 50 ]]; then
      FINDINGS+=("docs/$fs_file: description shorter than 50 chars ($desc_len)")
      WARN_COUNT=$((WARN_COUNT + 1))
    elif [[ $desc_len -gt 300 ]]; then
      FINDINGS+=("docs/$fs_file: description exceeds 300 chars ($desc_len)")
      WARN_COUNT=$((WARN_COUNT + 1))
    fi
  fi

done <<< "$FS_FILES"

echo "Files checked: $CHECKED"
echo "Excluded (EXCLUDE_PATHS or auto-skip): $(($(echo "$FS_FILES" | grep -c .) - CHECKED))"
echo ""

TOTAL=$((FAIL_COUNT + WARN_COUNT))

if [[ $TOTAL -eq 0 ]]; then
  echo "PASS: All $CHECKED docs have valid frontmatter."
  exit 0
fi

echo "Frontmatter findings: $FAIL_COUNT failures, $WARN_COUNT warnings"
echo ""
for f in "${FINDINGS[@]:0:50}"; do
  echo "  $f"
done

if [[ ${#FINDINGS[@]} -gt 50 ]]; then
  echo ""
  echo "  ... and $((${#FINDINGS[@]} - 50)) more"
fi
echo ""

if [[ "$STRICT" == "true" ]]; then
  echo "FAIL (--strict): $TOTAL frontmatter finding(s)."
  exit 1
else
  echo "WARN: $TOTAL frontmatter finding(s) (advisory mode)."
  echo "  CI runs this script without --strict; pass --strict for enforcing local runs."
  exit 0
fi
