#!/usr/bin/env node

const fs = require('fs');
const os = require('os');
const path = require('path');

const HOME = os.homedir();
const ROOT = process.env.RESEARCHPAD_PROJECT_DIR
  ? path.resolve(process.env.RESEARCHPAD_PROJECT_DIR)
  : path.resolve(__dirname, '..');
const WEB_DIR = path.join(HOME, '.researchpad-agent-web');
const USER_DATA_DIR = path.join(WEB_DIR, 'User');
const CODE_SERVER_DIR = path.join(HOME, '.researchpad-agent', 'code-server');
const ROO_IMPORT_FILE = path.join(WEB_DIR, 'roo-code-settings.json');

const COPILOT_IDS = new Set(['github.copilot', 'github.copilot-chat']);
const ROO_ACCESS_KEY_PROFILE_ID = 'researchpad-accesskey';
const ROO_BUILTIN_MODES = ['architect', 'code', 'ask', 'debug', 'orchestrator'];

function buildRooModeApiConfigs(configId) {
  return Object.fromEntries(ROO_BUILTIN_MODES.map((mode) => [mode, configId]));
}

function readJson(file, fallback = {}) {
  if (!fs.existsSync(file)) return fallback;
  try {
    return JSON.parse(fs.readFileSync(file, 'utf8'));
  } catch (error) {
    console.warn(`[runtime-fixes] Skipping invalid JSON: ${file} (${error.message})`);
    return fallback;
  }
}

