import * as vscode from 'vscode';
import * as path from 'path';
import { SidebarChatProvider } from '../sidebarProvider';
import { getActiveBrainProfile, logInfo } from '../utils';
import { pickConfigTarget } from '../lib/paths';

/**
 * Handles chat-domain messages: prompts, model selection, sessions, streaming control,
 * generic webview transport (export, settings, addMessage), action approvals, and the
 * cross-cutting `ready` bootstrap.
 *
 * Returns true when the message was handled by this domain, false otherwise — the
 * caller chains domain handlers until one accepts the message.
 */
export async function handleChatMessage(provider: SidebarChatProvider, data: any): Promise<boolean> {
    switch (data.type) {
        case 'prompt':
        case 'promptWithFile':
            logInfo(`[ASTRA-DEBUG] prompt case entered type=${data?.type} value=${JSON.stringify(String(data?.value ?? '').slice(0, 80))}`);
            provider._lmStudio?.activity.bump();
            // ── 📻 Datacollect Radio (slash 명령) 우선 분기 ──
            // 주의: globalState.update보다 *먼저* 잡는다 — 글로벌 state가 ~1MB까지
            // 누적된 환경에서 update가 느려 첫 prompt가 hang하는 사례 보고됨. slash
            // 명령은 LLM을 우회하니 blank chat state 갱신도 필요 없음.
            if (typeof data.value === 'string') {
                const { isSlashCommand, handleSlashCommand } = await import('../features/datacollect/slashRouter');
                const matched = isSlashCommand(data.value);
                logInfo(`[ASTRA-DEBUG] slash check matched=${matched} hasView=${!!provider._view}`);
                logInfo(`[SLASH] prompt received: ${JSON.stringify(data.value).slice(0, 100)} matched=${matched} hasView=${!!provider._view}`);
                if (matched) {
                    if (!provider._view?.webview) {
                        const msg = '📻 Datacollect Radio: 채팅 webview가 활성 상태가 아닙니다. Astra 사이드바를 한 번 열고 다시 시도해 주세요.';
                        await vscode.window.showWarningMessage(msg);
                        logInfo(`[SLASH] webview not available — aborting`);
                        return true;
                    }
                    logInfo(`[SLASH] handleSlashCommand entering`);
                    // Slash 명령 결과를 *chatHistory 에도* mirror — 다음 turn 의 LLM 이
                    // 직전 명령 출력 (예: /stocks discover 발굴 결과) 을 컨텍스트로 보게 한다.
                    // 안 하면 LLM 은 webview UI 만 보고 history 는 비어 있어서 "주식 데이터
                    // 없음" 같은 환각 응답이 나옴.
                    const realWebview = provider._view.webview;
                    const captured: string[] = [];
                    const captureWebview = {
                        postMessage: (msg: any) => {
                            if (msg && msg.type === 'streamChunk' && typeof msg.value === 'string') {
                                captured.push(msg.value);
                            }
                            return realWebview.postMessage(msg);
                        },
                    };
                    await handleSlashCommand(data.value, captureWebview as any, provider._context);
                    const collected = captured.join('').trim();
                    if (collected) {
                        // CRITICAL: getHistory() 는 *filtered copy* 를 반환하므로 직접 push 하면
                        // 원본 chatHistory 에 안 들어감 (silent fail). 새 배열로 setHistory.
                        const current = provider._agent.getHistory();
                        const updated = [
                            ...current,
                            { role: 'user' as const, content: data.value },
                            { role: 'assistant' as const, content: collected, internal: false },
                        ];
                        provider._agent.setHistory(updated);
                    }
                    logInfo(`[SLASH] handleSlashCommand returned, captured ${collected.length} chars → history len ${provider._agent.getHistory().length}`);
                    return true;
                }
            }
            await provider._sessionState.setBlankChatActive(false);
            // ── 1인 기업 모드 우선 분기 ──
            // 회사 모드일 땐 들어온 메시지를 intent classifier에 한 번 통과시켜
            // (a) 잡담/질문/짧은 응답 → 일반 채팅 경로, (b) 후속 대화 → 일반 채팅
            // 경로 + followup 라벨, (c) 신규 업무 → 풀 파이프라인 dispatch.
            // classifier가 비활성화돼 있거나 호출 실패 시엔 안전하게 new_task로
            // 폴백 — 즉 사용자가 의도한 작업 요청을 놓치는 일은 절대 없다.
            if (provider.isCompanyModeEnabled() && typeof data.value === 'string' && data.value.trim()) {
                let userPrompt = data.value.trim();
                const { getConfig } = await import('../config');
                const cfg = getConfig();
                const { readCompanyState, resolveActivePipeline } = await import('../features/company');
                const state = readCompanyState(provider._context);

                // ── alignment 답변 라우팅 ──
                // 사용자가 이전 메시지에서 alignment 카드를 받아 답변하는 중이면
                // 이 메시지를 분류기/dispatch가 아니라 alignment 답변 핸들러로
                // 보낸다. 답변이 처리되면서 자동으로 다음 라운드 또는 pipeline
                // 으로 진행됨.
                if (provider.isAlignmentPending()) {
                    await provider._handleAlignmentAnswer(userPrompt);
                    return true;
                }

                // ── 사용자 키워드 override ──
                // 입력 맨 앞에 `[파이프라인:id]` 또는 `[pipeline:id]`가 있으면
                // 분류기 무관하게 그 파이프라인 강제 + 그 키워드는 prompt에서
                // 제거 후 dispatcher에 전달. id가 유효하지 않으면 무시(분류기 정상 경로).
                let keywordOverrideId: string | undefined;
                const keywordMatch = userPrompt.match(/^\s*\[(?:파이프라인|pipeline)\s*:\s*([a-z0-9_-]+)\s*\]\s*/i);
                if (keywordMatch) {
                    const id = keywordMatch[1].toLowerCase();
                    if (state.pipelines?.[id]) {
                        keywordOverrideId = id;
                        userPrompt = userPrompt.slice(keywordMatch[0].length).trim() || userPrompt;
                    }
                }

                // ── alignment bypass 키워드 ──
                // 입력 맨 앞 `[건너뛰기]` 또는 `[skip]` → alignment 단계 1회 우회.
                // 사용자가 "지금은 빨리 가자"라고 명시한 경우에만 사용. prompt에서
                // 키워드 제거.
                let alignmentBypass = false;
                const bypassMatch = userPrompt.match(/^\s*\[(?:건너뛰기|skip)\]\s*/i);
                if (bypassMatch) {
                    alignmentBypass = true;
                    userPrompt = userPrompt.slice(bypassMatch[0].length).trim() || userPrompt;
                }

                if (cfg.companyDisableIntentClassifier) {
                    // 분류기 우회 모드 — 분류 단계는 건너뛰지만 alignment는 별도로
                    // 작동(사용자가 alignment off로 설정하지 않은 한). 분류기 끄는
                    // 이유는 보통 "잡담도 다 pipeline으로"인데 그럴수록 alignment
                    // 효과가 큼.
                    try { provider.pixelOfficeOnIntentClassified('new_task', userPrompt); } catch { /* noop */ }
                    if (cfg.companyIntentAlignmentMode === 'off' || alignmentBypass) {
                        await provider._runCompanyTurn(userPrompt, undefined, keywordOverrideId);
                    } else {
                        await provider._runIntentAlignment({
                            userPrompt,
                            pipelineIdOverride: keywordOverrideId,
                            mode: cfg.companyIntentAlignmentMode === 'strict' ? 'strict' : 'smart',
                            roundsLimit: cfg.companyIntentAlignmentMaxRounds,
                            roundsAsked: 0,
                        });
                    }
                    return true;
                }
                const { classifyChatIntent } = await import('../features/company');
                const { AIService } = await import('../core/services');
                const last = provider.getLastCompanyTurnSummary();
                const activePipeline = resolveActivePipeline(state);
                // 사용 가능한 모든 파이프라인을 분류기 후보로 전달 — 단, 활성화돼
                // 있어야 추천 의미가 있는 게 아니라 *정의돼 있기만 하면* 후보. 사용자가
                // 평소엔 짧은 걸 활성화해 두고 가끔 풀 사이클 의도가 명확한 발화를
                // 했을 때 분류기가 그쪽을 추천할 수 있게.
                const allPipelines = Object.values(state.pipelines ?? {});
                const verdict = await classifyChatIntent(
                    new AIService(),
                    userPrompt,
                    {
                        previousBrief: last?.brief,
                        previousReportTail: last?.reportTail,
                        previousTurnAt: last?.finishedAt,
                        activePipelineName: activePipeline?.name,
                        availablePipelines: allPipelines.length > 0
                            ? allPipelines.map((p) => ({
                                id: p.id,
                                name: p.name,
                                stageCount: p.stages.length,
                            }))
                            : undefined,
                    },
                    { model: cfg.companyIntentClassifierModel || cfg.defaultModel },
                );
                // Pixel Office: 분류 결과를 UI layer로만 흘림. 아래 분기 자체엔 영향 없음.
                try { provider.pixelOfficeOnIntentClassified(verdict.intent, userPrompt); } catch { /* noop */ }
                if (verdict.intent === 'new_task') {
                    // 우선순위: (1) 사용자 키워드 (2) autoSelect가 켜져 있고 분류기 추천 있음 (3) 사용자 활성 파이프라인.
                    let effectiveOverride = keywordOverrideId;
                    if (!effectiveOverride && cfg.companyAutoSelectPipeline && verdict.suggestedPipelineId) {
                        effectiveOverride = verdict.suggestedPipelineId;
                    }
                    // 분류기가 추천을 냈지만 autoSelect가 꺼져 있을 땐 라벨로만 안내.
                    if (verdict.suggestedPipelineId && !effectiveOverride && !cfg.companyAutoSelectPipeline) {
                        const tip = state.pipelines?.[verdict.suggestedPipelineId];
                        if (tip) {
                            provider._view?.webview.postMessage({
                                type: 'companyIntentDecision',
                                value: {
                                    intent: 'new_task',
                                    reason: `🧭 추천 파이프라인: "${tip.name}" (자동 적용은 설정 토글)`,
                                    label: '🛠️ 신규 업무',
                                },
                            });
                        }
                    } else if (effectiveOverride && effectiveOverride !== state.activePipelineId) {
                        const used = state.pipelines?.[effectiveOverride];
                        if (used) {
                            provider._view?.webview.postMessage({
                                type: 'companyIntentDecision',
                                value: {
                                    intent: 'new_task',
                                    reason: keywordOverrideId
                                        ? `🔧 키워드 override → "${used.name}"`
                                        : `🧭 CEO 자동 선택 → "${used.name}"`,
                                    label: '🛠️ 신규 업무',
                                },
                            });
                        }
                    }
                    // ── Intent Alignment 진입 ──
                    // off 모드이거나 bypass 키워드가 있으면 alignment 우회하고
                    // legacy 동작 (즉시 dispatch). 그 외엔 분석기 1라운드 돌려
                    // confidence에 따라 자동 진행 또는 카드 표시.
                    if (cfg.companyIntentAlignmentMode === 'off' || alignmentBypass) {
                        await provider._runCompanyTurn(userPrompt, undefined, effectiveOverride);
                    } else {
                        await provider._runIntentAlignment({
                            userPrompt,
                            pipelineIdOverride: effectiveOverride,
                            mode: cfg.companyIntentAlignmentMode === 'strict' ? 'strict' : 'smart',
                            roundsLimit: cfg.companyIntentAlignmentMaxRounds,
                            roundsAsked: 0,
                        });
                    }
                } else {
                    await provider._handleCompanyCasual(userPrompt, verdict.intent, verdict.reason, data);
                }
                return true;
            }
            try {
                await provider._handlePrompt(data);
                await provider._autoWriteChronicleAfterPrompt();
            } finally {
                // Persist the session even if _handlePrompt or the chronicle
                // auto-write throws *after* the answer already streamed —
                // otherwise the reply shows in the UI but never lands in the
                // 기록(Chat History) list. This is the regression that made
                // recent conversations stop appearing.
                await provider._saveCurrentSession();
            }
            return true;
        case 'activity':
            provider._lmStudio?.activity.bump();
            return true;
        case 'ready':
            await provider._sendBrainStatus();
            await provider._sendBrainProfiles();
            await provider._sendSessionList();
            await provider._sendModels();
            await provider._sendChronicleProjects();
            await provider._restoreActiveSessionIntoView();
            await provider._sendReadyStatus();
            // Slash 명령 목록 — webview 의 `/` 자동완성 dropdown 이 사용.
            try {
                const { listSlashCommands } = await import('../features/datacollect/slashRouter');
                const cmds = listSlashCommands().map(c => ({ name: c.name, description: c.description || '' }));
                provider._view?.webview.postMessage({ type: 'slashCommandList', commands: cmds });
            } catch (e: any) {
                // Slash 라우터 로드 실패해도 채팅 자체는 동작해야 — silent skip.
            }
            // Restore the Project Architecture chip + watcher if the active project
            // was already running in architecture mode in a previous VS Code session.
            await provider._sendArchitectureStatus();
            // Restore the Company chip from globalState so the user sees the same
            // mode they had on at last shutdown.
            await provider._sendCompanyStatus();
            // Pixel Office — 첫 로드 시 빈 idle 상태라도 한 번 push해서 webview가
            // 영역 자체를 그릴 수 있게.
            provider.pixelOfficeResend();
            return true;
        case 'getReadyStatus':
            await provider._sendReadyStatus();
            return true;
        case 'createLessonFromConversation':
            await vscode.commands.executeCommand('g1nation.lesson.fromConversation');
            return true;
        case 'manageLessons':
            await vscode.commands.executeCommand('g1nation.lesson.manage');
            return true;
        case 'getModels':
            await provider._sendModels();
            return true;
        case 'getSessions':
            await provider._sendSessionList();
            return true;
        case 'newChat':
            provider._currentSessionId = null;
            provider._currentSessionBrainId = getActiveBrainProfile().id;
            provider._agent.resetConversation();
            await provider._sessionState.setActiveSessionId(null);
            await provider._sessionState.setLastVisibleChat(null);
            await provider._sessionState.setBlankChatActive(true);
            // 직전 회사 turn 컨텍스트 캐시 비우기 — 새 세션은 followup 기준점이 없다.
            provider.clearLastCompanyTurnSummary();
            // 진행 중이던 alignment도 새 세션과 함께 폐기.
            provider.cancelPendingAlignment();
            provider.clearChat();
            await provider._sendBrainStatus();
            return true;
        case 'stopGeneration':
            // 1인 기업 모드는 AgentExecutor를 거치지 않으므로 별도 abort 경로.
            // 두 경로 모두 신호를 보내 두면 중간에 모드 전환되어도 안전.
            provider.abortCompanyTurn();
            // 진행 중인 Intent Alignment도 같이 정리 — 사용자가 Stop 누르면
            // 의도상 모든 대기 상태 해제.
            provider.cancelPendingAlignment();
            provider._agent.stop();
            return true;
        case 'loadSession':
            // 세션 전환 시 직전 회사 turn 캐시 무효화 — 로드된 세션의 직전 회사
            // 작업은 별도 파일에서 복원되어야 하지 메모리 캐시에 남은 다른
            // 세션의 보고서가 새 세션 첫 메시지의 followup 판정을 오염시키면 안 됨.
            provider.clearLastCompanyTurnSummary();
            provider.cancelPendingAlignment();
            await provider._loadSession(data.id);
            return true;
        case 'deleteSession':
            await provider._deleteSession(data.id);
            return true;
        case 'openSettings':
            // Route the sidebar gear button to Astra's own settings webview.
            // Falls back to VS Code Settings if the view hasn't registered yet
            // (e.g. during the very first activation pass) and surfaces any
            // unexpected error so the user isn't stuck with a silent button.
            try {
                await vscode.commands.executeCommand('g1nation.settings.focus');
            } catch (e: any) {
                logInfo('openSettings: settings.focus failed, falling back to VS Code Settings.', { error: e?.message ?? String(e) });
                try {
                    await vscode.commands.executeCommand('workbench.action.openSettings', 'g1nation');
                } catch (e2: any) {
                    vscode.window.showErrorMessage(`Astra Settings 열기 실패: ${e2?.message ?? e2}`);
                }
            }
            return true;
        case 'addMessage':
            provider._view?.webview.postMessage({ type: 'addMessage', role: data.role, value: data.value, rationale: data.rationale });
            return true;
        case 'refreshModels':
            await provider._sendModels(true);
            return true;
        case 'model': {
            // Write to whichever scope already holds the value so a stale
            // Workspace override doesn't shadow our Global update — that was
            // the "sidebar shows e2b but Settings shows e4b" desync.
            const { target } = pickConfigTarget('g1nation', 'defaultModel');
            await vscode.workspace.getConfiguration('g1nation').update('defaultModel', data.value, target);
            logInfo(`Default model updated to: ${data.value}`, { target });
            // Wipe any persistent LM Studio error segment from the readyBar — the
            // next load attempt will repaint it if it also fails.
            provider.clearLmStudioError();
            provider._lmStudio?.lifecycle.onModelSelected(data.value);
            return true;
        }
        case 'getKnowledgeMix': {
            // Ship the current global Knowledge Mix to the webview so the slider can
            // initialize. Per-agent overrides ride along with the agent map data.
            const cfg = vscode.workspace.getConfiguration('g1nation');
            const w = cfg.get<number>('knowledgeMix.secondBrainWeight', 50);
            const clamped = Math.max(0, Math.min(100, Math.round(typeof w === 'number' ? w : 50)));
            provider._view?.webview.postMessage({
                type: 'knowledgeMix',
                value: { weight: clamped, source: 'global' },
            });
            return true;
        }
        case 'setKnowledgeMix': {
            const raw = typeof data.value === 'number' ? data.value : NaN;
            if (!Number.isFinite(raw)) return true;
            const clamped = Math.max(0, Math.min(100, Math.round(raw)));
            // Use whichever scope already holds the value to avoid the same "Workspace
            // override shadows Global update" desync that the `model` case guards against.
            const { target } = pickConfigTarget('g1nation', 'knowledgeMix.secondBrainWeight');
            await vscode.workspace.getConfiguration('g1nation').update('knowledgeMix.secondBrainWeight', clamped, target);
            logInfo(`Knowledge Mix (global) updated to: ${clamped}`, { target });
            return true;
        }
        // ── Project Architecture (Feature 2) ──────────────────────────────────
        case 'getArchitectureStatus':
            await provider._sendArchitectureStatus();
            return true;
        case 'openArchitectureDoc':
            await provider._openArchitectureDoc();
            return true;
        case 'refreshArchitecture':
            await provider._refreshArchitecture();
            return true;
        case 'detachArchitecture':
            await provider._detachArchitecture();
            return true;
        case 'attachArchitecture':
            // Re-enable architecture context for the current workspace —
            // user clicked the inactive chip's [Attach] button.
            await provider._attachArchitecture();
            return true;
        case 'activateArchitectureFromText': {
            // Optional explicit-toggle path: webview can pass arbitrary text
            // (e.g. the current input draft) for one-shot intent detection.
            if (typeof data.text === 'string') {
                await provider._tryActivateArchitectureFromText(data.text);
            }
            return true;
        }
        // ── 1인 기업 모드 메시지 라우팅 ────────────────────────────────────
        // 별도 도메인 핸들러에 위임. 30+ 개의 회사 모드 메시지가 한 파일로 부풀어
        // SRP 가 깨졌던 걸 분리. 처리 못 한 경우 false 반환 → 아래 default 로 흐름.
        case 'getCompanyStatus':
        case 'getCompanyAgents':
        case 'getCompanyResumable':
        case 'discardResumableSession':
        case 'setCompanyEnabled':
        case 'setCompanyName':
        case 'setCompanyActiveAgents':
        case 'setCompanyAgentModel':
        case 'setCompanyAgentDisplay':
        case 'setCompanyAgentRoleCategory':
        case 'setCompanyAgentKnowledgeMix':
        case 'setCompanyAgentPrompt':
        case 'addCompanyAgent':
        case 'deleteCompanyAgent':
        case 'restoreHiddenAgent':
        case 'getCompanyPipelines':
        case 'upsertCompanyPipeline':
        case 'deleteCompanyPipeline':
        case 'getCompanyPipelineTemplate':
        case 'getPixelOfficeState':
        case 'openPixelOfficePanel':
        case 'respondCompanyAlignment':
        case 'respondCompanyApproval':
        case 'setActiveCompanyPipeline':
        case 'setCompanyScopePreset':
        case 'resumeCompanyTurn': {
            const { handleCompanyMessage } = await import('./companyHandlers');
            return await handleCompanyMessage(provider, data);
        }
        case 'proactiveTrigger':
            await provider._handleProactiveSuggestion(data.context);
            return true;
        case 'exportResponse': {
            const workspacePath = vscode.workspace.workspaceFolders?.[0].uri.fsPath || '';
            const defaultPath = path.join(workspacePath, 'g1_response.md');
            const uri = await vscode.window.showSaveDialog({
                defaultUri: vscode.Uri.file(defaultPath),
                filters: { 'Markdown': ['md'] }
            });
            if (uri) {
                await vscode.workspace.fs.writeFile(uri, Buffer.from(data.text, 'utf8'));
                vscode.window.showInformationMessage(`✅ Exported to ${path.basename(uri.fsPath)}`);
            }
            return true;
        }
        case 'approveAction':
            await provider._agent.approveTransaction();
            return true;
        case 'rejectAction':
            await provider._agent.rejectTransaction();
            return true;
        default:
            return false;
    }
}
