import * as vscode from 'vscode';
import { getConfig } from '../config';
import { AgentExecuteOptions } from '../lib/engine';

export abstract class BaseAgent {
    constructor(protected readonly modelName: string) {}

    protected async callLLM(persona: string, prompt: string, signal?: AbortSignal): Promise<string> {
        const { ollamaUrl } = getConfig();
        if (!ollamaUrl) {
            throw new Error('Ollama URL이 설정되지 않았습니다. 설정을 확인해주세요.');
        }

        if (typeof fetch === 'undefined') {
            throw new Error('이 환경에서는 fetch 함수를 사용할 수 없습니다. Node.js 버전을 확인하거나 polyfill이 필요합니다.');
        }

        const messages = [
            { role: 'system', content: persona },
            { role: 'user', content: prompt }
        ];

        // 엔진 자동 감지 (Ollama vs OpenAI/LM Studio)
        const isOllama = ollamaUrl.includes(':11434') || ollamaUrl.includes('ollama');
        const endpoint = isOllama ? `${ollamaUrl}/api/chat` : `${ollamaUrl}/v1/chat/completions`;

        // 컨텍스트 초과 방지를 위해 출력 토큰 상한을 항상 명시한다 (서브에이전트 중간 산출물용).
        const { contextLength, maxOutputTokens } = getConfig();
        const numCtx = Math.max(2048, contextLength);
        const outCap = Math.max(256, maxOutputTokens);

        let lastError: any;
        for (let attempt = 1; attempt <= 3; attempt++) {
            const controller = new AbortController();
            const timeoutId = setTimeout(() => controller.abort(), 45000);
            const combinedSignal = signal ? anySignal([signal, controller.signal]) : controller.signal;

            try {
                if (attempt > 1) await new Promise(resolve => setTimeout(resolve, 1000 * attempt));

            const response = await fetch(endpoint, {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify(isOllama ? {
                    model: this.modelName,
                    messages,
                    stream: false,
                    options: { temperature: 0.3, num_ctx: numCtx, num_predict: outCap }
                } : {
                    model: this.modelName,
                    messages,
                    stream: false,
                    temperature: 0.3,
                    max_tokens: outCap
                }),
                signal: combinedSignal
            });

            clearTimeout(timeoutId);

            if (!response.ok) {
                throw new Error(`Agent API Error: ${response.statusText} (${response.status})`);
            }

                const data = await response.json() as any;
                
                // 강력한 응답 추출 (Multi-path parsing)
                let content = '';
                if (data.message?.content) content = data.message.content;
                else if (data.choices?.[0]?.message?.content) content = data.choices[0].message.content;
                else if (data.choices?.[0]?.text) content = data.choices[0].text;
                else if (data.response) content = data.response;
                else if (typeof data === 'string') content = data;
                
                return content || '';
            } catch (error: any) {
                clearTimeout(timeoutId);
                lastError = error;
                if (error.name === 'AbortError') break;
                if (attempt === 3) break;
            }
        }
        throw lastError;
    }

    abstract execute(input: string, context?: string, signal?: AbortSignal, options?: AgentExecuteOptions): Promise<string>;
}

// Helper to combine signals (since AbortSignal.any is not always available in older Node)
function anySignal(signals: AbortSignal[]): AbortSignal {
    const controller = new AbortController();
    for (const signal of signals) {
        if (signal.aborted) {
            controller.abort();
            return signal;
        }
        signal.addEventListener('abort', () => controller.abort(), { once: true });
    }
    return controller.signal;
}

export class PlannerAgent extends BaseAgent {
    private readonly persona = `You are the [Master Strategist & Planner]. 
Your sole purpose is to transform vague requests into flawless, high-resolution execution blueprints.
- THINKING PROCESS: You must analyze the request from multiple angles (technical, logical, structural).
- OUTPUT RULE: You MUST output a structured <blueprint> using Markdown.
- COMPONENTS: Each blueprint must have [Objective], [Core Challenges], [Data Requirements], and [Step-by-Step Research Tasks].
- CONSTRAINT: Do not be vague. Use professional terminology. If the request is too simple, expand it with relevant technical considerations.`;

    async execute(input: string, brainContext?: string, signal?: AbortSignal, options?: AgentExecuteOptions): Promise<string> {
        const wrappedInput = `### SYSTEM INSTRUCTION: GENERATE EXECUTION BLUEPRINT
1. Target Goal: ${input}
2. Available Knowledge Base & Policy: ${brainContext}
3. Mission: Create a comprehensive research roadmap.`;
        return this.callLLM(this.persona, wrappedInput, signal);
    }
}

export class ResearcherAgent extends BaseAgent {
    private readonly persona = `You are the [Senior Technical Researcher]. 
Your mission is to extract, filter, and synthesize critical data based on a strategic blueprint.
- DATA INTEGRITY: Only provide high-quality, verified-style information. 
- FORMAT: Use [Key Facts], [Technical Deep-Dive], and [Summary of Knowledge] sections.
- CRITICAL THINKING: Identify gaps in the plan and provide extra insights to fill those gaps.
- NO FLUFF: Be concise but extremely dense with information.`;

