import * as vscode from 'vscode';
import * as fs from 'fs';
import * as path from 'path';
// TeamOps + System + Datacollect handler 자기 등록 — side-effect import. slashRouter
// 가 이미 로드된 후 등록되도록 entry point 에서. (v2.2.196~201 도메인별 파일 분리)
import './features/teamops/handlers';
import './features/system/handlers';
import './features/datacollect/handlers';
// axios removed in favor of native fetch
import {
    _getBrainDir,
    _isBrainDirExplicitlySet,
    findBrainFiles,
    SYSTEM_PROMPT,
    buildApiUrl,
    logError,
    logInfo,
    resolveEngine,
    openInEditorGroup
} from './utils';
import { getConfig, validateConfig } from './config';
import { AgentExecutor } from './agent';
import { BridgeServer } from './bridge';
import { SidebarChatProvider } from './sidebarProvider';
import { HealthCheckMonitor } from './core/health';
import { initAstraPathResolver } from './core/astraPath';
import { LMStudioClient } from './lmstudio/client';
import { ActivityTracker } from './lmstudio/activityTracker';
import { ModelLifecycleManager } from './lmstudio/lifecycleManager';
import { LMStudioStreamer } from './lmstudio/streamer';
import { NodeSystemSpecsProvider, HeuristicModelMemoryEstimator } from './system/specs';
import { ApprovalQueue } from './features/approval/approvalQueue';
import { ApprovalPanelProvider } from './features/approval/approvalPanelProvider';
import { ApprovalStatusBar } from './features/approval/approvalStatusBar';
import { TelegramHttpClient } from './integrations/telegram/telegramClient';
import { TelegramBot } from './integrations/telegram/telegramBot';
import { getBrainTokenIndex, clearBrainTokenIndex } from './retrieval';
import { lessonTemplate, lessonSlug, parseLessonFrontmatter, normalizeLessonTitle, bumpLessonOccurrences } from './retrieval/lessonHelpers';
import { runConnectGoogleCalendarIcal, runConnectGoogleCalendarOAuth } from './extension/calendarSetup';
import { runInitialSetup } from './extension/initialSetup';
import { startStocksWatcher } from './features/stocks';
import { startDailyBriefingWatcher } from './features/briefing/dailyBriefing';
import { ensureDefaultBrainConfigured } from './extension/brainBootstrap';
import { ensureEmbeddingConfigured } from './extension/embeddingBootstrap';
import { ensureFeatureInventory } from './extension/featureInventory';
import { startGrowthCycleWatcher, runGrowthCycleOnce } from './features/growth/growthCycleWatcher';
import { startSleepDigestWatcher, runSleepDigestOnce } from './features/growth/sleepDigest';
import { registerProviderCommands } from './extension/providerCommands';
import { registerScaffoldCommand } from './extension/scaffoldCommand';
import { registerLessonCommands } from './extension/lessonCommands';
import { registerEvalCommands } from './extension/evalCommands';
import { registerTelegramCommands, TELEGRAM_TOKEN_SECRET_KEY, type TelegramTokenStore } from './extension/telegramCommands';
import { setupSettingsPanel } from './extension/settingsSetup';
import { createTelegramBot } from './integrations/telegram/telegramSetup';

let _lifecycleManager: ModelLifecycleManager | undefined;
let _telegramBot: TelegramBot | undefined;

/**
 * Astra Extension Entry Point
 */
