#!/bin/bash
# done-watcher.sh - .done 감지 + 직접 알림 + stale 에스컬레이션
# Layer 2 Fallback: notify-completion.py(Layer 1) 실패 시 30초 이내 알림 보장
set -euo pipefail

EVENTS_DIR="/home/jay/workspace/memory/events"
LOG="/home/jay/workspace/logs/done-protocol.log"
LOCK_FILE="/tmp/done-watcher.lock"

# flock으로 동시 실행 방지 (TOCTOU 방어)
exec 9>"$LOCK_FILE"
flock -n 9 || { echo "already running"; exit 0; }

log() {
    echo "[$(date -Iseconds)] [done-watcher] $1" >> "$LOG"
}

# === NEW: 미알림 .done 감지 → 직접 Telegram 알림 ===
batch_tasks=()
for done_file in "$EVENTS_DIR"/*.done; do
    [ -f "$done_file" ] || continue
    [ -L "$done_file" ] && continue  # symlink 거부 (보안)

    task_id=$(basename "$done_file" .done)
    notified_file="$EVENTS_DIR/${task_id}.done.notified"

    # 이미 알림 완료된 건은 스킵
    [ -f "$notified_file" ] && continue

    # 원자적 소유권 획득 (TOCTOU 방어)
    proc_file="$EVENTS_DIR/${task_id}.done.processing"
    mv "$done_file" "$proc_file" 2>/dev/null || continue

    batch_tasks+=("$task_id:$proc_file")
done

if [ ${#batch_tasks[@]} -gt 0 ]; then
    # 배치 알림 메시지 구성
    if [ ${#batch_tasks[@]} -eq 1 ]; then
        task_id="${batch_tasks[0]%%:*}"
        msg="✅ ${task_id} 완료. 보고서: /home/jay/workspace/memory/reports/${task_id}.md"
    else
        msg="✅ 일괄 완료 (${#batch_tasks[@]}건):"
        for entry in "${batch_tasks[@]}"; do
            task_id="${entry%%:*}"
            msg+=$'\n'"  - ${task_id}"
        done
    fi

    # 직접 Telegram API 호출 (task_id를 환경변수로 전달, Python 인라인 삽입 금지)
    export DONE_WATCHER_MSG="$msg"
    if python3 -c "
import requests, os, sys
token = os.environ.get('ANU_BOT_TOKEN', '')
chat_id = os.environ.get('COKACDIR_CHAT_ID', '6937032012')
msg = os.environ.get('DONE_WATCHER_MSG', '')
if not token:
    sys.exit(1)
r = requests.post(f'https://api.telegram.org/bot{token}/sendMessage',
                   json={'chat_id': chat_id, 'text': msg},
                   timeout=10)
sys.exit(0 if r.status_code == 200 else 1)
" 2>/dev/null; then
        # 성공: .done.processing → .done 원상 복구 + .done.notified 마커 생성
        for entry in "${batch_tasks[@]}"; do
            task_id="${entry%%:*}"
            proc_file="${entry#*:}"
            original_done="$EVENTS_DIR/${task_id}.done"
            mv "$proc_file" "$original_done" 2>/dev/null || true
            # .done.notified 마커 생성 (O_EXCL, 환경변수 방식)
            export NOTIFIED_PATH="$EVENTS_DIR/${task_id}.done.notified"
            python3 -c "
import os
path = os.environ.get('NOTIFIED_PATH', '')
if path:
    try:
        fd = os.open(path, os.O_CREAT|os.O_EXCL|os.O_WRONLY)
        os.close(fd)
    except FileExistsError:
        pass
" 2>/dev/null || true
        done
        log "Fallback 알림 전송 성공: ${#batch_tasks[@]}건"
    else
        # 실패: .done.processing → .done 원상 복구 (다음 사이클 재시도)
        for entry in "${batch_tasks[@]}"; do
            proc_file="${entry#*:}"
            task_id="${entry%%:*}"
            mv "$proc_file" "$EVENTS_DIR/${task_id}.done" 2>/dev/null || true
        done
        log "Fallback 알림 전송 실패, 원상 복구"
    fi
fi

# === 기존: stale .done 에스컬레이션 (30분 이상) ===
for done_file in $(find "$EVENTS_DIR" -maxdepth 1 -name "*.done" ! -name "*.done.*" -type f 2>/dev/null); do
    task_id=$(basename "$done_file" .done)
    file_age=$(( $(date +%s) - $(stat -c %Y "$done_file") ))

    if [ "$file_age" -ge 1800 ]; then
        escalated_file="$EVENTS_DIR/${task_id}.done.escalated"
        [ -f "$escalated_file" ] && continue
        # O_EXCL 원자적 생성 (환경변수 방식)
        export ESCALATED_PATH="$escalated_file"
        python3 -c "
import os, sys
path = os.environ.get('ESCALATED_PATH', '')
if not path:
    sys.exit(1)
try:
    fd = os.open(path, os.O_CREAT|os.O_EXCL|os.O_WRONLY)
    os.close(fd)
except FileExistsError:
    sys.exit(1)
" 2>/dev/null || continue
        log "${task_id}: stale .done (${file_age}s) → 에스컬레이션"
        # 에스컬레이션은 cokacdir 의도적 유지 (맥락노트 5.1b 참조)
        source /home/jay/workspace/.env.keys 2>/dev/null || true
        cokacdir --cron "⚠️ ${task_id} 완료 후 ${file_age}초 경과, 아직 미처리." \
            --at "1m" --chat "$COKACDIR_CHAT_ID" --key "$COKACDIR_KEY_ANU" --once 2>/dev/null || true
    fi
done

# === 기존: 오래된 .done.acked 정리 (24시간 이상) ===
for acked_file in $(find "$EVENTS_DIR" -maxdepth 1 -name "*.done.acked" -type f 2>/dev/null); do
    file_age=$(( $(date +%s) - $(stat -c %Y "$acked_file") ))
    if [ "$file_age" -ge 86400 ]; then
        task_id=$(basename "$acked_file" .done.acked)
        ARCHIVE_DIR="$EVENTS_DIR/archive"
        mkdir -p "$ARCHIVE_DIR"
        mv "$acked_file" "$ARCHIVE_DIR/" 2>/dev/null && log "${task_id}: .done.acked → archive (24h 경과)"
        [ -f "$EVENTS_DIR/${task_id}.done.escalated" ] && mv "$EVENTS_DIR/${task_id}.done.escalated" "$ARCHIVE_DIR/" 2>/dev/null
        [ -f "$EVENTS_DIR/${task_id}.done.notified" ] && mv "$EVENTS_DIR/${task_id}.done.notified" "$ARCHIVE_DIR/" 2>/dev/null
    fi
done

# heartbeat
echo "$(date -Iseconds)" > /home/jay/workspace/logs/done-watcher.heartbeat
