#!/bin/bash
#==============================================================================
# Momentry Core — Fresh Install Script
# Usage: bash install_momentry.sh
#
# Phases:
#   1. 環境 (Environment) — system prereqs, service dependencies, config
#   2. Core — build/install the core binary (API server)
#   3. Worker — build/install the worker binary (pipeline processor)
#   4. Agents/Processors — processor scripts, Python deps, verification
#==============================================================================

set -euo pipefail

PROJECT_DIR="$(cd "$(dirname "$0")/../.." && pwd)"
COLOR=true

# ─── Color helpers ───
if [ "$COLOR" = true ]; then
    R='\033[0;31m'; G='\033[0;32m'; Y='\033[1;33m'; B='\033[0;34m'; C='\033[0;36m'; N='\033[0m'
else
    R=''; G=''; Y=''; B=''; C=''; N=''
fi
ok()    { echo -e "  ${G}✓${N} $1"; }
fail()  { echo -e "  ${R}✗${N} $1"; FAILURES+=("$1"); }
info()  { echo -e "  ${B}→${N} $1"; }
warn()  { echo -e "  ${Y}⚠${N} $1"; }
header(){ echo -e "\n${C}─── $1 ───${N}"; }
sub()   { echo -e "\n  ${B}$1${N}"; }

FAILURES=()

cd "$PROJECT_DIR"
echo -e "${C}========================================${N}"
echo -e "${C}  Momentry Core — Fresh Install${N}"
echo -e "${C}  Project: $PROJECT_DIR${N}"
echo -e "${C}  Date:    $(date '+%Y-%m-%d %H:%M:%S')${N}"
echo -e "${C}========================================${N}"

# ═══════════════════════════════════════════════════════════════
# Phase 1: 環境 (Environment)
# ═══════════════════════════════════════════════════════════════
header "Phase 1/4 — 環境 (Environment)"

# ── 1a. System prerequisites ──
sub "System prerequisites"

if xcode-select -p &>/dev/null; then
    ok "Xcode CLI tools"
else
    info "Installing Xcode Command Line Tools..."
    xcode-select --install || true
    echo "    Press any key after installation completes, then re-run."
    read -rn1
    xcode-select -p &>/dev/null && ok "Xcode CLI tools" || fail "Xcode CLI tools"
fi

if command -v brew &>/dev/null; then
    ok "Homebrew $(brew --version | head -1)"
else
    info "Installing Homebrew..."
    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
    eval "$(/opt/homebrew/bin/brew shellenv)"
    command -v brew &>/dev/null && ok "Homebrew" || fail "Homebrew"
fi

for tool in git curl jq wget tree cmake pkg-config; do
    command -v "$tool" &>/dev/null || brew install "$tool" &>/dev/null || true
done
ok "Basic tools (git curl jq wget tree cmake pkg-config)"

if command -v rustc &>/dev/null; then
    ok "Rust $(rustc --version)"
else
    info "Installing Rust via rustup..."
    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
    source "$HOME/.cargo/env"
    rustc --version &>/dev/null && ok "Rust" || fail "Rust"
fi

PYTHON_BIN="${MOMENTRY_PYTHON_PATH:-/opt/homebrew/bin/python3.11}"
if [ ! -f "$PYTHON_BIN" ]; then
    info "Installing Python 3.11 via Homebrew..."
    brew install python@3.11
    PYTHON_BIN="/opt/homebrew/bin/python3.11"
fi
ok "Python 3.11 ($PYTHON_BIN)"

# PG build deps
for dep in readline zlib icu4c openssl e2fsprogs; do
    brew list "$dep" &>/dev/null 2>&1 || brew install "$dep" &>/dev/null || true
done
ok "Build deps (readline zlib icu4c openssl e2fsprogs)"

# ── 1b. Service dependencies ──
sub "Service dependencies"

