/**
 * Self-Reflector Phase B — *분리된 콘텍스트*에서 LLM 한 번 더 호출해 응답을
 * 외부 시각으로 검증.
 *
 * Phase A의 self-check는 같은 모델·같은 콘텍스트에서 자기 자신을 보는 한계가
 * 있다. 모델이 자기가 만든 답변을 자신 있게 잘못 평가하는 *과신 편향*은
 * LLM의 잘 알려진 약점이다. Phase B는 이걸 보완하기 위해:
 *
 *   1. specialist 응답이 끝나면
 *   2. *새로운* system prompt로 LLM에게 (task + 응답)을 보여주고
 *   3. "이 응답이 task를 충실히 처리했나? 명백한 오류가 있나?"를 묻는다
 *   4. {verdict: pass|warn|fail, issues: [...]} JSON으로 받음
 *   5. fail이면 issue들을 prepend해 같은 specialist 1회 retry
 *
 * 호출자(dispatcher)는 verdict + issues + final response를 받아 그대로 chat에
 * 표시한다. 검증 LLM 자체가 실패해도 *원본 응답은 보존* — 검증 layer가
 * 진행 자체를 막지 않는다.
 */
import { IAIService } from '../../core/services';
import { logError, logInfo } from '../../utils';

export interface VerifyInput {
    /** specialist에게 줬던 task 문자열 (revisionNote 등 prefix 포함). */
    task: string;
    /** specialist의 raw 응답 (action-tag 실행 *전*). */
    response: string;
    /** specialist가 누구였는지 (검증 프롬프트 컨텍스트). */
    agentName: string;
    /** 검증에 사용할 모델. 비싸지 않아도 OK — 검증은 짧고 가볍게. */
    model?: string;
    /** Requirement Contract — 있으면 검증 기준으로 직접 활용. */
    contractBlock?: string;
}

export type VerifyVerdict = 'pass' | 'warn' | 'fail';

export interface VerifyResult {
    verdict: VerifyVerdict;
    /** 발견된 이슈 목록 (verdict='warn'·'fail' 시 1~3개). */
    issues: string[];
    /** 한 줄 요약 — chat label용. */
    summary: string;
    /** 검증 LLM이 실패한 경우 true; 호출자는 원본 응답 보존하고 진행. */
    verifierError?: boolean;
}

const SYSTEM_PROMPT = `당신은 *외부 감리* 입니다. 다른 에이전트가 방금 사용자 task에 대해 만든 응답을 객관적으로 점검합니다. 응답을 만든 본인이 아니므로 *과신 없이* 보세요.

점검 기준:
  1. task 요구를 *직접* 처리했는가 (회피·동문서답 X)
  2. 명백한 사실 오류·논리 모순이 없는가
  3. 코드/파일이 포함됐다면 정의되지 않은 변수·잘못된 경로·존재하지 않는 import가 없는가
  4. Requirement Contract가 있으면 criteria를 위반하지 않는가

🛑 **빈 깡통(Hollow Code) 자동 fail**:
  코드 파일이 포함됐는데 *실제 로직이 비어 있으면* 무조건 "fail":
    - 함수 본문이 \`pass\` / \`return None\` / \`return null\` / \`...\` 한 줄뿐
    - 함수 본문이 TODO/FIXME 주석뿐
    - 클래스/모듈에 import만 있고 로직 0줄
    - "여기에 X를 구현하세요" 같은 placeholder만 들어 있음
  이런 패턴은 *문법은 통과해도 사용자가 원한 결과물이 아닙니다*. issues에
  "빈 깡통: <함수명> 본문이 stub뿐" 처럼 *구체적인 위치*를 적으세요.

평가 라벨:
  - "pass" : 위 모든 기준 통과
  - "warn" : 일부 약점이 있지만 사용자가 받아 볼 만함 (issues에 적기)
  - "fail" : 핵심 오류·누락 또는 *빈 깡통* 발견 — 재작업 필요 (issues에 적기)

⚠️ 반드시 아래 JSON 한 번만. 다른 텍스트(설명·코드펜스) 일체 금지.

{"verdict":"pass"|"warn"|"fail","issues":["<이슈1>","<이슈2>"],"summary":"한 줄(30자 이내)"}`;

