#!/bin/bash
# scripts/deploy.sh - Automated deploy for SyncServer
#
# Usage:
#   ./scripts/deploy.sh                    # Full deploy
#   ./scripts/deploy.sh --dry-run          # Simulate without making changes
#   ./scripts/deploy.sh --help             # Show help
#   SKIP_BUILD=1 ./scripts/deploy.sh       # Skip rebuild (use existing .deb)
#   LXC_HOST=user@host ./scripts/deploy.sh # Custom target
#
# Environment variables:
#   LXC_HOST       - SSH target (default: root@10.5.1.30)
#   LXC_PORT       - SSH port (default: 22)
#   REMOTE_DIR     - Remote directory (default: ~/move-data-nas)
#   SKIP_BUILD     - Skip make package if 1
#   SKIP_VERIFY    - Skip post-deploy verification if 1
#   LOG_DIR        - Where to write logs (default: logs/)

set -euo pipefail

# === Configuration ===
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
LXC_HOST="${LXC_HOST:-root@10.5.1.30}"
LXC_PORT="${LXC_PORT:-22}"
REMOTE_DIR="${REMOTE_DIR:-~/move-data-nas}"
LOG_DIR="${LOG_DIR:-$PROJECT_DIR/logs}"
SKIP_BUILD="${SKIP_BUILD:-0}"
SKIP_VERIFY="${SKIP_VERIFY:-0}"
DRY_RUN=0
HEALTH_TIMEOUT=30
SSH_OPTS="-o ConnectTimeout=5 -o StrictHostKeyChecking=accept-new -p $LXC_PORT"

# === Setup ===
mkdir -p "$LOG_DIR"
LOG_FILE="$LOG_DIR/deploy_$(date +%Y%m%d_%H%M%S).log"
exec > >(tee -a "$LOG_FILE") 2>&1

# === Helpers ===
log()     { echo "→ $*"; }
success() { echo "✓ $*"; }
warn()    { echo "⚠ $*" >&2; }
die()     { echo "✗ $*" >&2; exit 1; }

run_ssh() { ssh $SSH_OPTS "$LXC_HOST" "$@"; }
run_scp() { scp -P "$LXC_PORT" "$@"; }

BACKUP_PATH=""
rollback() {
    if [ -z "$BACKUP_PATH" ]; then
        warn "No backup path set, cannot rollback"
        return 1
    fi
    warn "Rolling back to $BACKUP_PATH..."
    if run_ssh "systemctl stop syncserver 2>/dev/null; cp $BACKUP_PATH /usr/bin/syncserver; systemctl start syncserver 2>/dev/null || true"; then
        warn "Rollback completed"
    else
        warn "Rollback failed - manual intervention required"
    fi
}

trap 'die "Interrupted by signal"' INT TERM

# === Argument parsing ===
while [ $# -gt 0 ]; do
    case "$1" in
        --dry-run) DRY_RUN=1; shift ;;
        --help|-h)
            sed -n '2,16p' "$0"
            exit 0
            ;;
        *) die "Unknown argument: $1. Use --help for usage." ;;
    esac
done

# === Step 1: Pre-flight ===
log "Pre-flight checks..."
cd "$PROJECT_DIR"
git diff --quiet || die "Git working tree is dirty. Commit or stash first."
git diff --cached --quiet || die "Git index has staged changes. Commit first."
[ "$(git rev-parse --abbrev-ref HEAD)" = "main" ] || die "Must be on main branch"
[ -f "$SCRIPT_DIR/bump-version.sh" ] || die "bump-version.sh not found"
[ -f "Makefile" ] || die "Makefile not found"
[ -d "web" ] || die "web/ directory not found"

for tool in ssh scp make go dpkg-deb; do
    command -v "$tool" >/dev/null || die "$tool not found"
done

if ! run_ssh 'true' 2>/dev/null; then
    [ "$DRY_RUN" = "1" ] && warn "Cannot reach $LXC_HOST, but --dry-run so continuing" || die "Cannot ssh to $LXC_HOST"
fi

FREE_KB=$(df -k "$PROJECT_DIR" | tail -1 | awk '{print $4}')
if [ "$FREE_KB" -lt 512000 ]; then
    warn "Less than 500MB free on local disk"
fi

success "Pre-flight OK"