export async function activate(context: vscode.ExtensionContext) {
    // Activation 시점 popup + DevTools console — 사용자 환경(Antigravity 등 VS Code 변종)
    // 에서 우리 vsix가 실제로 활성화됐는지 결정적으로 가시화. console.error는 사용자가
    // F12 DevTools console에서 다른 모든 출력과 함께 그대로 보인다 (logInfo의 OutputChannel
    // 과 별개 채널 — popup도 OutputChannel도 못 보는 경우의 마지막 안전망).
    const ext = vscode.extensions.getExtension('g1nation.astra');
    const version = ext?.packageJSON?.version || '(unknown)';
    console.log(`[ASTRA-DEBUG] activate v${version} pid=${process.pid}`);
    void vscode.window.showInformationMessage(`📡 Astra v${version} activated (PID=${process.pid})`);
    logInfo(`Astra activating... version=${version} pid=${process.pid}`);

    // 두뇌 기본 위치 부트스트랩 — 미설정이면 문서\AstraBrain 생성·등록.
    // 성장 데이터(레슨·기억·지식)가 숨김 폴백(~/.g1nation-brain)으로 조용히 가는 것을 방지.
    // 이후 코드가 getConfig() 로 두뇌 경로를 읽으므로 설정 기록이 끝난 뒤 진행해야 한다.
    await ensureDefaultBrainConfigured();

    // 임베딩 자동 감지 — 엔진에 임베딩 모델이 로드돼 있으면 하이브리드 검색을 켠다.
    // 비차단 (TF-IDF 검색은 이미 동작 중 — 결과를 기다릴 필요 없음).
    void ensureEmbeddingConfigured(context);

    // 기능 인벤토리 — 버전 변경 시 package.json 에서 자기 기능 문서를 기계 생성해
    // 두뇌에 기록. 자기 평가가 구식 스냅샷 대신 항상 현행 소스를 근거로 하게 한다.
    void ensureFeatureInventory(context);

    // Initialize Astra Path Resolver (.astra → ConnectAI/.astra/)
    initAstraPathResolver(context);
    
    // Start Environment Health Monitoring
    HealthCheckMonitor.runAllChecks();
    HealthCheckMonitor.startInterval(600000); // Check every 10 mins
    
    // 0. Validate Configuration
    const validation = validateConfig();
    if (!validation.valid) {
        vscode.window.showErrorMessage(`Astra Configuration Error: ${validation.errors.join(' ')}`);
        logError('Configuration validation failed.', { errors: validation.errors });
    }

    // 1. Ensure Brain Directory
    await _ensureBrainDir(context);

    // 2. Initialize LM Studio Lifecycle Subsystem
    let provider: SidebarChatProvider | undefined;
    const initialUrl = getConfig().ollamaUrl;
    const activityTracker = new ActivityTracker();
    const lmStudioClient = new LMStudioClient(initialUrl);
    const systemSpecs = new NodeSystemSpecsProvider();
    const memoryEstimator = new HeuristicModelMemoryEstimator();
    logInfo('System specs detected.', { summary: systemSpecs.get().summary });
    const lifecycle = new ModelLifecycleManager({
        client: lmStudioClient,
        activity: activityTracker,
        getConfig: () => {
            // Read from getConfig() so we share the same setting parsers (incl. gpuOffloadRatio coercion)
            // with the rest of the codebase instead of duplicating the logic here.
            const ag = getConfig();
            const cfg = vscode.workspace.getConfiguration('g1nation');
            return {
                idleTimeoutMs: cfg.get<number>('lmStudio.idleTimeoutMs', 300000),
                autoLoadOnSelect: cfg.get<boolean>('lmStudio.autoLoadOnSelect', true),
                loadConfig: {
                    flashAttention: ag.lmStudioLoad.flashAttention,
                    gpuOffloadRatio: ag.lmStudioLoad.gpuOffloadRatio,
                    offloadKVCacheToGpu: ag.lmStudioLoad.offloadKVCacheToGpu,
                    keepModelInMemory: ag.lmStudioLoad.keepModelInMemory,
                    useFp16ForKVCache: ag.lmStudioLoad.useFp16ForKVCache,
                    evalBatchSize: ag.lmStudioLoad.evalBatchSize,
                },
                draftModel: ag.lmStudioDraftModel || undefined,
            };
        },
        notifyError: (msg) => provider?.postLmStudioError(msg),
        initialEngine: resolveEngine(initialUrl),
        systemSpecs,
        memoryEstimator,
    });
    _lifecycleManager = lifecycle;
    context.subscriptions.push({ dispose: () => activityTracker.dispose() });
    context.subscriptions.push({ dispose: () => lifecycle.dispose() });

    // React to engine URL changes — re-target the SDK and reset state.
    context.subscriptions.push(
        vscode.workspace.onDidChangeConfiguration((e) => {
            if (!e.affectsConfiguration('g1nation.ollamaUrl')) return;
            const newUrl = vscode.workspace.getConfiguration('g1nation').get<string>('ollamaUrl', '');
            lmStudioClient.setBaseUrl(newUrl);
            lifecycle.setEngine(resolveEngine(newUrl));
        })
    );

    // Keep the sidebar's model dropdown in sync when defaultModel / ollamaUrl is
    // changed from elsewhere (Settings panel, raw settings.json, …). Without
    // this the user sees a desync: Settings shows the new model, sidebar still
    // shows the old one until a manual refresh.
    context.subscriptions.push(
        vscode.workspace.onDidChangeConfiguration((e) => {
            const touchedModel = e.affectsConfiguration('g1nation.defaultModel');
            const touchedUrl = e.affectsConfiguration('g1nation.ollamaUrl');
            if (!touchedModel && !touchedUrl) return;
            // _sendModels is best-effort; the provider may not have a webview
            // attached yet during very early activation.
            void provider?._sendModels(touchedUrl);
            // 모델이 *어디서든*(설정 패널·settings.json·사이드바) 바뀌면 lifecycle 을
            // 깨워 이전 모델 자동 언로드 → 새 모델 로드. 이게 없으면 설정 패널에서
            // 전환 시 lifecycle 이 모른 채 추론 시점에 JIT 로드만 돼 VRAM 이 안 비고
            // 로드 실패가 난다.
            if (touchedModel) {
                const newModel = (vscode.workspace.getConfiguration('g1nation').get<string>('defaultModel', '') || '').trim();
                if (newModel) lifecycle.onModelSelected(newModel);
            }
        })
    );

    // 3. Initialize Approval subsystem (queue + panel webview + status bar badge)
    // Astra 2.81: sidebar view container is gone; all webviews open in editor
    // column 3 instead. We don't register a WebviewViewProvider — panels are
    // created on-demand via openAsPanel().
    const approvalQueue = new ApprovalQueue();
    const approvalPanel = new ApprovalPanelProvider(context.extensionUri, approvalQueue);
    const approvalStatusBar = new ApprovalStatusBar(approvalQueue);
    context.subscriptions.push(
        approvalStatusBar,
        { dispose: () => approvalQueue.dispose() },
        vscode.commands.registerCommand(ApprovalStatusBar.focusCommand, () => approvalPanel.focus()),
    );

    // 4. Initialize Agent Executor (with stream lifecycle hooks + LM Studio SDK streamer + approval queue)
    const lmStudioStreamer = new LMStudioStreamer(lmStudioClient);
    const agent = new AgentExecutor(context, {
        onStreamLifecycle: {
            start: () => lifecycle.onStreamStart(),
            end: () => lifecycle.onStreamEnd(),
        },
        lmStudioStreamer,
        approvalQueue,
    });

    // 4. Initialize Chat Provider (renders into an editor column, not a sidebar view)
    provider = new SidebarChatProvider(context.extensionUri, context, agent, {
        lifecycle,
        activity: activityTracker,
        loadedModels: () => lmStudioClient.listLoadedCached(),
        downloadedModels: () => lmStudioClient.listDownloadedCached(),
    });
    // One-time repair: rewrite any chronicle projects that were saved with the
    // workspace parent as their `projectRoot` (a side-effect of the old
    // pre-multi-subproject activation code). Idempotent and silent when there's
    // nothing to fix.
    void provider._repairCorruptedChronicleProjectRoots().catch((e: any) => {
        logError('architecture: chronicle repair failed.', { error: e?.message ?? String(e) });
    });
    context.subscriptions.push(
        vscode.commands.registerCommand('g1nation.openChat', () => {
            provider!.openAsPanel(vscode.ViewColumn.Three);
        })
    );

    // Datacollect Python 의존성 자동 점검·설치 — `Astra: Setup Datacollect Dependencies`.
    // 슬래시 명령(/youtube /research /wikify 등)이 yt-dlp / youtube-transcript-api 같은
    // Python 패키지를 필요로 하는데, Astra 확장만 깔면 그게 자동으로 따라오지 않아
    // 사용자가 매번 수동 pip install 해야 했음. 이 명령으로 한 번에 처리.
    context.subscriptions.push(
        vscode.commands.registerCommand('g1nation.setupDatacollect', async () => {
            const { runDatacollectSetup } = await import('./features/setup/datacollectSetup');
            await runDatacollectSetup();
        })
    );

    // ── Activity Bar launcher view ────────────────────────────────────────
    // Adds a sparkle (✦) icon to VS Code's left activity bar. Clicking it
    // opens a small sidebar with action buttons (Open Chat / New Chat /
    // Settings / Company / Architecture). Solves the user-reported pain
    // point: when the Astra Chat editor tab is accidentally closed, there
    // was no one-click way to reopen it short of restarting the extension.
    //
    // The view itself has no items — VS Code renders the `viewsWelcome`
    // content from package.json instead, which is just a list of command
    // links. Cheap, theme-aware, no webview to maintain.
    const astraLauncherProvider: vscode.TreeDataProvider<never> = {
        getTreeItem: () => new vscode.TreeItem(''),
        getChildren: () => [],
    };
    context.subscriptions.push(
        vscode.window.registerTreeDataProvider('g1nation-astra-launcher', astraLauncherProvider),
    );

    // 4. Initialize Bridge Server (Port 4825)
    const bridge = new BridgeServer(provider);
    try {
        bridge.start();
        logInfo('Bridge server started on port 4825.');
    } catch (err) {
        logError('Failed to start bridge server.', err);
    }


    // 5. Register Core Commands
    context.subscriptions.push(
        vscode.commands.registerCommand('g1nation.focusInput', () => {
            provider.focusInput();
        })
    );

    context.subscriptions.push(
        vscode.commands.registerCommand('g1nation.clearChat', () => {
            provider.clearChat();
        })
    );

    context.subscriptions.push(
        vscode.commands.registerCommand('g1nation.syncBrain', async () => {
            await provider!.syncBrain();
        })
    );

    // Telegram Bot integration — opt-in (g1nation.telegram.enabled), token in SecretStorage.
    // `tokenStore` 는 telegramCommands 모듈과 공유 — 모듈이 secrets change 시
    // `tokenStore.current` 를 갱신하면 client 가 다음 호출에서 새 값을 본다.
    const tokenStore: TelegramTokenStore = {
        current: (await context.secrets.get(TELEGRAM_TOKEN_SECRET_KEY)) || '',
    };
    const telegramClient = new TelegramHttpClient({
        getToken: () => tokenStore.current,
    });

    // buildTelegramSystemPrompt / looksLikeWorkOrder / buildTelegramCompanyContext /
    // latestCompanySessionDir / chunkTelegramMessage → `src/integrations/telegram/promptBuilders.ts`
    // handler 콜백 + AIService → `src/integrations/telegram/telegramSetup.ts`
    const telegramBot = createTelegramBot(context, { telegramClient, getProvider: () => provider });
    _telegramBot = telegramBot;

    context.subscriptions.push(
        // refresh + dispose + listeners + setBotToken/clearBotToken/testConnection → `src/extension/telegramCommands.ts`
        ...registerTelegramCommands(context, { telegramBot, telegramClient, tokenStore }),
        // knowledge map + lesson cards → `src/extension/lessonCommands.ts`
        ...registerLessonCommands({ getAgent: () => agent }),
        // 검색 평가 하니스 (recall@k / MRR) → `src/extension/evalCommands.ts`
        ...registerEvalCommands(),
        // architecture / company / calendar / devil commands → `src/extension/providerCommands.ts`
        ...registerProviderCommands(context, { getProvider: () => provider }),
    );

    // listLessonFiles / createLessonCard / manageLessons → `src/extension/lessons.ts`

    // Astra Settings webview — single entry point for user-facing config (Phase 5-A: Telegram only).
    // panel 인스턴스 + listener/command disposables → `src/extension/settingsSetup.ts`
    const { settingsPanel, disposables: settingsDisposables } = setupSettingsPanel(context, {
        telegramClient,
        telegramBot,
        // 모델 dropdown 이 보유 모델 전부를 보이도록 SDK 다운로드 목록을 전달.
        lmStudioDownloaded: () => lmStudioClient.listDownloadedCached(),
    });
    context.subscriptions.push(...settingsDisposables);

    // g1nation.scaffoldProject → `src/extension/scaffoldCommand.ts`
    context.subscriptions.push(registerScaffoldCommand());

    // 6. Run Initial Setup (Automatic Model/Engine Detection)
    const setupComplete = context.globalState.get<boolean>('setupComplete', false);
    if (!setupComplete) {
        await runInitialSetup(context);
    }

    // Stocks watcher — VS Code 시작 시 자동 활성화. KST 09:00 / 15:00 자동 트리거.
    // disposable 은 subscriptions 에 푸시해 종료 시 timer cleanup.
    context.subscriptions.push(startStocksWatcher(context));

    // Daily briefing — 평일 KST 09:30 오늘의 일정·할일을 텔레그램으로 발송.
    // 텔레그램/캘린더 미연결이면 fire 시점에 조용히 skip (로그만).
    context.subscriptions.push(startDailyBriefingWatcher(context));

    // 주간 성장 사이클 — Self-Evolving OS 폐루프 (평가→학습큐→노후점검→승인분 실행→통지).
    context.subscriptions.push(startGrowthCycleWatcher(context));
    context.subscriptions.push(vscode.commands.registerCommand('g1nation.growthCycle.runNow', async () => {
        vscode.window.showInformationMessage('성장 사이클 수동 실행 중… (완료 시 알림)');
        const summary = await runGrowthCycleOnce(context);
        vscode.window.showInformationMessage(`성장 사이클 완료 — ${summary}`);
    }));

    // Sleep-time 지식 사전 소화 — 유휴 시간(기본 03:00 KST)에 최근 변경 지식을
    // learned-context 소화 노트(<brain>/Digests/)로 변환. 응답 지연을 유휴 시간으로 이동.
    context.subscriptions.push(startSleepDigestWatcher());
    context.subscriptions.push(vscode.commands.registerCommand('g1nation.sleepDigest.runNow', async () => {
        vscode.window.showInformationMessage('지식 사전 소화 실행 중… (LLM 호출, 수 분 소요 가능)');
        const summary = await runSleepDigestOnce();
        vscode.window.showInformationMessage(`사전 소화 완료 — ${summary}`);
    }));
    context.subscriptions.push(vscode.commands.registerCommand('g1nation.conflictScan.runNow', async () => {
        const { runConflictScanOnce } = await import('./features/growth/conflictScan');
        vscode.window.showInformationMessage('지식 충돌 스캔 실행 중…');
        const summary = await runConflictScanOnce();
        vscode.window.showInformationMessage(`충돌 스캔 완료 — ${summary}`);
    }));

    // 7. Auto-open all three Astra webviews as tabs in editor column 3.
    // The sidebar/activity-bar entry point was removed in 2.81 — all three views
    // (Chat, Approvals, Settings) now stack as tabs in the third editor column.
    // Order matters: Chat opens last so it ends up as the active tab.
    try {
        approvalPanel.openAsPanel(vscode.ViewColumn.Three);
        await settingsPanel.openAsPanel(vscode.ViewColumn.Three);
        provider!.openAsPanel(vscode.ViewColumn.Three);
    } catch (e) {
        logError('Failed to auto-open Astra panels.', e);
    }
}

