#!/usr/bin/env python3
"""
데이터 영속성 E2E 테스트
시나리오: 유저가 게임을 시작 → 전투 2회 → 서버 중단 → 서버 재시작 → 데이터 확인 → 전투 2회 더 → 완결
"""
import json, time, sys, os, signal, subprocess, urllib.request

BASE = 'http://localhost:8600'
SERVER_DIR = '/home/jooyoung/.openclaw/workspace-260312/orchestration/server'
DB_PATH = '/home/jooyoung/.openclaw/workspace-260312/orchestration/data/battle.db'

PASS, FAIL = 0, 0
def ok(msg): global PASS; PASS+=1; print(f'  ✅ {msg}')
def ng(msg): global FAIL; FAIL+=1; print(f'  ❌ {msg}')
def check(cond, msg): ok(msg) if cond else ng(msg)
def section(title): print(f'\n━━━ {title} ━━━')

def api(method, path, data=None, expect_fail=False):
    body = json.dumps(data).encode() if data else None
    req = urllib.request.Request(f'{BASE}{path}', data=body, method=method)
    if body: req.add_header('Content-Type', 'application/json')
    try:
        with urllib.request.urlopen(req, timeout=5) as r:
            return json.loads(r.read())
    except Exception as e:
        if expect_fail:
            return None
        print(f'    API ERROR: {method} {path} → {e}')
        return None

def server_running():
    try:
        r = api('GET', '/api/health')
        return r and r.get('status') == 'ok'
    except:
        return False

def kill_server():
    """서버 프로세스 종료"""
    os.system("pkill -f 'python3 simple_server.py' 2>/dev/null")
    os.system("pkill -f 'uvicorn.*8600' 2>/dev/null")
    time.sleep(2)

def start_server():
    """서버 시작 (setsid로 격리)"""
    subprocess.Popen(
        ['python3', 'simple_server.py'],
        cwd=SERVER_DIR,
        stdout=open('/tmp/persist-test-server.log', 'w'),
        stderr=subprocess.STDOUT,
        start_new_session=True
    )
    # 서버 준비 대기
    for i in range(10):
        time.sleep(1)
        if server_running():
            return True
    return False

# ============================================================
# 테스트 데이터
# ============================================================
USER_ID = f'persist-test-{int(time.time())}'
GAME_ID = 'game-persist-test'
SESSION_ID = None

BATTLES = [
    {'id': 'persist-b1', 'adapter': 'roguelike',    'enemyLevel': 2, 'result': 'win',  'hp': 80, 'turns': 5,  'dmg': 100},
    {'id': 'persist-b2', 'adapter': 'card-battle',  'enemyLevel': 3, 'result': 'win',  'hp': 60, 'turns': 8,  'dmg': 150},
    {'id': 'persist-b3', 'adapter': 'tactics-grid', 'enemyLevel': 4, 'result': 'lose', 'hp': 0,  'turns': 12, 'dmg': 200},
    {'id': 'persist-b4', 'adapter': 'rhythm-battle','enemyLevel': 5, 'result': 'win',  'hp': 45, 'turns': 10, 'dmg': 250},
]

# ============================================================
section('Phase 1: 서버 확인')
# ============================================================

check(server_running(), '서버 실행 중')

# ============================================================
section('Phase 2: 유저 등록 + 세션 시작')
# ============================================================

r = api('POST', '/api/user/register', {
    'userId': USER_ID,
    'deviceHash': 'persist-test-device',
    'displayName': 'PersistTester'
})
check(r and USER_ID in str(r), f'유저 등록 ({USER_ID})')

r = api('POST', '/api/session', {
    'userId': USER_ID,
    'gameId': GAME_ID,
    'clientBuild': 'persist-v1'
})
SESSION_ID = r.get('sessionId', '') if r else ''
check(len(SESSION_ID) > 10, f'세션 생성 ({SESSION_ID[:8]}...)')

# ============================================================
section('Phase 3: 전투 2회 (서버 중단 전)')
# ============================================================