# === Step 2: Bump version ===
log "Bumping version..."
CURRENT_VERSION=$(grep -oE 'var version = "[0-9]+\.[0-9]+\.[0-9]+"' cmd/server/main.go | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
[ -n "$CURRENT_VERSION" ] || die "Cannot parse current version"

if [ "$DRY_RUN" = "1" ]; then
    success "Would bump from $CURRENT_VERSION (dry run)"
    NEW_VERSION="$CURRENT_VERSION"
else
    "$SCRIPT_DIR/bump-version.sh" >/dev/null
    NEW_VERSION=$(grep -oE 'var version = "[0-9]+\.[0-9]+\.[0-9]+"' cmd/server/main.go | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
    [ "$NEW_VERSION" != "$CURRENT_VERSION" ] || die "Version bump failed"

    git add cmd/server/main.go Makefile
    git commit -m "Bump version to $NEW_VERSION" >/dev/null
    git push || die "git push failed"
    success "Bumped to $NEW_VERSION and pushed"
fi

VERSION="$NEW_VERSION"

# === Step 3: Build ===
if [ "$SKIP_BUILD" = "1" ]; then
    log "Skipping build (SKIP_BUILD=1)"
    DEB="dist/syncserver_${VERSION}_amd64.deb"
    [ -f "$DEB" ] || die "SKIP_BUILD=1 but $DEB does not exist"
else
    log "Building..."
    if [ "$DRY_RUN" = "1" ]; then
        log "[dry-run] Would run: make clean && make package"
        DEB="dist/syncserver_${VERSION}_amd64.deb"
        if [ -f "$DEB" ]; then
            log "[dry-run] Found existing $DEB"
        else
            log "[dry-run] $DEB does not exist yet (would be created by build)"
        fi
    else
        make clean >/dev/null
        make package
        DEB="dist/syncserver_${VERSION}_amd64.deb"
        [ -f "$DEB" ] || die "Build failed: $DEB not found"
        success "Built: $DEB ($(du -h "$DEB" | cut -f1))"
    fi
fi

# === Step 4: Backup on LXC ===
BACKUP_PATH="/var/backups/syncserver.previous.${VERSION}"
log "Backing up current binary on LXC to $BACKUP_PATH..."

if [ "$DRY_RUN" = "1" ]; then
    log "[dry-run] Would backup /usr/bin/syncserver"
else
    if ! run_ssh "mkdir -p /var/backups && cp /usr/bin/syncserver $BACKUP_PATH && ls -la $BACKUP_PATH" 2>/dev/null; then
        die "Backup failed on $LXC_HOST"
    fi
    success "Backed up to $BACKUP_PATH"
fi

# === Step 5: Deploy ===
log "Deploying to $LXC_HOST..."

if [ "$DRY_RUN" = "1" ]; then
    log "[dry-run] Would scp $DEB and run dpkg -i"
    echo ""
    echo "Dry run complete. No changes made."
    echo "Log file: $LOG_FILE"
    exit 0
fi

if ! run_scp "$DEB" "${LXC_HOST}:${REMOTE_DIR}/dist/" 2>/dev/null; then
    rollback
    die "SCP failed"
fi

if ! run_ssh "cd $REMOTE_DIR && dpkg -i dist/syncserver_${VERSION}_amd64.deb" 2>/dev/null; then
    rollback
    die "dpkg -i failed, rolled back"
fi

success "Deployed $DEB"

# === Step 6: Verify ===
if [ "$SKIP_VERIFY" = "1" ]; then
    log "Skipping verify (SKIP_VERIFY=1)"
else
    log "Verifying deployment (timeout: ${HEALTH_TIMEOUT}s)..."

    SERVICE_ACTIVE=""
    for i in $(seq 1 $HEALTH_TIMEOUT); do
        SERVICE_ACTIVE=$(run_ssh 'systemctl is-active syncserver' 2>/dev/null || echo "unknown")
        if [ "$SERVICE_ACTIVE" = "active" ]; then
            break
        fi
        sleep 1
    done

    if [ "$SERVICE_ACTIVE" != "active" ]; then
        rollback
        die "Service did not become active within ${HEALTH_TIMEOUT}s (current: $SERVICE_ACTIVE)"
    fi
    success "Service active"

    REMOTE_VERSION=$(run_ssh '/usr/bin/syncserver --version' 2>/dev/null | tr -d '[:space:]' || echo "unknown")
    if [ "$REMOTE_VERSION" != "$VERSION" ]; then
        rollback
        die "Version mismatch: deployed $VERSION, remote reports $REMOTE_VERSION"
    fi
    success "Version: $REMOTE_VERSION"

    CACHE_HEADER=$(run_ssh 'curl -sI http://localhost:8080/' 2>/dev/null | grep -i "cache-control:" | head -1 | tr -d '\r')
    if ! echo "$CACHE_HEADER" | grep -qi "no-cache"; then
        warn "Cache-Control header missing or wrong: $CACHE_HEADER"
    else
        success "Cache headers OK"
    fi

    HTTP_CODE=$(run_ssh 'curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/' 2>/dev/null || echo "000")
    if [ "$HTTP_CODE" != "200" ]; then
        rollback
        die "Health check failed: HTTP $HTTP_CODE"
    fi
    success "HTTP /  → 200"
fi

# === Summary ===
echo ""
echo "════════════════════════════════════════════"
echo "  Deploy Summary"
echo "════════════════════════════════════════════"
echo "  Version:   $VERSION"
echo "  Host:      $LXC_HOST"
echo "  Binary:    /usr/bin/syncserver"
echo "  Backup:    $BACKUP_PATH"
echo "  Status:    active"
echo "  HTTP:      200"
echo "  Headers:   $CACHE_HEADER"
echo "  Time:      $(date)"
echo "  Log file:  $LOG_FILE"
echo "════════════════════════════════════════════"
echo ""
echo "Next: hard reload browser once (Ctrl+Shift+R) to clear stale cache"