function _buildUserMessage(input: VerifyInput): string {
    const lines: string[] = [];
    if (input.contractBlock) {
        lines.push(input.contractBlock);
        lines.push('');
    }
    lines.push(`[검증 대상 에이전트] ${input.agentName}`);
    lines.push('');
    lines.push('[task]');
    lines.push(input.task.slice(0, 2000));
    lines.push('');
    lines.push('[해당 에이전트의 응답]');
    lines.push((input.response || '').slice(0, 4000));
    lines.push('');
    lines.push('점검 JSON만 출력:');
    return lines.join('\n');
}

function _parseVerdictJson(raw: string): { verdict: VerifyVerdict; issues: string[]; summary: string } | null {
    if (!raw || !raw.trim()) return null;
    const fenced = raw.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
    const stage1 = (fenced ? fenced[1] : raw).trim();
    const tryParse = (s: string) => {
        try {
            const obj = JSON.parse(s) as Record<string, unknown>;
            const v = typeof obj.verdict === 'string' ? obj.verdict.toLowerCase().trim() : '';
            if (v !== 'pass' && v !== 'warn' && v !== 'fail') return null;
            const issues = Array.isArray(obj.issues)
                ? obj.issues.filter((x): x is string => typeof x === 'string' && x.trim().length > 0)
                    .map((x) => x.trim()).slice(0, 5)
                : [];
            const summary = typeof obj.summary === 'string' ? obj.summary.trim() : '';
            return { verdict: v as VerifyVerdict, issues, summary };
        } catch {
            return null;
        }
    };
    const direct = tryParse(stage1);
    if (direct) return direct;
    const m = stage1.match(/\{[\s\S]*\}/);
    if (m) {
        const balanced = tryParse(m[0]);
        if (balanced) return balanced;
    }
    // 최후의 fallback: 정규식으로 verdict만이라도.
    const verdictMatch = stage1.match(/"verdict"\s*:\s*"(pass|warn|fail)"/i);
    if (verdictMatch) {
        return {
            verdict: verdictMatch[1].toLowerCase() as VerifyVerdict,
            issues: [],
            summary: '',
        };
    }
    return null;
}

/**
 * 검증 한 회차. 호출 실패 / JSON 파싱 실패는 *통과로 간주* (verifierError=true
 * 표시). 검증 layer가 작업 진행을 막지 않게 — 본래 의도가 안전망이지 통제관문이
 * 아니므로.
 */
export async function verifyResponse(
    ai: IAIService,
    input: VerifyInput,
): Promise<VerifyResult> {
    let raw = '';
    try {
        const result = await ai.chat({
            system: SYSTEM_PROMPT,
            user: _buildUserMessage(input),
            model: input.model,
        });
        raw = result.content || '';
    } catch (e: any) {
        logError('selfReflectorVerifier: call failed; treating as pass.', { error: e?.message ?? String(e) });
        return { verdict: 'pass', issues: [], summary: '검증 실패 — 원본 유지', verifierError: true };
    }
    const parsed = _parseVerdictJson(raw);
    if (!parsed) {
        logInfo('selfReflectorVerifier: parse failed; treating as pass.', { rawHead: raw.slice(0, 100) });
        return { verdict: 'pass', issues: [], summary: '검증 응답 파싱 실패 — 원본 유지', verifierError: true };
    }
    return {
        verdict: parsed.verdict,
        issues: parsed.issues,
        summary: parsed.summary || `verdict: ${parsed.verdict}`,
    };
}

/**
 * 검증 결과를 retry 시 사용할 prompt prefix로 직렬화. 호출자가 task 앞에
 * prepend 해 specialist를 1회 더 호출한다.
 */
export function formatIssuesForRetry(issues: string[]): string {
    if (!issues.length) return '';
    const lines: string[] = [];
    lines.push('[외부 감리 지적 — 반드시 반영]');
    for (const i of issues) lines.push(`- ${i}`);
    return lines.join('\n');
}