for b in BATTLES[:2]:
    r = api('POST', f'/api/battle/{b["id"]}', {
        'result': b['result'],
        'userId': USER_ID,
        'gameId': GAME_ID,
        'sessionId': SESSION_ID,
        'adapter': b['adapter'],
        'battleSlotId': f'SLOT_{b["id"][-1]}',
        'scenarioNodeId': f'NODE_{b["id"][-1]}',
        'remainingHP': b['hp'],
        'turnsUsed': b['turns'],
        'totalDamage': b['dmg'],
        'enemyLevel': b['enemyLevel'],
        'playerStats': {'HP': 100, 'ATK': 25, 'DEF': 12, 'SPD': 8},
        'enemyStats': {'HP': 80, 'ATK': 20, 'DEF': 8, 'SPD': 6},
        'inputCount': b['turns'] * 3,
        'isAutoplay': False,
        'clientBuild': 'persist-v1',
        'durationMs': b['turns'] * 3000,
        'timestamp': int(time.time() * 1000)
    })
    exp = r.get('expGained', 0) if r else 0
    check(r and r.get('status') == 'ok', f'전투 {b["id"]} ({b["adapter"]}, {b["result"]}) → EXP {exp}')

# 세션 업데이트
api('POST', f'/api/session/{SESSION_ID}/update', {
    'battlesPlayed': 2, 'battlesWon': 2, 'lastNodeId': 'NODE_2'
})

# 이벤트 기록
api('POST', '/api/event', {
    'userId': USER_ID, 'sessionId': SESSION_ID, 'gameId': GAME_ID,
    'eventType': 'chapter_enter', 'eventData': {'chapter': 2}
})

# 중간 상태 스냅샷
r = api('GET', f'/api/user/{USER_ID}/stats')
pre_level = r.get('level', 0) if r else 0
pre_exp = r.get('exp', 0) if r else 0
pre_battles = r.get('totalBattles', 0) if r else 0
print(f'  📊 중간 상태: level={pre_level}, exp={pre_exp}, battles={pre_battles}')

# ============================================================
section('Phase 4: 서버 강제 종료')
# ============================================================

kill_server()
time.sleep(1)
check(not server_running(), '서버 종료 확인')

# DB 파일 존재 확인
check(os.path.exists(DB_PATH), f'DB 파일 존재 ({os.path.getsize(DB_PATH)} bytes)')

# ============================================================
section('Phase 5: 서버 재시작')
# ============================================================

started = start_server()
check(started, '서버 재시작 성공')

if not started:
    print('  🔴 서버 재시작 실패 — 테스트 중단')
    print(f'\n  결과: ✅ {PASS} / ❌ {FAIL}')
    sys.exit(1)

# ============================================================
section('Phase 6: 영속성 검증 (서버 재시작 후)')
# ============================================================

# 유저 데이터 생존 확인
r = api('GET', f'/api/user/{USER_ID}/stats')
check(r and r.get('level') == pre_level, f'유저 레벨 보존 ({r.get("level") if r else "NULL"} == {pre_level})')
check(r and r.get('exp') == pre_exp, f'유저 EXP 보존 ({r.get("exp") if r else "NULL"} == {pre_exp})')
check(r and r.get('totalBattles') == pre_battles, f'전투 수 보존 ({r.get("totalBattles") if r else "NULL"} == {pre_battles})')

# 전투 기록 생존 확인
for b in BATTLES[:2]:
    r = api('GET', f'/api/battle/{b["id"]}')
    data = r.get('data', r) if r else {}
    check(data and b['adapter'] in str(data), f'전투 {b["id"]} 기록 생존')

# 세션 데이터 생존 확인
r = api('GET', f'/api/user/{USER_ID}/history')
check(r and len(r.get('battles', [])) >= 2, f'전투 이력 생존 ({len(r.get("battles",[])) if r else 0}건)')

# 버전 API 확인 (서버 재시작 후에도 동작)
r = api('GET', '/api/version')
check(r and 'build' in r, '버전 API 동작')

# ============================================================
section('Phase 7: 전투 2회 더 (재시작 후 이어서)')
# ============================================================

for b in BATTLES[2:]:
    r = api('POST', f'/api/battle/{b["id"]}', {
        'result': b['result'],
        'userId': USER_ID,
        'gameId': GAME_ID,
        'sessionId': SESSION_ID,
        'adapter': b['adapter'],
        'battleSlotId': f'SLOT_{b["id"][-1]}',
        'scenarioNodeId': f'NODE_{b["id"][-1]}',
        'remainingHP': b['hp'],
        'turnsUsed': b['turns'],
        'totalDamage': b['dmg'],
        'enemyLevel': b['enemyLevel'],
        'playerStats': {'HP': 100, 'ATK': 30, 'DEF': 15, 'SPD': 10},
        'enemyStats': {'HP': 90, 'ATK': 28, 'DEF': 12, 'SPD': 9},
        'inputCount': b['turns'] * 3,
        'durationMs': b['turns'] * 3000,
        'timestamp': int(time.time() * 1000)
    })
    exp = r.get('expGained', 0) if r else 0
    check(r and r.get('status') == 'ok', f'전투 {b["id"]} ({b["adapter"]}, {b["result"]}) → EXP {exp}')