# PostgreSQL
PG_BIN="${PG_BIN:-$HOME/pgsql/18.3/bin}"
PG_DATA="${PG_DATA:-$HOME/pgsql/data}"
if [ ! -f "$PG_BIN/postgres" ]; then
    info "Building PostgreSQL 18.3 from source..."
    bash "$PROJECT_DIR/scripts/setup/01_postgresql.sh"
fi
"$PG_BIN/pg_isready" -q 2>/dev/null || "$PG_BIN/pg_ctl" -D "$PG_DATA" -l "$HOME/pgsql/pg.log" start 2>/dev/null || true
sleep 2
"$PG_BIN/pg_isready" -q 2>/dev/null && ok "PostgreSQL" || fail "PostgreSQL"

DB_NAME="${DB_NAME:-momentry}"
"$PG_BIN/psql" -U accusys -d postgres -tc "SELECT 1 FROM pg_database WHERE datname='$DB_NAME'" | grep -q 1 || \
    "$PG_BIN/createdb" -U accusys "$DB_NAME" 2>/dev/null || true
"$PG_BIN/psql" -U accusys -d "$DB_NAME" -c "CREATE EXTENSION IF NOT EXISTS vector" &>/dev/null || true
"$PG_BIN/psql" -U accusys -d "$DB_NAME" -c "SELECT 1" &>/dev/null && ok "Database '$DB_NAME'" || fail "Database '$DB_NAME'"

# Redis
redis-cli ping 2>/dev/null | grep -q PONG || { brew install redis &>/dev/null && brew services start redis &>/dev/null || true; }
redis-cli ping 2>/dev/null | grep -q PONG && ok "Redis" || fail "Redis"

# MongoDB
if ! command -v mongosh &>/dev/null || ! mongosh --quiet --eval "db.adminCommand('ping')" &>/dev/null; then
    brew tap mongodb/brew &>/dev/null
    brew install mongodb-community &>/dev/null && brew services start mongodb-community &>/dev/null || true
    sleep 3
fi
mongosh --quiet --eval "db.adminCommand('ping')" &>/dev/null && ok "MongoDB" || fail "MongoDB"

# Qdrant
QDRANT_BIN="$PROJECT_DIR/services/qdrant/target/release/qdrant"
if [ ! -f "$QDRANT_BIN" ] && [ -d "$PROJECT_DIR/services/qdrant" ]; then
    info "Building Qdrant..."
    cd "$PROJECT_DIR/services/qdrant" && cargo build --release --bin qdrant 2>&1 | tail -3 && cd "$PROJECT_DIR"
fi
if curl -sf http://localhost:6333/healthz &>/dev/null; then
    ok "Qdrant"
elif [ -f "$QDRANT_BIN" ]; then
    nohup "$QDRANT_BIN" > "$HOME/qdrant.log" 2>&1 &
    for i in $(seq 1 15); do sleep 2; curl -sf http://localhost:6333/healthz &>/dev/null && break; done
    curl -sf http://localhost:6333/healthz &>/dev/null && ok "Qdrant" || fail "Qdrant"
else
    warn "Qdrant source not found — skip (will need manual setup)"
fi

# ── 1c. External tools ──
sub "External tools"
for tool in ffmpeg ffprobe rsync yt-dlp; do
    command -v "$tool" &>/dev/null || brew install "$tool" &>/dev/null || true
done
ok "ffmpeg ffprobe rsync yt-dlp"

command -v soffice &>/dev/null || brew install --cask libreoffice &>/dev/null || true
command -v soffice &>/dev/null && ok "LibreOffice" || warn "LibreOffice"

