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

# sync.sh — Extract key facts from service repos and update insights-ai service docs.
#
# Appends/updates a <!-- SYNC:START --> ... <!-- SYNC:END --> block at the bottom
# of each services/*.md file with machine-extracted metadata (versions, deps, commands).
# Hand-written content above the SYNC block is never touched.
#
# Usage:
#   ./sync.sh                     # Sync all services
#   ./sync.sh graphql-product     # Sync a single service
#   ./sync.sh --dry-run           # Preview changes without writing
#   ./sync.sh --diff              # Show diff after syncing
#   ./sync.sh --help              # Show help

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PARENT_DIR="$(dirname "$SCRIPT_DIR")"
SERVICES_DIR="${SCRIPT_DIR}/services"

DRY_RUN=false
SHOW_DIFF=false
TARGETS=()

# ANSI colors
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
RED='\033[0;31m'
DIM='\033[2m'
BOLD='\033[1m'
RESET='\033[0m'

log_info()   { echo -e "${BLUE}[INFO]${RESET}  $*"; }
log_ok()     { echo -e "${GREEN}[ OK ]${RESET}  $*"; }
log_warn()   { echo -e "${YELLOW}[WARN]${RESET}  $*"; }
log_err()    { echo -e "${RED}[ERR]${RESET}  $*"; }
log_skip()   { echo -e "${DIM}[SKIP]${RESET}  $*"; }

# ---------------------------------------------------------------------------
# Service type detection
# ---------------------------------------------------------------------------

detect_type() {
    local svc_dir="$1"
    if [[ -f "${svc_dir}/Cargo.toml" ]]; then
        echo "rust"
    elif [[ -f "${svc_dir}/dbt_project.yml" ]]; then
        echo "dbt"
    elif [[ -f "${svc_dir}/package.json" ]]; then
        echo "node"
    elif [[ -f "${svc_dir}/Pipfile" ]] || [[ -f "${svc_dir}/requirements.txt" ]] || [[ -f "${svc_dir}/setup.py" ]]; then
        echo "python"
    else
        echo "unknown"
    fi
}

# ---------------------------------------------------------------------------
# Extractors by service type
# ---------------------------------------------------------------------------