# ============================================================
section('Phase 8: 세션 완결')
# ============================================================

# 세션 업데이트
api('POST', f'/api/session/{SESSION_ID}/update', {
    'battlesPlayed': 4, 'battlesWon': 3, 'lastNodeId': 'NODE_FINAL', 'chaptersCompleted': 4
})

# 엔딩 이벤트
api('POST', '/api/event', {
    'userId': USER_ID, 'sessionId': SESSION_ID, 'gameId': GAME_ID,
    'eventType': 'game_complete', 'eventData': {'endingId': 'ENDING_TRUE', 'totalTimeMs': 180000}
})

# 세션 종료
r = api('POST', f'/api/session/{SESSION_ID}/end', {
    'endReason': 'completed',
    'endingId': 'ENDING_TRUE'
})
check(r is not None, '세션 종료 (completed)')

# ============================================================
section('Phase 9: 최종 상태 검증')
# ============================================================

# 유저 최종 통계
r = api('GET', f'/api/user/{USER_ID}/stats')
if r:
    print(f'  📊 최종: level={r.get("level")}, exp={r.get("exp")}, battles={r.get("totalBattles")}, wins={r.get("wins")}, winRate={r.get("winRate","?")}%')
    check(r.get('totalBattles') == 4, f'총 전투 4회 ({r.get("totalBattles")})')
    check(r.get('wins') == 3, f'승리 3회 ({r.get("wins")})')
    # EXP: win×3 (2×15 + 3×15 + 5×15 = 150) + lose×1 (4×5 = 20) = 170 → level 2, exp 70
    check(r.get('level') >= 2, f'레벨 ≥ 2 ({r.get("level")})')

# 전투 이력 전체
r = api('GET', f'/api/user/{USER_ID}/history')
if r:
    battles = r.get('battles', [])
    check(len(battles) == 4, f'전투 이력 4건 ({len(battles)})')

# DB 직접 검증
import sqlite3
db = sqlite3.connect(DB_PATH)

sess = db.execute('SELECT end_reason, ending_id, battles_played, battles_won, chapters_completed FROM game_sessions WHERE session_id=?', (SESSION_ID,)).fetchone()
if sess:
    check(sess[0] == 'completed', f'DB: end_reason=completed ({sess[0]})')
    check(sess[1] == 'ENDING_TRUE', f'DB: ending_id=ENDING_TRUE ({sess[1]})')
    check(sess[2] == 4, f'DB: battles_played=4 ({sess[2]})')
    check(sess[3] == 3, f'DB: battles_won=3 ({sess[3]})')
    check(sess[4] == 4, f'DB: chapters_completed=4 ({sess[4]})')
else:
    ng('DB: 세션 데이터 없음')

# 이벤트 수
evt_count = db.execute('SELECT COUNT(*) FROM events WHERE session_id=?', (SESSION_ID,)).fetchone()[0]
check(evt_count >= 2, f'DB: events ≥ 2 ({evt_count})')

# 어댑터별 전투 확인
adapters = [r[0] for r in db.execute('SELECT DISTINCT adapter FROM battles WHERE session_id=?', (SESSION_ID,)).fetchall()]
check(len(adapters) == 4, f'DB: 4종 어댑터 ({adapters})')

db.close()

# ============================================================
section('결과 요약')
# ============================================================

print(f'''
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  ✅ PASS: {PASS}
  ❌ FAIL: {FAIL}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  {"🟢 영속성 검증 통과" if FAIL == 0 else "🔴 실패 항목 있음"}

  시나리오:
    유저 등록 → 세션 시작 → 전투 2회
    → 서버 강제 종료 → 서버 재시작
    → 데이터 생존 확인 → 전투 2회 추가
    → 세션 완결 (ENDING_TRUE)
    → 최종 상태 검증
''')