# ── 1d. Configuration ──
sub "Configuration"
[ -f "$PROJECT_DIR/.env" ] && ok ".env exists" || {
    cp "$PROJECT_DIR/.env.example" "$PROJECT_DIR/.env"
    ok ".env created from template"
    info "  Edit $PROJECT_DIR/.env to customize settings"
}
WATCH_DIR="${MOMENTRY_SFTP_ROOT:-$PROJECT_DIR/storage/watch}"
for d in output output_dev thumbnails storage data; do mkdir -p "$PROJECT_DIR/$d"; done
mkdir -p "$WATCH_DIR"
ok "Directories (output output_dev thumbnails storage data watch)"
"$PG_BIN/psql" -U accusys -d "$DB_NAME" -c "CREATE SCHEMA IF NOT EXISTS dev" &>/dev/null
"$PG_BIN/psql" -U accusys -d "$DB_NAME" -c "CREATE SCHEMA IF NOT EXISTS public" &>/dev/null
ok "Database schemas (dev, public)"

# Git repo (build.rs needs git hash)
if [ ! -d "$PROJECT_DIR/.git" ]; then
    git init && git add -A && git commit -m "init" 2>/dev/null || true
fi
ok "Git repository (for build hash)"

# ── 1e. Startup check ──
sub "Startup check"
cd "$PROJECT_DIR"
"$PG_BIN/pg_isready" -q 2>/dev/null && ok "PostgreSQL" || fail "PostgreSQL"
redis-cli ping 2>/dev/null | grep -q PONG && ok "Redis" || fail "Redis"
mongosh --quiet --eval "db.adminCommand('ping')" &>/dev/null && ok "MongoDB" || fail "MongoDB"
curl -sf http://localhost:6333/healthz &>/dev/null && ok "Qdrant" || warn "Qdrant"
ok "All core services verified"

# ═══════════════════════════════════════════════════════════════
# Phase 2: Core (API server binary)
# ═══════════════════════════════════════════════════════════════
header "Phase 2/4 — Core (API Server)"

cd "$PROJECT_DIR"

# Migrations — apply all release/migrate_*.sql in order
for mig in "$PROJECT_DIR"/release/migrate_*.sql; do
    [ ! -f "$mig" ] && continue
    MIG_NAME=$(basename "$mig")
    MIG_HASH=$(shasum -a 256 "$mig" | awk '{print $1}')
    # Check if already applied
    ALREADY=$("$PG_BIN/psql" -U accusys -d "$DB_NAME" -t -A -c \
        "SELECT COUNT(*) FROM schema_migrations WHERE filename='$MIG_NAME'" 2>/dev/null || echo "0")
    if [ "$ALREADY" -gt 0 ]; then
        ok "Migration $MIG_NAME (already applied)"
        continue
    fi
    # Apply migration
    T0=$(date +%s%N)
    if "$PG_BIN/psql" -U accusys -d "$DB_NAME" -f "$mig" &>/dev/null; then
        T1=$(date +%s%N)
        DURATION_MS=$(( (T1 - T0) / 1000000 ))
        # Record in schema_migrations
        "$PG_BIN/psql" -U accusys -d "$DB_NAME" -c \
            "INSERT INTO schema_migrations (filename, checksum, duration_ms) VALUES ('$MIG_NAME', '$MIG_HASH', $DURATION_MS) ON CONFLICT (filename) DO UPDATE SET checksum=EXCLUDED.checksum" &>/dev/null || true
        ok "Migration $MIG_NAME (${DURATION_MS}ms)"
    else
        fail "Migration $MIG_NAME FAILED"
    fi
done
ok "Database migrations applied"

# Build core binary
info "Building momentry_playground (API + worker binary)..."
cargo build --bin momentry_playground 2>&1 | tail -3
if [ -f "$PROJECT_DIR/target/debug/momentry_playground" ]; then
    ok "momentry_playground binary ($(ls -lh target/debug/momentry_playground | awk '{print $5}'))"
else
    fail "momentry_playground build"
fi

# Start API server
if curl -sf http://127.0.0.1:3003/health &>/dev/null; then
    ok "API server already running"