export async function deactivate() {
    HealthCheckMonitor.dispose();
    // Release the in-memory brain token index (and any pending debounced disk
    // write timer) — the `_states` Map is otherwise never cleared for the
    // process lifetime.
    clearBrainTokenIndex();
    if (_telegramBot) {
        try { await _telegramBot.stop(); } catch (e) { logError('Telegram bot stop during deactivate failed.', e); }
        _telegramBot = undefined;
    }
    if (_lifecycleManager) {
        try {
            await _lifecycleManager.disposeAndUnload(2000);
        } catch (e) {
            logError('Lifecycle dispose during deactivate failed.', e);
        }
        _lifecycleManager = undefined;
    }
}

// runConnectGoogleCalendarIcal / runConnectGoogleCalendarOAuth → `src/extension/calendarSetup.ts`

// runInitialSetup → `src/extension/initialSetup.ts`

async function _ensureBrainDir(context: vscode.ExtensionContext): Promise<string | null> {
    if (_isBrainDirExplicitlySet()) {
        const dir = _getBrainDir();
        if (!fs.existsSync(dir)) {
            try {
                fs.mkdirSync(dir, { recursive: true });
            } catch (e) {
                logError('Failed to create brain directory.', e);
            }
        }
        return dir;
    }

    const defaultDir = _getBrainDir();
    if (!fs.existsSync(defaultDir)) {
        try {
            fs.mkdirSync(defaultDir, { recursive: true });
            // Create a welcome file
            fs.writeFileSync(path.join(defaultDir, 'Welcome.md'), "# Welcome to your Second Brain\n\nAstra will store and retrieve knowledge from here.");
        } catch (e) {
            logError('Failed to initialize default brain directory.', e);
        }
    }
    return defaultDir;
}