    async execute(input: string, brainContext?: string, signal?: AbortSignal, options?: AgentExecuteOptions): Promise<string> {
        const wrappedInput = `### SYSTEM INSTRUCTION: DATA HARVESTING
1. Blueprint to Follow: ${input}
2. Contextual Constraints & Policy: ${brainContext}
3. Mission: Provide a dense summary of facts and technical insights.`;
        return this.callLLM(this.persona, wrappedInput, signal);
    }
}

export class WriterAgent extends BaseAgent {
    private readonly persona = `You are the [Lead Synthesis Writer & Editor].
Your goal is to produce a state-of-the-art final report that wows the user.
- TONE: Authoritative yet accessible. Professional developer/consultant style.
- STRUCTURE: Use an executive summary, detailed analysis sections, and a "Final Recommendation" block.
- LANGUAGE: Always respond in the user's language (KOREAN).
- POLISHING: Ensure logical flow between sections. Make it look like a premium report.
- SELF-CORRECTION: When a [REFLECTION CRITIQUE] block is provided, you MUST address each listed gap, contradiction, or missing-evidence item explicitly before producing the final report. Do not silently ignore the critique.`;

    async execute(input: string, originalRequest?: string, signal?: AbortSignal, options?: AgentExecuteOptions): Promise<string> {
        // [Astra v4.0] Advisor 모드 처리
        if (options?.config?.role === 'advisor') {
            const advisorPersona = `You are the [Strategic Proactive Advisor].
Analyze the provided report and suggest 3 high-impact next actions for the user.
- Focus on decision forks, risk mitigation, or immediate implementation steps.
- Be extremely concrete and actionable.
- Respond in KOREAN.`;
            return this.callLLM(advisorPersona, input, signal);
        }

        // Fix 3: Trim input if it's too long (Basic Context Diet)
        const trimmedData = input.length > 8000 ? input.substring(0, 8000) + '... [Data Trimmed for Performance]' : input;

        const policy = options?.context || '';
        const reflection = options?.priorResults?.reflection;
        // Reflector 결과가 있으면 별도 블록으로 주입. 길이 4000자 cap (Writer 입력 비대화 방지).
        const reflectionBlock = reflection && reflection.trim().length > 0
            ? `\n5. [REFLECTION CRITIQUE — must be addressed]:\n${reflection.length > 4000 ? reflection.substring(0, 4000) + '... [Critique Trimmed]' : reflection}`
            : '';

        const wrappedInput = `### SYSTEM INSTRUCTION: FINAL SYNTHESIS
1. Gathered Research Data: ${trimmedData}
2. User's Original Objective: ${originalRequest}
3. Applied Knowledge & Filtering Policy: ${policy}
4. Mission: Write the definitive final report in KOREAN.${reflectionBlock}`;
        return this.callLLM(this.persona, wrappedInput, signal);
    }
}

export class ReflectorAgent extends BaseAgent {
    private readonly persona = `You are the [Internal Critic & Self-Reflection Officer].
Your sole role is META-COGNITION: stress-test the plan and the research output BEFORE the Writer commits to a final report.
- POSTURE: Skeptical, rigorous, blunt. You are looking for what is WRONG, not what is right.
- DO NOT: rewrite the report, add new content, or speculate beyond the evidence provided.
- DO: surface gaps, unsupported claims, contradictions, drift from the original objective, and missing perspectives.
- OUTPUT STRICTLY in this Markdown shape (Korean):
  ## 🧭 Alignment with Objective
  - <원래 요청 대비 일치/이탈 평가>
  ## 🕳 Gaps & Missing Evidence
  - <plan에는 있지만 research가 다루지 않은 항목>
  ## ⚖️ Contradictions / Conflicts
  - <research 내부 또는 brain context와의 모순; 없으면 "발견되지 않음">
  ## 🚨 Unsupported / Weak Claims
  - <근거가 빈약하거나 일반화된 진술>
  ## ✅ Guidance for Writer
  - <Writer가 최종 리포트에서 반드시 보정해야 할 3~5개 구체 지시>
- CONSTRAINT: 최대 500단어. 새 지식을 만들지 말고, 제공된 자료에서만 판단할 것.`;

    async execute(input: string, _context?: string, signal?: AbortSignal, options?: AgentExecuteOptions): Promise<string> {
        const plan = options?.priorResults?.plan || '(plan unavailable)';
        const research = input;
        const originalPrompt = options?.priorResults?.originalPrompt || '(original prompt unavailable)';
        const brainContext = options?.context || '';

        // Reflector 는 중간 단계이므로 비대한 입력을 방지하기 위해 각 섹션을 cap.
        const cap = (s: string, n: number) => s.length > n ? s.substring(0, n) + '... [trimmed]' : s;

        const wrappedInput = `### SYSTEM INSTRUCTION: SELF-REFLECTION PASS
1. Original User Objective:
${cap(originalPrompt, 1500)}

2. Planner Blueprint:
${cap(plan, 3000)}

3. Researcher Output (to be critiqued):
${cap(research, 5000)}

4. Knowledge / Brain Context (for cross-check only — do not invent beyond this):
${cap(brainContext, 2000)}

5. Mission: Run a single rigorous reflection pass and output the structured critique exactly as specified by your persona.`;
        return this.callLLM(this.persona, wrappedInput, signal);
    }
}