function writeJson(file, value) {
  fs.mkdirSync(path.dirname(file), { recursive: true });
  fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`);
}

function deepMerge(...values) {
  const output = {};

  for (const value of values) {
    if (!value || typeof value !== 'object' || Array.isArray(value)) continue;

    for (const [key, next] of Object.entries(value)) {
      const prev = output[key];
      if (
        prev &&
        next &&
        typeof prev === 'object' &&
        typeof next === 'object' &&
        !Array.isArray(prev) &&
        !Array.isArray(next)
      ) {
        output[key] = deepMerge(prev, next);
      } else {
        output[key] = next;
      }
    }
  }

  return output;
}

function removeIfExists(file) {
  if (fs.existsSync(file)) fs.rmSync(file, { recursive: true, force: true });
}

function readText(file) {
  if (!fs.existsSync(file)) return '';
  try {
    return fs.readFileSync(file, 'utf8').trim();
  } catch {
    return '';
  }
}

function firstString(...values) {
  for (const value of values) {
    if (typeof value === 'string' && value.trim()) return value.trim();
  }
  return '';
}

function getRooLatestAnnouncementId() {
  const extensionDir = findRooExtensionDir();
  if (!extensionDir) return '';

  const extensionJs = path.join(extensionDir, 'dist', 'extension.js');
  const source = readText(extensionJs);
  return source.match(/latestAnnouncementId="([^"]+)"/)?.[1] || '';
}

function buildRooImportSettings(settings) {
  // 优先从 AccessKey 持久化文件读取
  const accessKeyFile = path.join(HOME, '.researchpad-agent', 'access-key.json');
  let accessKey = '';
  try {
    if (fs.existsSync(accessKeyFile)) {
      const data = JSON.parse(fs.readFileSync(accessKeyFile, 'utf8'));
      accessKey = data.accessKey || '';
    }
  } catch {}

  // 向后兼容: 如果没有 AccessKey，尝试旧的 DeepSeek 直连方式
  if (!accessKey) {
    accessKey = firstString(
      settings['roo-cline.deepSeekApiKey'],
      process.env.DEEPSEEK_API_KEY,
      readText(path.join(ROOT, 'deepseek API key.txt')),
    );
  }

  if (!accessKey) return null;

  const isAccessKey = accessKey.length === 23; // AccessKey 是 23 位
  const profileName = isAccessKey ? 'ResearchPad DeepSeek (AccessKey)' : 'ResearchPad DeepSeek';

  let apiConfig;
  const profileId = isAccessKey ? ROO_ACCESS_KEY_PROFILE_ID : 'researchpad-deepseek';
  if (isAccessKey) {
    // AccessKey 模式: 通过本地代理 -> api.co-scientist.cc -> DeepSeek
    apiConfig = {
      id: profileId,
      apiProvider: 'openai',
      openAiModelId: 'deepseek-v3.2',
      openAiBaseUrl: 'https://local.researchpad.org:8443/api/v1',
      openAiApiKey: accessKey,
    };
  } else {
    // 旧模式: DeepSeek 直连（向后兼容）
    const model = firstString(settings['roo-cline.deepSeekModelId'], 'deepseek-chat');
    const baseUrl = firstString(settings['roo-cline.deepSeekBaseUrl']);
    apiConfig = {
      id: profileId,
      apiProvider: 'deepseek',
      apiModelId: model,
      deepSeekApiKey: accessKey,
    };
    if (baseUrl) apiConfig.deepSeekBaseUrl = baseUrl;
  }

  const globalSettings = {
    telemetrySetting: 'disabled',
  };
  const latestAnnouncementId = getRooLatestAnnouncementId();
  if (latestAnnouncementId) globalSettings.lastShownAnnouncementId = latestAnnouncementId;

  return {
    providerProfiles: {
      currentApiConfigName: profileName,
      apiConfigs: {
        [profileName]: apiConfig,
      },
      modeApiConfigs: buildRooModeApiConfigs(profileId),
    },
    globalSettings,
  };
}

function patchRuntimeSettings() {
  const repoSettings = readJson(path.join(ROOT, 'build', 'defaults', 'settings.json'));
  const rooSettings = readJson(path.join(ROOT, 'build', 'defaults', 'roo-code-settings.json'));
  const actualSettingsPath = path.join(USER_DATA_DIR, 'settings.json');
  const legacySettingsPath = path.join(USER_DATA_DIR, 'User', 'settings.json');
  const legacySettings = readJson(legacySettingsPath);
  const actualSettings = readJson(actualSettingsPath);

  const enforcedSettings = {
    'workbench.startupEditor': 'none',
    'workbench.editor.enablePreview': false,
    'workbench.layoutControl.enabled': false,
    'workbench.secondarySideBar.defaultVisibility': 'hidden',
    'security.workspace.trust.enabled': false,
    'editor.minimap.enabled': false,
    'files.autoSave': 'afterDelay',
    'files.autoSaveDelay': 1000,
    'telemetry.telemetryLevel': 'off',
    'telemetry.feedback.enabled': false,
    'update.mode': 'none',
    'extensions.autoUpdate': false,
    'extensions.autoCheckUpdates': false,
    'extensions.ignoreRecommendations': true,
    'chat.agent.enabled': false,
    'chat.commandCenter.enabled': false,
    'chat.setup.state': 'hidden',
    'github.copilot.enable': {
      '*': false,
    },
    'github.copilot.chat.enabled': false,
    'github.gitAuthentication': false,
    'git.terminalAuthentication': false,
  };

  const mergedSettings = deepMerge(repoSettings, rooSettings, legacySettings, actualSettings, enforcedSettings);
  const rooImportSettings = buildRooImportSettings(mergedSettings);

  if (rooImportSettings) {
    const profileName = rooImportSettings.providerProfiles.currentApiConfigName;
    const profile = rooImportSettings.providerProfiles.apiConfigs[profileName];

    if (profile.apiProvider === 'openai') {
      // AccessKey 模式
      mergedSettings['roo-cline.apiProvider'] = 'openai';
      mergedSettings['roo-cline.openAiBaseUrl'] = profile.openAiBaseUrl;
      mergedSettings['roo-cline.openAiApiKey'] = profile.openAiApiKey;
      mergedSettings['roo-cline.openAiModelId'] = profile.openAiModelId;
      // 清除旧的 DeepSeek 配置
      delete mergedSettings['roo-cline.deepSeekApiKey'];
      delete mergedSettings['roo-cline.deepSeekBaseUrl'];
      delete mergedSettings['roo-cline.deepSeekModelId'];
    } else {
      // 旧模式: DeepSeek 直连
      mergedSettings['roo-cline.apiProvider'] = 'deepseek';
      mergedSettings['roo-cline.deepSeekApiKey'] = profile.deepSeekApiKey;
      mergedSettings['roo-cline.deepSeekModelId'] = profile.apiModelId;
    }

    mergedSettings['roo-cline.autoImportSettingsPath'] = ROO_IMPORT_FILE;
    writeJson(ROO_IMPORT_FILE, rooImportSettings);
  }

  writeJson(actualSettingsPath, mergedSettings);
  writeJson(legacySettingsPath, mergedSettings);
}

function patchProductJson() {
  const productFile = path.join(CODE_SERVER_DIR, 'lib', 'vscode', 'product.json');
  if (!fs.existsSync(productFile)) return;

  const product = readJson(productFile);

  if (Array.isArray(product.trustedExtensionAuthAccess)) {
    product.trustedExtensionAuthAccess = product.trustedExtensionAuthAccess.filter(
      (id) => !COPILOT_IDS.has(String(id).toLowerCase()),
    );
  }

  if (Array.isArray(product.builtInExtensionsEnabledWithAutoUpdates)) {
    product.builtInExtensionsEnabledWithAutoUpdates =
      product.builtInExtensionsEnabledWithAutoUpdates.filter(
        (id) => !String(id).toLowerCase().includes('copilot'),
      );
  }

  product.enableTelemetry = false;
  writeJson(productFile, product);
}

function disableBundledExtension(name) {
  const extensionsDir = path.join(CODE_SERVER_DIR, 'lib', 'vscode', 'extensions');
  const activeDir = path.join(extensionsDir, name);
  const legacyDisabledDir = path.join(extensionsDir, `${name}.disabled`);
  const quarantineDir = path.join(CODE_SERVER_DIR, 'lib', 'vscode', 'extensions.disabled');
  const targetDir = path.join(quarantineDir, name);

  for (const sourceDir of [activeDir, legacyDisabledDir]) {
    if (!fs.existsSync(sourceDir)) continue;

    fs.mkdirSync(quarantineDir, { recursive: true });
    if (fs.existsSync(targetDir)) fs.rmSync(targetDir, { recursive: true, force: true });
    fs.renameSync(sourceDir, targetDir);
  }
}

function findRooExtensionDir() {
  const extensionsDir = path.join(WEB_DIR, 'extensions');
  if (!fs.existsSync(extensionsDir)) return null;

  const candidates = fs
    .readdirSync(extensionsDir)
    .filter((entry) => entry.startsWith('rooveterinaryinc.roo-cline-'))
    .sort();

  if (!candidates.length) return null;
  return path.join(extensionsDir, candidates[candidates.length - 1]);
}

function copyLogo(source, target) {
  fs.mkdirSync(path.dirname(target), { recursive: true });
  fs.copyFileSync(source, target);
}

function patchRooExtension() {
  const extensionDir = findRooExtensionDir();
  if (!extensionDir) return;

  const logoCandidates = [
    path.join(ROOT, 'logo-noBG.png'),
    path.join(ROOT, 'extensions', 'researchpad-agent-extension', 'media', 'logo.png'),
    path.join(ROOT, 'build', 'branding', 'icon-128.png'),
  ];
  const logo = logoCandidates.find((candidate) => fs.existsSync(candidate));

  if (logo) {
    for (const target of [
      path.join(extensionDir, 'assets', 'icons', 'rp-bird.png'),
      path.join(extensionDir, 'assets', 'icons', 'icon.png'),
      path.join(extensionDir, 'assets', 'icons', 'panel_dark.png'),
      path.join(extensionDir, 'assets', 'icons', 'panel_light.png'),
      path.join(extensionDir, 'assets', 'images', 'roo.png'),
    ]) {
      copyLogo(logo, target);
    }

    const logoSvg = [
      '<svg xmlns="http://www.w3.org/2000/svg" width="120" height="120" viewBox="0 0 120 120">',
      '  <image href="../icons/rp-bird.png" width="120" height="120" preserveAspectRatio="xMidYMid meet"/>',
      '</svg>',
      '',
    ].join('\n');
    fs.writeFileSync(path.join(extensionDir, 'assets', 'images', 'roo-logo.svg'), logoSvg);
  }

  const packageFile = path.join(extensionDir, 'package.json');
  const manifest = readJson(packageFile);

  manifest.icon = 'assets/icons/rp-bird.png';
  const activityBar = manifest.contributes?.viewsContainers?.activitybar;
  if (Array.isArray(activityBar) && activityBar[0]) {
    activityBar[0].icon = 'assets/icons/rp-bird.png';
  }

  const menus = manifest.contributes?.menus;
  if (menus && typeof menus === 'object') {
    for (const [menu, entries] of Object.entries(menus)) {
      if (Array.isArray(entries)) {
        menus[menu] = entries.filter((entry) => entry.command !== 'roo-cline.cloudButtonClicked');
      }
    }
  }

  writeJson(packageFile, manifest);

  const webviewAssetDir = path.join(extensionDir, 'webview-ui', 'build', 'assets');
  if (fs.existsSync(webviewAssetDir)) {
    for (const file of fs.readdirSync(webviewAssetDir)) {
      if (!file.endsWith('.js')) continue;

      const assetFile = path.join(webviewAssetDir, file);
      const original = fs.readFileSync(assetFile, 'utf8');
      let patched = original.replaceAll('cloudButtonClicked:"cloud"', 'cloudButtonClicked:"chat"');
      patched = patched.replace(
        'if(u===null)b("landing"),xe.postMessage({type:"rooCloudSignIn",useProviderSignup:!0}),h(!0);else if(u==="roo")',
        'if(u===null){p("custom");return}else if(u==="roo")',
      );
      patched = patched.replace(
        'else b("providerSelection"),xe.postMessage({type:"rooCloudSignIn",useProviderSignup:!0}),h(!0);else{',
        'else p("custom");else{',
      );
      if (patched !== original) fs.writeFileSync(assetFile, patched);
    }
  }

  const extensionJs = path.join(extensionDir, 'dist', 'extension.js');
  if (fs.existsSync(extensionJs)) {
    const original = fs.readFileSync(extensionJs, 'utf8');
    let patched = original;

    const reuseTabNeedle = 's7e=async({context:t,outputChannel:e})=>{let r=await v1.getInstance(t),';
    const reuseTabPatch =
      's7e=async({context:t,outputChannel:e})=>{if(Pge){try{return Pge.reveal(UC.ViewColumn.Two,!1),Pge}catch{}}let r=await v1.getInstance(t),';

    if (!patched.includes('Pge.reveal(UC.ViewColumn.Two,!1),Pge')) {
      if (patched.includes(reuseTabNeedle)) {
        patched = patched.replace(reuseTabNeedle, reuseTabPatch);
      } else {
        console.warn('[runtime-fixes] Roo tab panel patch target not found.');
      }
    }

    const autoOpenNeedle = 'FMt({context:t,outputChannel:e0,provider:u});let h=new class{provideTextDocumentContent';
    const autoOpenPatch =
      'FMt({context:t,outputChannel:e0,provider:u});try{setTimeout(async()=>{try{await Ed.commands.executeCommand("workbench.action.closeAuxiliaryBar");if(!Pge)await s7e({context:t,outputChannel:e0});await Ed.commands.executeCommand("workbench.view.explorer")}catch(l){e0.appendLine(`[ResearchPad] Failed to open right ResearchPad panel: ${l instanceof Error?l.message:String(l)}`)}},1200)}catch(l){e0.appendLine(`[ResearchPad] Failed to schedule right ResearchPad panel: ${l instanceof Error?l.message:String(l)}`)}let h=new class{provideTextDocumentContent';

    if (!patched.includes('[ResearchPad] Failed to open right ResearchPad panel')) {
      if (patched.includes(autoOpenNeedle)) {
        patched = patched.replace(autoOpenNeedle, autoOpenPatch);
      } else {
        console.warn('[runtime-fixes] Roo auto-open patch target not found.');
      }
    }

    if (patched !== original) fs.writeFileSync(extensionJs, patched);
  }
}

function clearCaches() {
  for (const target of [
    path.join(USER_DATA_DIR, 'CachedProfilesData', '__default__profile__', 'extensions.user.cache'),
    path.join(USER_DATA_DIR, 'CachedProfilesData', '__default__profile__', 'extensions.builtin.cache'),
    path.join(USER_DATA_DIR, 'User', 'customBuiltinExtensionsCache.json'),
    path.join(USER_DATA_DIR, 'User', 'systemExtensionsCache.json'),
    path.join(USER_DATA_DIR, 'User', 'globalStorage', 'emptyWindowChatSessions'),
  ]) {
    removeIfExists(target);
  }
}

patchRuntimeSettings();
patchProductJson();
for (const name of ['copilot', 'github-authentication', 'microsoft-authentication', 'mermaid-chat-features']) {
  disableBundledExtension(name);
}
patchRooExtension();
clearCaches();
