/**
 * Resilience & Boundary Stress Test Suite (v2.77.3)
 * 
 * 이 테스트는 ConnectAI 엔진이 극한의 환경(인증 실패, 네트워크 차단, 타임아웃 등)에서
 * 얼마나 안정적으로 복구되고, 신뢰성 지표(Resilience Metrics)를 정확히 기록하는지 검증합니다.
 */

import {
    AgentEngine,
    IAgent,
    AgentExecuteOptions,
    MissionState,
    CacheManager
} from '../src/lib/engine';
import * as fs from 'fs';
import * as path from 'path';
import * as vscode from 'vscode';

// --- Helpers ---
const getBaseDir = () => {
    if (process.env.ASTRA_TEST_ROOT) return process.env.ASTRA_TEST_ROOT;
    try {
        const folders = require('vscode').workspace.workspaceFolders;
        if (folders && folders.length > 0) return folders[0].uri.fsPath;
    } catch (e) {}
    return process.cwd();
};

const clearCache = () => {
    const baseDir = getBaseDir();
    const cacheDir = path.join(baseDir, '.astra', 'cache');
    if (fs.existsSync(cacheDir)) {
        fs.rmSync(cacheDir, { recursive: true, force: true });
    }
    const missionDir = path.join(baseDir, '.astra', 'missions');
    if (fs.existsSync(missionDir)) {
        fs.rmSync(missionDir, { recursive: true, force: true });
    }
};

beforeAll(() => {
    process.env.ASTRA_TEST_ROOT = path.join(process.cwd(), '.astra', 'tests', 'stress');
    if (!fs.existsSync(process.env.ASTRA_TEST_ROOT)) {
        fs.mkdirSync(process.env.ASTRA_TEST_ROOT, { recursive: true });
    }
    clearCache();
});

const noopProgress = () => {};

// --- Mock Agents for Stress Testing ---

/**
 * 시나리오 A: 100% 인증 실패만 발생하는 에이전트
 */
class AuthFailureAgent implements IAgent {
    public callCount = 0;
    async execute(): Promise<string> {
        this.callCount++;
        throw new Error('AUTH_FAILURE: Invalid API Key or Session Expired');
    }
}

/**
 * 시나리오 B: 100% 네트워크 차단 발생 (Fallback 유도)
 */
class NetworkBlackoutAgent implements IAgent {
    public callCount = 0;
    async execute(): Promise<string> {
        this.callCount++;
        throw new Error('ECONNREFUSED: Connection refused by peer');
    }
}

/**
 * 시나리오 C: 극심한 지연 발생 (Timeout 유도)
 */
class ExtremeLatencyAgent implements IAgent {
    constructor(private delayMs: number = 5000) {}
    async execute(): Promise<string> {
        await new Promise(r => setTimeout(r, this.delayMs));
        return 'Delayed response';
    }
}

/**
 * 시나리오 D: 데이터 충돌 유발 (High Conflict Score 유도)
 */
class ConflictAgent implements IAgent {
    async execute(): Promise<string> {
        return "ConnectAI는 전적으로 비동기 엔진입니다. [CONFLICT] 그러나 어떤 문서는 동기적으로 작동한다고 주장합니다.";
    }
}

