(function () {
    const vscode = acquireVsCodeApi();
    const $ = (id) => document.getElementById(id);

    // ---- Telegram ----
    const tokenInput = $('tgToken');
    const saveBtn = $('tgSaveToken');
    const clearBtn = $('tgClearToken');
    const testBtn = $('tgTest');
    const enabledChk = $('tgEnabled');
    const enrollBtn = $('tgEnroll');
    const enrollCancelBtn = $('tgEnrollCancel');
    const enrollStatus = $('tgEnrollStatus');
    const tokenStatus = $('tgTokenStatus');
    const botName = $('tgBotName');
    const tgFeedback = $('tgFeedback');
    const tgError = $('tgError');
    const chatList = $('tgChatIds');

    // ---- Connection ----
    const cnUrl = $('cnUrl');
    const cnModel = $('cnModel');
    const cnTimeout = $('cnTimeout');
    const cnRefreshModels = $('cnRefreshModels');
    const cnModelHint = $('cnModelHint');

    // ---- Datacollect ----
    const dcBridgeTarget = $('dcBridgeTarget');
    const dcBridgeUrl = $('dcBridgeUrl');
    const dcBridgeNasUrl = $('dcBridgeNasUrl');
    const dcBridgeNasToken = $('dcBridgeNasToken');
    const dcSavePath = $('dcSavePath');
    const dcCrawlDepth = $('dcCrawlDepth');
    const dcMaxPages = $('dcMaxPages');
    const dcSynthTemp = $('dcSynthTemp');

    // ---- Memory ----
    const memEnabled = $('memEnabled');
    const memShort = $('memShort');
    const memMid = $('memMid');
    const memLong = $('memLong');

    // ---- Brain ----
    const brainName = $('brainName');
    const brainPath = $('brainPath');
    const brainAutoPush = $('brainAutoPush');

    // ---- Advanced ----
    const advDryRun = $('advDryRun');
    const advMulti = $('advMulti');
    const advAutoSteps = $('advAutoSteps');
    const advCtxSize = $('advCtxSize');
    const advChatTemp = $('advChatTemp');
    const advChunkedSwitch = $('advChunkedSwitch');
    const advChunkedMax = $('advChunkedMax');
    const advPolishPersona = $('advPolishPersona');

    // ---- Google (Calendar + Sheets) ----
    const gClientId = $('gClientId');
    const gClientSecret = $('gClientSecret');
    const gCalendarId = $('gCalendarId');
    const gDefaultDur = $('gDefaultDur');
    const gIcalUrl = $('gIcalUrl');
    const gIcalDays = $('gIcalDays');
    const googleConnStatus = $('googleConnStatus');
    const googleConnStatusInline = $('googleConnStatusInline');
    const googleConnectBtn = $('googleConnect');
    const googleDisconnectBtn = $('googleDisconnect');
    const googleIcalRefreshBtn = $('googleIcalRefresh');
    const googleIcalStatus = $('googleIcalStatus');
    const googleFeedback = $('googleFeedback');
    const googleError = $('googleError');

    // ---- Devil Agent ----
    const devilEnabled = $('devilEnabled');

    // ---- Cloud LLM Providers ----
    const prOpenrouterEnabled = $('prOpenrouterEnabled');
    const prOpenrouterKey = $('prOpenrouterKey');
    const prOpenrouterDefault = $('prOpenrouterDefault');
    const prAnthropicEnabled = $('prAnthropicEnabled');
    const prAnthropicKey = $('prAnthropicKey');
    const prAnthropicDefault = $('prAnthropicDefault');
    const prGeminiEnabled = $('prGeminiEnabled');
    const prGeminiKey = $('prGeminiKey');
    const prGeminiDefault = $('prGeminiDefault');

    // ---- Banner ----
    const bannerError = $('bannerError');

    // ---- Telegram listeners ----
    saveBtn.addEventListener('click', () => {
        const t = (tokenInput.value || '').trim();
        if (!t) return;
        vscode.postMessage({ type: 'telegram.saveToken', token: t });
        tokenInput.value = '';
    });
    clearBtn.addEventListener('click', () => vscode.postMessage({ type: 'telegram.clearToken' }));
    testBtn.addEventListener('click', () => vscode.postMessage({ type: 'telegram.testConnection' }));
    enabledChk.addEventListener('change', (e) =>
        vscode.postMessage({ type: 'telegram.toggleEnabled', enabled: e.target.checked })
    );
    enrollBtn.addEventListener('click', () => vscode.postMessage({ type: 'telegram.enroll' }));
    enrollCancelBtn.addEventListener('click', () => vscode.postMessage({ type: 'telegram.cancelEnroll' }));
    chatList.addEventListener('click', (e) => {
        if (!(e.target instanceof HTMLElement)) return;
        if (e.target.classList.contains('remove')) {
            const id = Number(e.target.dataset.id);
            if (Number.isFinite(id)) vscode.postMessage({ type: 'telegram.removeChatId', chatId: id });
        }
    });

    // ---- Connection listeners ----
    document.querySelector('[data-save="connection.url"]').addEventListener('click', () =>
        vscode.postMessage({ type: 'connection.update', ollamaUrl: cnUrl.value })
    );
    document.querySelector('[data-save="connection.model"]').addEventListener('click', () =>
        vscode.postMessage({ type: 'connection.update', defaultModel: cnModel.value })
    );
    cnRefreshModels.addEventListener('click', () =>
        vscode.postMessage({ type: 'connection.update', refreshModels: true })
    );
    cnModel.addEventListener('change', () =>
        vscode.postMessage({ type: 'connection.update', defaultModel: cnModel.value })
    );
    document.querySelector('[data-save="connection.timeout"]').addEventListener('click', () =>
        vscode.postMessage({ type: 'connection.update', requestTimeout: Number(cnTimeout.value) })
    );

    // ---- Datacollect listeners ----
    document.querySelector('[data-save="datacollect.bridgeTarget"]').addEventListener('click', () =>
        vscode.postMessage({ type: 'datacollect.update', bridgeTarget: dcBridgeTarget.value })
    );
    document.querySelector('[data-save="datacollect.bridgeUrl"]').addEventListener('click', () =>
        vscode.postMessage({ type: 'datacollect.update', bridgeUrl: dcBridgeUrl.value })
    );
    document.querySelector('[data-save="datacollect.bridgeNasUrl"]').addEventListener('click', () =>
        vscode.postMessage({ type: 'datacollect.update', bridgeNasUrl: dcBridgeNasUrl.value })
    );
    document.querySelector('[data-save="datacollect.bridgeNasToken"]').addEventListener('click', () =>
        vscode.postMessage({ type: 'datacollect.update', bridgeNasToken: dcBridgeNasToken.value })
    );
    document.querySelector('[data-save="datacollect.savePath"]').addEventListener('click', () =>
        vscode.postMessage({ type: 'datacollect.update', savePath: dcSavePath.value })
    );
    document.querySelector('[data-save="datacollect.crawlDepth"]').addEventListener('click', () =>
        vscode.postMessage({ type: 'datacollect.update', crawlDepth: Number(dcCrawlDepth.value) })
    );
    document.querySelector('[data-save="datacollect.maxPages"]').addEventListener('click', () =>
        vscode.postMessage({ type: 'datacollect.update', maxPages: Number(dcMaxPages.value) })
    );
    document.querySelector('[data-save="datacollect.synthesisTemperature"]').addEventListener('click', () =>
        vscode.postMessage({ type: 'datacollect.update', synthesisTemperature: Number(dcSynthTemp.value) })
    );

    // ---- Memory listeners ----
    memEnabled.addEventListener('change', (e) =>
        vscode.postMessage({ type: 'memory.update', memoryEnabled: e.target.checked })
    );
    document.querySelector('[data-save="memory.short"]').addEventListener('click', () =>
        vscode.postMessage({ type: 'memory.update', memoryShortTermMessages: Number(memShort.value) })
    );
    document.querySelector('[data-save="memory.mid"]').addEventListener('click', () =>
        vscode.postMessage({ type: 'memory.update', memoryMediumTermSessions: Number(memMid.value) })
    );
    document.querySelector('[data-save="memory.long"]').addEventListener('click', () =>
        vscode.postMessage({ type: 'memory.update', memoryLongTermFiles: Number(memLong.value) })
    );

    // ---- Brain listeners ----
    brainAutoPush.addEventListener('change', (e) =>
        vscode.postMessage({ type: 'brain.update', autoPushBrain: e.target.checked })
    );
    $('brainOpenSettings').addEventListener('click', () =>
        vscode.postMessage({ type: 'openVscodeSettings' })
    );

    // ---- Advanced listeners ----
    advDryRun.addEventListener('change', (e) =>
        vscode.postMessage({ type: 'advanced.update', dryRun: e.target.checked })
    );
    advMulti.addEventListener('change', (e) =>
        vscode.postMessage({ type: 'advanced.update', multiAgentEnabled: e.target.checked })
    );
    document.querySelector('[data-save="advanced.autoSteps"]').addEventListener('click', () =>
        vscode.postMessage({ type: 'advanced.update', maxAutoSteps: Number(advAutoSteps.value) })
    );
    // ---- Google listeners ----
    document.querySelector('[data-save="google.clientId"]').addEventListener('click', () =>
        vscode.postMessage({ type: 'google.update', clientId: gClientId.value })
    );
    document.querySelector('[data-save="google.clientSecret"]').addEventListener('click', () => {
        // 저장 후 입력 필드는 비움 — 다음부터는 placeholder 가 "저장됨" 으로 표시됨.
        vscode.postMessage({ type: 'google.update', clientSecret: gClientSecret.value });
        gClientSecret.value = '';
    });
    document.querySelector('[data-save="google.calendarId"]').addEventListener('click', () =>
        vscode.postMessage({ type: 'google.update', calendarId: gCalendarId.value })
    );
    document.querySelector('[data-save="google.defaultEventDurationMinutes"]').addEventListener('click', () =>
        vscode.postMessage({ type: 'google.update', defaultEventDurationMinutes: Number(gDefaultDur.value) })
    );
    document.querySelector('[data-save="google.icalUrl"]').addEventListener('click', () => {
        vscode.postMessage({ type: 'google.update', icalUrl: gIcalUrl.value });
        gIcalUrl.value = '';
    });
    document.querySelector('[data-save="google.icalDaysAhead"]').addEventListener('click', () =>
        vscode.postMessage({ type: 'google.update', icalDaysAhead: Number(gIcalDays.value) })
    );
    googleConnectBtn.addEventListener('click', () => vscode.postMessage({ type: 'google.connect' }));
    googleDisconnectBtn.addEventListener('click', () => vscode.postMessage({ type: 'google.disconnect' }));
    googleIcalRefreshBtn.addEventListener('click', () => vscode.postMessage({ type: 'google.icalRefresh' }));

    // ---- Devil Agent listener ----
    devilEnabled.addEventListener('change', (e) =>
        vscode.postMessage({ type: 'devilAgent.toggle', enabled: e.target.checked })
    );

    // ---- Cloud LLM Providers listeners ----
    prOpenrouterEnabled.addEventListener('change', (e) =>
        vscode.postMessage({ type: 'providers.update', providerId: 'openrouter', enabled: e.target.checked })
    );
    document.querySelector('[data-save="providers.openrouter.apiKey"]').addEventListener('click', () => {
        vscode.postMessage({ type: 'providers.update', providerId: 'openrouter', apiKey: prOpenrouterKey.value });
        prOpenrouterKey.value = '';
    });
    document.querySelector('[data-save="providers.openrouter.defaultModel"]').addEventListener('click', () =>
        vscode.postMessage({ type: 'providers.update', providerId: 'openrouter', defaultModel: prOpenrouterDefault.value })
    );
    prAnthropicEnabled.addEventListener('change', (e) =>
        vscode.postMessage({ type: 'providers.update', providerId: 'anthropic', enabled: e.target.checked })
    );
    document.querySelector('[data-save="providers.anthropic.apiKey"]').addEventListener('click', () => {
        vscode.postMessage({ type: 'providers.update', providerId: 'anthropic', apiKey: prAnthropicKey.value });
        prAnthropicKey.value = '';
    });
    document.querySelector('[data-save="providers.anthropic.defaultModel"]').addEventListener('click', () =>
        vscode.postMessage({ type: 'providers.update', providerId: 'anthropic', defaultModel: prAnthropicDefault.value })
    );
    prGeminiEnabled.addEventListener('change', (e) =>
        vscode.postMessage({ type: 'providers.update', providerId: 'gemini', enabled: e.target.checked })
    );
    document.querySelector('[data-save="providers.gemini.apiKey"]').addEventListener('click', () => {
        vscode.postMessage({ type: 'providers.update', providerId: 'gemini', apiKey: prGeminiKey.value });
        prGeminiKey.value = '';
    });
    document.querySelector('[data-save="providers.gemini.defaultModel"]').addEventListener('click', () =>
        vscode.postMessage({ type: 'providers.update', providerId: 'gemini', defaultModel: prGeminiDefault.value })
    );

    document.querySelector('[data-save="advanced.ctxSize"]').addEventListener('click', () =>
        vscode.postMessage({ type: 'advanced.update', maxContextSize: Number(advCtxSize.value) })
    );

    document.querySelector('[data-save="advanced.chatTemperature"]').addEventListener('click', () =>
        vscode.postMessage({ type: 'advanced.update', chatTemperature: Number(advChatTemp.value) })
    );

    document.querySelector('[data-save="advanced.chunkedSwitchTokens"]').addEventListener('click', () =>
        vscode.postMessage({ type: 'advanced.update', chunkedSwitchTokens: Number(advChunkedSwitch.value) })
    );

    document.querySelector('[data-save="advanced.chunkedMaxSections"]').addEventListener('click', () =>
        vscode.postMessage({ type: 'advanced.update', chunkedMaxSections: Number(advChunkedMax.value) })
    );

    document.querySelector('[data-save="advanced.polishPersonaOverride"]').addEventListener('click', () =>
        vscode.postMessage({ type: 'advanced.update', polishPersonaOverride: String(advPolishPersona.value || '') })
    );

    // ---- Header ----
    $('openVscodeSettings').addEventListener('click', () =>
        vscode.postMessage({ type: 'openVscodeSettings' })
    );

    // ---- State sync ----
    window.addEventListener('message', (e) => {
        const msg = e.data;
        if (!msg || msg.type !== 'state') return;
        renderState(msg.value);
    });

    /** Set input.value only when the field is not currently focused, so user edits aren't clobbered mid-typing. */
    function setIfNotFocused(input, value) {
        if (document.activeElement === input) return;
        const next = value === undefined || value === null ? '' : String(value);
        if (input.value !== next) input.value = next;
    }

    function renderState(state) {
        // ---- Banner ----
        if (state.bannerError) {
            bannerError.hidden = false;
            bannerError.textContent = state.bannerError;
        } else {
            bannerError.hidden = true;
            bannerError.textContent = '';
        }

        // ---- Telegram ----
        const tg = state.telegram;
        tokenStatus.textContent = tg.hasToken ? '저장된 토큰이 있습니다.' : '아직 토큰이 등록되지 않았습니다.';
        clearBtn.disabled = !tg.hasToken;

        if (tg.botName) {
            botName.textContent = `연결됨: ${tg.botName}` + (tg.connected ? ' · 폴링 중' : ' · 비활성화 상태');
            botName.classList.toggle('ok', tg.connected);
        } else {
            botName.textContent = '';
            botName.classList.remove('ok');
        }

        enabledChk.checked = !!tg.enabled;
        enabledChk.disabled = !tg.hasToken;

        if (tg.enrolling) {
            enrollBtn.hidden = true;
            enrollCancelBtn.hidden = false;
            enrollStatus.textContent = '봇에게 메시지를 한 번 보내주세요. 다음 메시지의 채널이 자동 등록됩니다.';
        } else {
            enrollBtn.hidden = false;
            enrollCancelBtn.hidden = true;
            enrollStatus.textContent = '';
        }
        enrollBtn.disabled = !tg.hasToken;

        chatList.innerHTML = '';
        if (!tg.allowedChatIds || tg.allowedChatIds.length === 0) {
            const li = document.createElement('li');
            li.className = 'empty';
            li.textContent = '등록된 채널 없음';
            chatList.appendChild(li);
        } else {
            for (const id of tg.allowedChatIds) {
                const li = document.createElement('li');
                li.textContent = String(id);
                const x = document.createElement('span');
                x.className = 'remove';
                x.dataset.id = String(id);
                x.textContent = '✕';
                x.title = '허용 목록에서 제거';
                li.appendChild(x);
                chatList.appendChild(li);
            }
        }

        if (tg.lastSuccess) {
            tgFeedback.hidden = false;
            tgFeedback.textContent = tg.lastSuccess;
        } else {
            tgFeedback.hidden = true;
            tgFeedback.textContent = '';
        }
        if (tg.lastError) {
            tgError.hidden = false;
            tgError.textContent = tg.lastError;
        } else {
            tgError.hidden = true;
            tgError.textContent = '';
        }

        // ---- Connection ----
        const cn = state.connection;
        setIfNotFocused(cnUrl, cn.ollamaUrl);
        setIfNotFocused(cnTimeout, cn.requestTimeout);

        // Model dropdown — preserve selection, allow current value even if not in list
        const wantedModel = cn.defaultModel || '';
        const list = Array.isArray(cn.availableModels) ? cn.availableModels.slice() : [];
        if (wantedModel && !list.includes(wantedModel)) list.unshift(wantedModel);
        // Only repaint when list actually changed (otherwise we lose the user's
        // open dropdown state).
        const currentOptions = Array.from(cnModel.options).map((o) => o.value);
        const listChanged = currentOptions.length !== list.length
            || currentOptions.some((v, i) => v !== list[i]);
        if (listChanged) {
            cnModel.innerHTML = '';
            for (const m of list) {
                const opt = document.createElement('option');
                opt.value = m;
                opt.textContent = m;
                cnModel.appendChild(opt);
            }
            if (list.length === 0) {
                const opt = document.createElement('option');
                opt.value = '';
                opt.textContent = '(모델 없음 — 엔진 연결 확인)';
                opt.disabled = true;
                cnModel.appendChild(opt);
            }
        }
        cnModel.value = wantedModel;
        cnModelHint.textContent = cn.modelsLoading
            ? '모델 목록 가져오는 중…'
            : `사이드바에서 선택한 모델이 여기에도 동기화됩니다. (${list.length}개 발견)`;

        // ---- Datacollect ----
        const dc = state.datacollect;
        if (dc) {
            if (dcBridgeTarget && document.activeElement !== dcBridgeTarget && (dc.bridgeTarget === 'local' || dc.bridgeTarget === 'nas')) {
                dcBridgeTarget.value = dc.bridgeTarget;
            }
            setIfNotFocused(dcBridgeUrl, dc.bridgeUrl);
            setIfNotFocused(dcBridgeNasUrl, dc.bridgeNasUrl);
            setIfNotFocused(dcBridgeNasToken, dc.bridgeNasToken);
            setIfNotFocused(dcSavePath, dc.savePath);
            setIfNotFocused(dcCrawlDepth, dc.crawlDepth);
            setIfNotFocused(dcMaxPages, dc.maxPages);
            setIfNotFocused(dcSynthTemp, dc.synthesisTemperature);
        }

        // ---- Memory ----
        const mem = state.memory;
        memEnabled.checked = !!mem.memoryEnabled;
        setIfNotFocused(memShort, mem.memoryShortTermMessages);
        setIfNotFocused(memMid, mem.memoryMediumTermSessions);
        setIfNotFocused(memLong, mem.memoryLongTermFiles);

        // ---- Brain ----
        const br = state.brain;
        brainName.textContent = br.activeBrainName + (br.profileCount > 0 ? ` (전체 ${br.profileCount}개)` : '');
        brainPath.textContent = br.activeBrainPath || '경로 없음';
        brainAutoPush.checked = !!br.autoPushBrain;

        // ---- Advanced ----
        const adv = state.advanced;
        advDryRun.checked = !!adv.dryRun;
        advMulti.checked = !!adv.multiAgentEnabled;
        setIfNotFocused(advAutoSteps, adv.maxAutoSteps);
        setIfNotFocused(advCtxSize, adv.maxContextSize);
        setIfNotFocused(advChatTemp, adv.chatTemperature);
        setIfNotFocused(advChunkedSwitch, adv.chunkedSwitchTokens);
        setIfNotFocused(advChunkedMax, adv.chunkedMaxSections);
        setIfNotFocused(advPolishPersona, adv.polishPersonaOverride);

        // ---- Google (Calendar + Sheets) ----
        const g = state.google;
        if (g) {
            setIfNotFocused(gClientId, g.clientId);
            // Secret 은 값 자체를 화면에 안 그림 — 설정 여부만 placeholder 로 표현.
            gClientSecret.placeholder = g.hasClientSecret ? '••• 저장됨 (덮어쓰려면 새 값 입력)' : 'GOCSPX-...';
            setIfNotFocused(gCalendarId, g.calendarId);
            setIfNotFocused(gDefaultDur, g.defaultEventDurationMinutes);
            gIcalUrl.placeholder = g.hasIcalUrl ? '••• 저장됨 (덮어쓰려면 새 URL 입력)' : 'https://calendar.google.com/calendar/ical/.../basic.ics';
            setIfNotFocused(gIcalDays, g.icalDaysAhead);
            // 연결 상태 readout.
            if (g.connected) {
                const at = g.connectedAt ? g.connectedAt.slice(0, 16).replace('T', ' ') : '';
                googleConnStatus.textContent = `✅ 연결됨${g.connectedAs ? ' · ' + g.connectedAs : ''}${at ? ' (' + at + ')' : ''}`;
                googleConnStatus.style.color = '#10b981';
                googleDisconnectBtn.disabled = false;
            } else {
                googleConnStatus.textContent = '⛔ OAuth 연결 안됨 — Client ID/Secret 저장 후 [OAuth 연결] 클릭';
                googleConnStatus.style.color = '';
                googleDisconnectBtn.disabled = true;
            }
            googleIcalStatus.textContent = g.lastIcalFetchAt
                ? `마지막 새로고침: ${g.lastIcalFetchAt.slice(0, 16).replace('T', ' ')}`
                : '';
        }

        // ---- Devil Agent ----
        if (state.devilAgent) {
            devilEnabled.checked = !!state.devilAgent.enabled;
        }

        // ---- Cloud LLM Providers ----
        const pr = state.providers;
        if (pr) {
            // OpenRouter
            prOpenrouterEnabled.checked = !!pr.openrouter.enabled;
            prOpenrouterKey.placeholder = pr.openrouter.hasApiKey ? '••• 저장됨 (덮어쓰려면 새 값)' : 'sk-or-...';
            setIfNotFocused(prOpenrouterDefault, pr.openrouter.defaultModel);
            // Anthropic
            prAnthropicEnabled.checked = !!pr.anthropic.enabled;
            prAnthropicKey.placeholder = pr.anthropic.hasApiKey ? '••• 저장됨 (덮어쓰려면 새 값)' : 'sk-ant-...';
            setIfNotFocused(prAnthropicDefault, pr.anthropic.defaultModel);
            // Gemini
            prGeminiEnabled.checked = !!pr.gemini.enabled;
            prGeminiKey.placeholder = pr.gemini.hasApiKey ? '••• 저장됨 (덮어쓰려면 새 값)' : 'AIzaSy...';
            setIfNotFocused(prGeminiDefault, pr.gemini.defaultModel);
        }
    }

    // ---- Tabs (카테고리 네비) — 표현 계층만. 기존 refs/리스너/state 에 무영향 ----
    (function initTabs() {
        const tabButtons = Array.from(document.querySelectorAll('.tab'));
        const sections = Array.from(document.querySelectorAll('.section'));
        if (tabButtons.length === 0) return;
        const valid = new Set(tabButtons.map((b) => b.dataset.tab));
        function setActiveTab(tab) {
            tabButtons.forEach((b) => b.classList.toggle('active', b.dataset.tab === tab));
            // 숨김만(hidden) — DOM 제거 X. renderState 는 숨겨진 입력에도 값을 채운다.
            sections.forEach((s) => { s.hidden = s.dataset.tab !== tab; });
            try { const st = vscode.getState() || {}; vscode.setState({ ...st, activeTab: tab }); } catch (e) { /* noop */ }
        }
        tabButtons.forEach((b) => b.addEventListener('click', () => setActiveTab(b.dataset.tab)));
        let initial = 'model';
        try { const saved = (vscode.getState() || {}).activeTab; if (saved && valid.has(saved)) initial = saved; } catch (e) { /* noop */ }
        if (!valid.has(initial)) initial = tabButtons[0].dataset.tab;
        setActiveTab(initial);
    })();

    vscode.postMessage({ type: 'ready' });
})();