extract_node() {
    local svc_dir="$1"
    local pkg="${svc_dir}/package.json"

    if [[ ! -f "$pkg" ]]; then
        echo "No package.json found"
        return
    fi

    echo "#### Package Info"
    echo ""
    echo '```'
    python3 -c "
import json, sys
d = json.load(open('${pkg}'))
print(f\"Name: {d.get('name', 'N/A')}\")
print(f\"Version: {d.get('version', 'N/A')}\")
engines = d.get('engines', {})
if engines:
    for k, v in engines.items():
        print(f\"{k.capitalize()}: {v}\")
" 2>/dev/null || echo "  (parse error)"
    echo '```'

    # Node version from .nvmrc or .node-version
    if [[ -f "${svc_dir}/.nvmrc" ]]; then
        echo ""
        echo "**Node version (.nvmrc):** $(cat "${svc_dir}/.nvmrc")"
    elif [[ -f "${svc_dir}/.node-version" ]]; then
        echo ""
        echo "**Node version (.node-version):** $(cat "${svc_dir}/.node-version")"
    fi

    # Scripts
    echo ""
    echo "#### Available Scripts"
    echo ""
    echo '```'
    python3 -c "
import json
d = json.load(open('${pkg}'))
for k in sorted(d.get('scripts', {}).keys()):
    print(f'  {k}')
" 2>/dev/null || echo "  (parse error)"
    echo '```'

    # Key dependencies
    echo ""
    echo "#### Key Dependencies"
    echo ""
    echo '```'
    python3 -c "
import json
d = json.load(open('${pkg}'))
deps = d.get('dependencies', {})
important = [k for k in deps if any(p in k for p in ['apollo', 'theorchard', 'graphql', 'sentry', 'datadog', 'redis', 'opensearch', 'neo4j', 'snowflake', 'zod', 'express'])]
for k in sorted(important):
    print(f'  {k}: {deps[k]}')
if not important:
    for k in sorted(list(deps.keys())[:15]):
        print(f'  {k}: {deps[k]}')
" 2>/dev/null || echo "  (parse error)"
    echo '```'

    # TypeScript config
    if [[ -f "${svc_dir}/tsconfig.json" ]]; then
        echo ""
        echo "#### TypeScript Config"
        echo ""
        echo '```'
        python3 -c "
import json
d = json.load(open('${svc_dir}/tsconfig.json'))
co = d.get('compilerOptions', {})
print(f\"  target: {co.get('target', 'N/A')}\")
print(f\"  module: {co.get('module', 'N/A')}\")
print(f\"  strict: {co.get('strict', 'N/A')}\")
" 2>/dev/null || echo "  (parse error)"
        echo '```'
    fi
}

extract_python() {
    local svc_dir="$1"

    # Python version
    if [[ -f "${svc_dir}/.python-version" ]]; then
        echo "**Python version:** $(cat "${svc_dir}/.python-version")"
        echo ""
    fi

    # Pipfile dependencies
    if [[ -f "${svc_dir}/Pipfile" ]]; then
        echo "#### Dependencies (Pipfile)"
        echo ""
        echo '```'
        python3 -c "
import re
in_packages = False
with open('${svc_dir}/Pipfile') as f:
    for line in f:
        line = line.strip()
        if line == '[packages]':
            in_packages = True
            continue
        elif line.startswith('['):
            in_packages = False
            continue
        if in_packages and '=' in line:
            print(f'  {line}')
" 2>/dev/null || echo "  (parse error)"
        echo '```'
    elif [[ -f "${svc_dir}/requirements.txt" ]]; then
        echo "#### Dependencies (requirements.txt)"
        echo ""
        echo '```'
        head -20 "${svc_dir}/requirements.txt" | sed 's/^/  /'
        echo '```'
    fi

    # Makefile targets
    if [[ -f "${svc_dir}/Makefile" ]]; then
        echo ""
        echo "#### Makefile Targets"
        echo ""
        echo '```'
        grep -E '^[a-zA-Z_-]+:' "${svc_dir}/Makefile" 2>/dev/null | sed 's/:.*//; s/^/  /' | sort -u || echo "  (none found)"
        echo '```'
    fi
}

extract_rust() {
    local svc_dir="$1"
    local cargo="${svc_dir}/Cargo.toml"

    if [[ ! -f "$cargo" ]]; then
        echo "No Cargo.toml found"
        return
    fi

    echo "#### Cargo Package"
    echo ""
    echo '```'
    python3 -c "
lines = open('${cargo}').readlines()
in_package = False
in_deps = False
for line in lines:
    s = line.strip()
    if s == '[package]':
        in_package = True
        in_deps = False
        continue
    elif s == '[dependencies]':
        in_deps = True
        in_package = False
        continue
    elif s.startswith('['):
        in_package = False
        in_deps = False
        continue
    if in_package and '=' in s:
        print(f'  {s}')
    elif in_deps and '=' in s:
        print(f'  {s}')
" 2>/dev/null || echo "  (parse error)"
    echo '```'

    # Rust toolchain
    if [[ -f "${svc_dir}/rust-toolchain.toml" ]]; then
        echo ""
        echo "**Rust toolchain:** $(grep 'channel' "${svc_dir}/rust-toolchain.toml" 2>/dev/null | head -1 || echo 'unknown')"
    elif [[ -f "${svc_dir}/rust-toolchain" ]]; then
        echo ""
        echo "**Rust toolchain:** $(cat "${svc_dir}/rust-toolchain")"
    fi

    # Makefile targets
    if [[ -f "${svc_dir}/Makefile" ]]; then
        echo ""
        echo "#### Makefile Targets"
        echo ""
        echo '```'
        grep -E '^[a-zA-Z_-]+:' "${svc_dir}/Makefile" 2>/dev/null | sed 's/:.*//; s/^/  /' | sort -u || echo "  (none found)"
        echo '```'
    fi
}

extract_dbt() {
    local svc_dir="$1"

    # Python version
    if [[ -f "${svc_dir}/.python-version" ]]; then
        echo "**Python version:** $(cat "${svc_dir}/.python-version")"
        echo ""
    fi

    # dbt_project.yml key fields
    if [[ -f "${svc_dir}/dbt_project.yml" ]]; then
        echo "#### dbt Project"
        echo ""
        echo '```'
        grep -E '^\s*(name|version|profile|model-paths|test-paths|seed-paths):' "${svc_dir}/dbt_project.yml" 2>/dev/null | sed 's/^/  /' || echo "  (parse error)"
        echo '```'
    fi

    # Pipfile deps
    if [[ -f "${svc_dir}/Pipfile" ]]; then
        echo ""
        echo "#### Dependencies (Pipfile)"
        echo ""
        echo '```'
        python3 -c "
in_packages = False
with open('${svc_dir}/Pipfile') as f:
    for line in f:
        line = line.strip()
        if line == '[packages]':
            in_packages = True
            continue
        elif line.startswith('['):
            in_packages = False
            continue
        if in_packages and '=' in line:
            print(f'  {line}')
" 2>/dev/null || echo "  (parse error)"
        echo '```'
    fi

    # Makefile targets
    if [[ -f "${svc_dir}/Makefile" ]]; then
        echo ""
        echo "#### Makefile Targets"
        echo ""
        echo '```'
        grep -E '^[a-zA-Z_-]+:' "${svc_dir}/Makefile" 2>/dev/null | sed 's/:.*//; s/^/  /' | sort -u || echo "  (none found)"
        echo '```'
    fi

    # packages.yml (dbt packages)
    if [[ -f "${svc_dir}/packages.yml" ]]; then
        echo ""
        echo "#### dbt Packages (packages.yml)"
        echo ""
        echo '```'
        cat "${svc_dir}/packages.yml" | sed 's/^/  /'
        echo '```'
    fi
}

# ---------------------------------------------------------------------------
# Top-level directory structure
# ---------------------------------------------------------------------------

extract_structure() {
    local svc_dir="$1"
    echo ""
    echo "#### Directory Structure (top-level)"
    echo ""
    echo '```'
    ls -1d "${svc_dir}"/*/ 2>/dev/null | xargs -I{} basename {} | sed 's/^/  /' | head -25 || echo "  (empty)"
    echo '```'
}

# ---------------------------------------------------------------------------
# Sync a single service
# ---------------------------------------------------------------------------

sync_service() {
    local service="$1"
    local svc_dir="${PARENT_DIR}/${service}"
    local doc_file="${SERVICES_DIR}/${service}.md"

    # Skip if service directory doesn't exist
    if [[ ! -d "$svc_dir" ]]; then
        log_skip "${service} (directory not found: ${svc_dir})"
        return
    fi

    # Skip if doc file doesn't exist
    if [[ ! -f "$doc_file" ]]; then
        log_skip "${service} (no doc file: ${doc_file})"
        return
    fi

    local svc_type
    svc_type="$(detect_type "$svc_dir")"

    # Build the sync block content
    local sync_content=""
    sync_content+="<!-- SYNC:START -->"$'\n'
    sync_content+="<!-- Auto-generated by sync.sh on $(date -u '+%Y-%m-%d %H:%M UTC'). Do not edit manually. -->"$'\n'
    sync_content+=""$'\n'
    sync_content+="### Extracted Metadata"$'\n'
    sync_content+=""$'\n'
    sync_content+="**Service type:** ${svc_type}"$'\n'
    sync_content+="**Last synced:** $(date -u '+%Y-%m-%d %H:%M UTC')"$'\n'
    sync_content+=""$'\n'

    # Extract based on type
    case "$svc_type" in
        node)   sync_content+="$(extract_node "$svc_dir")"$'\n' ;;
        python) sync_content+="$(extract_python "$svc_dir")"$'\n' ;;
        rust)   sync_content+="$(extract_rust "$svc_dir")"$'\n' ;;
        dbt)    sync_content+="$(extract_dbt "$svc_dir")"$'\n' ;;
        *)      sync_content+="Unknown service type. Cannot extract metadata."$'\n' ;;
    esac

    # Directory structure for all types
    sync_content+="$(extract_structure "$svc_dir")"$'\n'

    sync_content+=""$'\n'
    sync_content+="<!-- SYNC:END -->"

    # Read existing doc, strip old sync block if present
    local existing_content
    existing_content="$(cat "$doc_file")"

    local stripped_content
    if echo "$existing_content" | grep -q '<!-- SYNC:START -->'; then
        # Remove everything from SYNC:START to SYNC:END (inclusive)
        stripped_content="$(echo "$existing_content" | sed '/<!-- SYNC:START -->/,/<!-- SYNC:END -->/d')"
        # Remove trailing blank lines
        stripped_content="$(echo "$stripped_content" | awk 'NF{found=NR} END{for(i=1;i<=found;i++) print lines[i]} {lines[NR]=$0}')"
    else
        stripped_content="$existing_content"
    fi

    # Combine: original content + blank line + sync block
    local new_content="${stripped_content}"$'\n'$'\n'"${sync_content}"$'\n'

    if $DRY_RUN; then
        # Show what would change
        local tmp_file
        tmp_file="$(mktemp)"
        echo "$new_content" > "$tmp_file"
        if diff -q "$doc_file" "$tmp_file" >/dev/null 2>&1; then
            log_ok "${service} ${DIM}(no changes)${RESET}"
        else
            log_info "${service} ${YELLOW}(would update)${RESET}"
            diff --color=always "$doc_file" "$tmp_file" 2>/dev/null | head -30 || true
        fi
        rm -f "$tmp_file"
    else
        echo "$new_content" > "$doc_file"
        log_ok "${service} ${DIM}(${svc_type})${RESET}"
    fi
}

# ---------------------------------------------------------------------------
# All known services
# ---------------------------------------------------------------------------

ALL_SERVICES=(
    "frontend-insights"
    "orchard-suite"
    "graphql-router"
    "graphql-analytics"
    "graphql-knowledge-search"
    "graphql-knowledge"
    "graphql-product"
    "graphql-user"
    "ows-analytics"
    "ows-charts"
    "ows-playlist"
    "dbt-analytics"
)

# ---------------------------------------------------------------------------
# Usage
# ---------------------------------------------------------------------------

usage() {
    cat << 'EOF'

sync.sh — Keep insights-ai service docs in sync with actual service repos.

USAGE
  ./sync.sh                     Sync all 12 services
  ./sync.sh <service>           Sync a single service
  ./sync.sh --dry-run           Preview changes without writing
  ./sync.sh --diff              Show git diff after syncing
  ./sync.sh --help              Show this help

WHAT IT DOES
  Reads package.json, Pipfile, Cargo.toml, dbt_project.yml, Makefile,
  .nvmrc, .python-version, tsconfig.json, etc. from each service repo
  and appends an auto-generated metadata block to services/<service>.md.

  The block is wrapped in <!-- SYNC:START --> and <!-- SYNC:END --> markers.
  Everything above the markers (hand-written docs) is never touched.

EXAMPLES
  ./sync.sh                           # Sync all services
  ./sync.sh graphql-product           # Sync only graphql-product
  ./sync.sh --dry-run graphql-product # Preview changes for one service
  ./sync.sh --diff                    # Sync all, then show git diff

EOF
}

# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------

main() {
    # Parse args
    for arg in "$@"; do
        case "$arg" in
            --dry-run)  DRY_RUN=true ;;
            --diff)     SHOW_DIFF=true ;;
            --help|-h)  usage; exit 0 ;;
            --*)        log_err "Unknown flag: $arg"; usage; exit 1 ;;
            *)          TARGETS+=("$arg") ;;
        esac
    done

    # Default to all services if no targets specified
    if [[ ${#TARGETS[@]} -eq 0 ]]; then
        TARGETS=("${ALL_SERVICES[@]}")
    fi

    echo ""
    echo -e "${BOLD}insights-ai sync${RESET}"
    echo -e "${DIM}Extracting metadata from service repos into services/*.md${RESET}"
    echo ""

    if $DRY_RUN; then
        echo -e "${YELLOW}DRY RUN — no files will be modified${RESET}"
        echo ""
    fi

    local synced=0
    for service in "${TARGETS[@]}"; do
        # Validate service name
        local valid=false
        for known in "${ALL_SERVICES[@]}"; do
            if [[ "$known" == "$service" ]]; then
                valid=true
                break
            fi
        done

        if ! $valid; then
            log_err "Unknown service: ${service}"
            echo "  Known services: ${ALL_SERVICES[*]}"
            continue
        fi

        sync_service "$service"
        synced=$((synced + 1))
    done

    echo ""
    echo -e "${BOLD}Synced ${synced}/${#TARGETS[@]} services${RESET}"

    if $SHOW_DIFF && ! $DRY_RUN; then
        echo ""
        echo -e "${BOLD}Git diff:${RESET}"
        cd "$SCRIPT_DIR"
        git diff --stat services/ 2>/dev/null || echo "  (not a git repo or no changes)"
        echo ""
        git diff --color services/ 2>/dev/null | head -100 || true
    fi
}

main "$@"