else
    DATABASE_SCHEMA=dev nohup target/debug/momentry_playground server --port 3003 \
        > "$PROJECT_DIR/playground_boot.log" 2>&1 &
    for i in $(seq 1 10); do sleep 2; curl -sf http://127.0.0.1:3003/health &>/dev/null && break; done
    curl -sf http://127.0.0.1:3003/health &>/dev/null && \
        ok "API server started (port 3003)" || fail "API server start"
fi

# Health check
HEALTH=$(curl -sf http://127.0.0.1:3003/health 2>/dev/null || echo '{"status":"error"}')
echo "$HEALTH" | python3 -c "
import json,sys; d=json.load(sys.stdin)
print(f'    Version:   {d.get(\"version\",\"?\")}')
print(f'    Build:     {d.get(\"build_git_hash\",\"?\")}')
print(f'    Timestamp: {d.get(\"build_timestamp\",\"?\")}')
print(f'    Status:    {d.get(\"status\",\"?\")}')" 2>/dev/null
echo "$HEALTH" | python3 -c "import json,sys;d=json.load(sys.stdin);exit(0 if d.get('status')=='ok' else 1)" 2>/dev/null && \
    ok "Health: ok" || warn "Health: degraded"

# ═══════════════════════════════════════════════════════════════
# Phase 3: Worker (pipeline processing binary)
# ═══════════════════════════════════════════════════════════════
header "Phase 3/4 — Worker (Pipeline Processor)"

# Worker is the same binary (`momentry_playground worker`), already built above.
# This phase verifies it can start and pick up jobs.

# Test worker configuration
info "Worker binary: target/debug/momentry_playground"
info "Worker command: DATABASE_SCHEMA=dev ./target/debug/momentry_playground worker --max-concurrent 2 --poll-interval 5"

# Create Qdrant collection for dev
QDRANT_COLLECTION="${QDRANT_COLLECTION:-momentry_dev_rule1_v2}"
EXISTS=$(curl -sf "http://localhost:6333/collections/$QDRANT_COLLECTION" 2>/dev/null | \
    python3 -c "import sys,json;d=json.load(sys.stdin);print(d.get('result',{}).get('status','not_found'))" 2>/dev/null || echo "error")
if [ "$EXISTS" = "not_found" ] || [ "$EXISTS" = "error" ]; then
    curl -sf -X PUT "http://localhost:6333/collections/$QDRANT_COLLECTION" \
        -H "Content-Type: application/json" \
        -d '{"vectors":{"size":768,"distance":"Cosine"}}' &>/dev/null || true
fi
curl -sf "http://localhost:6333/collections/$QDRANT_COLLECTION" &>/dev/null && \
    ok "Qdrant collection '$QDRANT_COLLECTION'" || warn "Qdrant collection"

# Test worker dry-run (verify it can at least parse config)
info "Verifying worker can start (dry-run)..."
DATABASE_SCHEMA=dev timeout 5 ./target/debug/momentry_playground worker \
    --max-concurrent 1 --poll-interval 10 2>&1 | head -5 || true

ok "Worker binary verified"

# ═══════════════════════════════════════════════════════════════
# Phase 4: Watcher (File Detection)
# ═══════════════════════════════════════════════════════════════
header "Phase 4/5 — Watcher (File Detection)"

# Watcher is embedded in the server binary — auto-starts with `momentry_playground server`.
# It polls the watch directory every 60s, detecting new files (detection only,
# never auto-modifies). Configuration via MOMENTRY_SFTP_ROOT env var.

WATCH_DIR="${MOMENTRY_SFTP_ROOT:-$PROJECT_DIR/storage/watch}"
mkdir -p "$WATCH_DIR"

# Verify watcher auto-started with the server (check logs for [WATCHER] message)
if [ -f "$PROJECT_DIR/playground_boot.log" ] && grep -q "\[WATCHER\]" "$PROJECT_DIR/playground_boot.log" 2>/dev/null; then
    ok "Watcher started (check server logs for [WATCHER] messages)"
elif curl -sf http://127.0.0.1:3003/health &>/dev/null; then
    # Server is running — watcher should be running inside it
    ok "Watcher should be running (auto-started with server)"
    info "  Watch dir: $WATCH_DIR"
    info "  Poll interval: 60s"
else
    warn "Watcher status unknown (server not running)"
fi

# Place a test marker file to confirm watcher detects it
TEST_MARKER="$WATCH_DIR/.watcher_test_$(date +%s)"
touch "$TEST_MARKER" 2>/dev/null && ok "Watcher directory writable" || warn "Watcher directory not writable"
rm -f "$TEST_MARKER" 2>/dev/null || true

# ═══════════════════════════════════════════════════════════════
# Phase 5: Agents/Processors (Python scripts + ML models)
# ═══════════════════════════════════════════════════════════════
header "Phase 5/5 — Agents/Processors"

# ── 4a. Python dependencies ──
sub "Python packages"
for pkg in PyPDF2 python-docx openpyxl python-pptx; do
    if "$PYTHON_BIN" -c "import ${pkg%%=*}" &>/dev/null 2>&1; then
        ok "$pkg"
    else
        "$PYTHON_BIN" -m pip install "$pkg" --quiet && ok "$pkg" || warn "$pkg"
    fi
done

# ── 4b. Processor script inventory ──
sub "Processor script inventory"
cd "$PROJECT_DIR"
SCRIPT_COUNT=$(find scripts -name '*.py' -type f 2>/dev/null | wc -l | tr -d ' ')
ok "Total .py files: $SCRIPT_COUNT"

# Check core processor scripts (required)
PROCESSORS=(asr_processor yolo_processor face_processor pose_processor \
            ocr_processor cut_processor caption_processor scene_classifier \
            story_processor asrx_processor probe_file visual_chunk_processor)
MISSING=0
for p in "${PROCESSORS[@]}"; do
    FILE=$(find scripts -name "${p}.py" -type f 2>/dev/null | head -1)
    if [ -n "$FILE" ]; then
        ok "Processor: $p"
    else
        warn "Processor: $p — not found"
        MISSING=$((MISSING + 1))
    fi
done

# ── 4c. Script integrity (SHA256 check) ──
sub "Script integrity (SHA256)"
CHECKSUMS_FILE="$PROJECT_DIR/scripts/checksums.sha256"
CS_TOTAL=0; CS_PASS=0; CS_FAIL=0
if [ -f "$CHECKSUMS_FILE" ]; then
    while IFS= read -r line; do
        [ -z "$line" ] && continue
        EXPECTED_HASH=$(echo "$line" | awk '{print $1}')
        FILE_PATH=$(echo "$line" | awk '{print $2}')
        FULL_PATH="$PROJECT_DIR/scripts/$FILE_PATH"
        CS_TOTAL=$((CS_TOTAL + 1))
        if [ -f "$FULL_PATH" ]; then
            ACTUAL_HASH=$(shasum -a 256 "$FULL_PATH" 2>/dev/null | awk '{print $1}')
            if [ "$ACTUAL_HASH" = "$EXPECTED_HASH" ]; then
                CS_PASS=$((CS_PASS + 1))
            else
                CS_FAIL=$((CS_FAIL + 1))
                [ $CS_FAIL -le 5 ] && warn "$FILE_PATH — hash mismatch"
            fi
        else
            CS_FAIL=$((CS_FAIL + 1))
            [ $CS_FAIL -le 5 ] && warn "$FILE_PATH — not found"
        fi
    done < "$CHECKSUMS_FILE"
    ok "$CS_PASS/$CS_TOTAL scripts match checksums"
    [ $CS_FAIL -gt 0 ] && fail "Script integrity: $CS_FAIL mismatches" || true
else
    warn "checksums.sha256 not found — skipping integrity check"
fi

# ── 4d. ML models ──
sub "ML models"
MODEL_COUNT=$(find models -type f 2>/dev/null | wc -l | tr -d ' ')
ok "Model files: $MODEL_COUNT"

# ── 4e. Processor verification via API ──
sub "Processor verification"
DETAILED=$(curl -sf http://127.0.0.1:3003/health/detailed 2>/dev/null || echo '{}')
echo "$DETAILED" | python3 -c "
import json,sys
d=json.load(sys.stdin)
p=d.get('pipeline',{})
proc=p.get('processors',{})
print(f'    Scripts dir:  {p.get(\"scripts_ready\",\"?\")}')
print(f'    Script count: {p.get(\"scripts_count\",\"?\")}')
print(f'    Models dir:   {p.get(\"models_ready\",\"?\")}')
print(f'    Model count:  {p.get(\"models_count\",\"?\")}')
print(f'    ffmpeg:       {p.get(\"ffmpeg\",\"?\")}')
print(f'    Embedding:    {p.get(\"embedding_server\",{}).get(\"status\",\"?\")}')
print(f'    LLM:          {p.get(\"llm\",{}).get(\"status\",\"?\")}')
print(f'    rsync:        {p.get(\"rsync\",{}).get(\"status\",\"?\")}')
print(f'    Embedding srv:{p.get(\"embedding_server\",{}).get(\"status\",\"?\")}')
for k in ['asr','yolo','face','pose','ocr','cut','caption','scene','story','asrx','probe','visual_chunk']:
    print(f'    {k}: {\"✓\" if proc.get(k) else \"✗\"}')
" 2>/dev/null

# ── 4f. API smoke test ──
sub "API smoke test"
API_KEY="${MOMENTRY_API_KEY:-muser_68600856036340bcafc01930eb4bd839_1774418104_97221b69}"
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" \
    -H "X-API-Key: $API_KEY" \
    "http://127.0.0.1:3003/api/v1/videos?page=1&page_size=1" 2>/dev/null || echo "000")
ok "GET /api/v1/videos → HTTP $STATUS"

# ═══════════════════════════════════════════════════════════════
# Summary
# ═══════════════════════════════════════════════════════════════
echo ""
echo -e "${C}========================================${N}"
if [ ${#FAILURES[@]} -eq 0 ]; then
    echo -e "${G}  Install Complete — All Checks Passed${N}"
else
    echo -e "${Y}  Install Complete — ${#FAILURES[@]} Warnings${N}"
    for f in "${FAILURES[@]}"; do echo -e "  ${R}✗${N} $f"; done
fi
echo -e "${C}========================================${N}"
echo ""
echo "  API:        http://127.0.0.1:3003"
echo "  Project:    $PROJECT_DIR"
echo "  Config:     $PROJECT_DIR/.env"
echo "  Scripts:    $SCRIPT_COUNT .py files"
echo "  Watch dir:  $WATCH_DIR"
echo ""
echo "  Commands:"
echo "    Start server:  DATABASE_SCHEMA=dev ./target/debug/momentry_playground server --port 3003"
echo "    Start worker:  DATABASE_SCHEMA=dev ./target/debug/momentry_playground worker --max-concurrent 2"
echo "    Quick check:   bash scripts/setup/check_momentry.sh"
echo "    Upgrade:       bash scripts/setup/upgrade_momentry.sh <delivery_dir>"
echo ""
echo "  Components installed:"
echo "    ✓ 環境 (Environment)   — services, tools, config"
echo "    ✓ Core                — API server (port 3003)"
echo "    ✓ Worker              — pipeline processor"
echo "    ✓ Watcher             — file detection (auto-started with server)"
echo "    ✓ Agents/Processors   — $SCRIPT_COUNT .py scripts, 12 processors"
echo ""

exit $([ ${#FAILURES[@]} -eq 0 ] && echo 0 || echo 1)