describe('Resilience & Boundary Stress Tests', () => {
    beforeEach(() => {
        clearCache();
    });

    test('[Scenario A] 무한 인증 실패 시 안전하게 중단되고 에러를 보고해야 한다', async () => {
        const engine = new AgentEngine(
            new AuthFailureAgent(),
            new AuthFailureAgent(),
            new AuthFailureAgent()
        );

        const missionId = `stress_auth_${Date.now()}`;
        try {
            await engine.runMission(missionId, 'Auth Test', 'ctx', new AbortController().signal, noopProgress);
            fail('Should have thrown an error');
        } catch (error: any) {
            expect(error.message).toContain('AUTH_FAILURE');
            // 재시도가 소진될 때까지 호출되었는지 확인 (Permanent 에러는 즉시 중단)
            // ErrorClassifier에 따라 AUTH_FAILURE는 Permanent로 분류됨
        }
    }, 10000);

    test('[Scenario B] 섹션 단계 네트워크 블랙아웃 시 Fallback(이전데이터)으로 자동 복구되어야 한다', async () => {
        // chunked flow 에서는 outline / section / polish 모두 동일 writer 가 처리.
        // section 단계만 transient 실패하도록 role-aware mock 으로 분기하고,
        // caller 가 previousValidData 를 주입해 fallback 경로가 활성화되는지 검증.
        const fallbackData = 'Emergency Fallback Data from Previous Step. Long enough to satisfy validators.';
        const roleAwareMock: IAgent = {
            execute: async (_input, _ctx, _signal, options) => {
                const role = (options?.config?.role as string | undefined) ?? 'section';
                if (role === 'outline') {
                    return '[{"heading":"본문","scope":"전체 답변"}]';
                }
                if (role === 'section') {
                    throw new Error('ECONNREFUSED: Connection refused by peer');
                }
                // polish
                return 'Final Report — polished output that includes recovered data summary.';
            },
        };

        const engine = new AgentEngine(roleAwareMock);
        const missionId = `stress_fallback_${Date.now()}`;
        // fast-path 휴리스틱을 우회해 outline → section → polish 가 모두 돌도록 분석 키워드 포함 prompt 사용.
        const chunkedPrompt = '다음 보고서 본문을 종합적으로 분석해서 핵심 사안을 정리하고 향후 개선 방향을 상세히 제안해 주세요. 리뷰는 가능한 한 꼼꼼하게 작성되어야 합니다.';
        const result = await engine.runMission(
            missionId,
            chunkedPrompt,
            'Resilience Context',
            new AbortController().signal,
            noopProgress,
            { priorResults: { previousValidData: fallbackData }, config: { allowFallback: true } }
        );

        // polish 가 정상 실행돼야 하고, fallback 카운트가 최소 1 (section 실패 → fallback).
        expect(result).toContain('Final Report');

        const missionPath = path.join(getBaseDir(), '.astra', 'missions', `${missionId}.json`);
        const state = JSON.parse(fs.readFileSync(missionPath, 'utf-8'));
        expect(state.resilienceMetrics.fallbacks).toBeGreaterThanOrEqual(1);

        console.log(`   ✅ Fallback Recovery (chunked section step) Verified: ${state.resilienceMetrics.fallbacks} instances`);
    }, 15000);

    test('[Scenario D] 섹션 응답에 충돌 신호 있을 때 Conflict Score 가 🚨 High 로 기록되어야 한다', async () => {
        // 섹션 단계 응답에 [CONFLICT WARNING] + 수치 모순 + 상충 용어를 같이 넣어
        // AgentDataValidator.validateHandoff 의 점수가 50 을 넘기는지 확인.
        const roleAwareMock: IAgent = {
            execute: async (_input, _ctx, _signal, options) => {
                const role = (options?.config?.role as string | undefined) ?? 'section';
                if (role === 'outline') {
                    return '[{"heading":"본문","scope":"전체 답변"}]';
                }
                if (role === 'section') {
                    return "[CONFLICT WARNING] 성능이 200% 증가했습니다. vs 그러나 동시에 50% 감소했습니다. 최적화와 성능 저하가 동시에 발견됨.";
                }
                return 'Final report with inconsistencies. This should be long enough to pass validation.';
            },
        };

        const engine = new AgentEngine(roleAwareMock);
        const missionId = `stress_conflict_${Date.now()}`;
        const chunkedPrompt = '다음 사항을 종합 분석해서 상충 지점과 충돌 위험을 꼼꼼히 보고하고, 정합성 검증 결과를 상세히 정리해 주세요. 리뷰는 가능한 한 자세하게 작성되어야 합니다.';
        await engine.runMission(missionId, chunkedPrompt, 'ctx', new AbortController().signal, noopProgress);

        const missionPath = path.join(getBaseDir(), '.astra', 'missions', `${missionId}.json`);
        const state = JSON.parse(fs.readFileSync(missionPath, 'utf-8'));

        // 수치 모순(25) + 상충 용어(15) + 경고 태그(30) = 70 점 예상
        expect(state.resilienceMetrics.maxConflictScore).toBeGreaterThan(50);

        console.log(`   🚨 High Conflict Detected: Risk Score ${state.resilienceMetrics.maxConflictScore}`);
    }, 15000);
});

// Mock Helpers
class MockSuccessAgent implements IAgent {
    constructor(private response: string) {}
    async execute(): Promise<string> {
        return this.response;
    }
}
