/**
 * AG Auto Click & Scroll — Injector Module (Node.js)
 * 
 * Handles injecting/uninstalling the auto-click-scroll script
 * into Antigravity's workbench.html, updating checksums, 
 * and managing config files.
 */

const fs = require('fs');
const path = require('path');
const os = require('os');
const crypto = require('crypto');
const { execSync } = require('child_process');

const TAG_START = '<!-- AG-AUTO-CLICK-SCROLL-START -->';
const TAG_END = '<!-- AG-AUTO-CLICK-SCROLL-END -->';
const SCRIPT_FILENAME = 'ag-auto-script.js';
const CONFIG_FILENAME = 'ag-auto-config.json';

// Default config
const DEFAULT_CONFIG = {
  enabled: true,
  scrollEnabled: true,
  clickPatterns: ['Run', 'Allow', 'Always Allow', 'Keep Waiting', 'Accept', 'Retry', 'Continue', 'Allow Once', 'Allow This Conversation'],
  acceptInChatOnly: true,
  pauseScrollMs: 7000,
  scrollIntervalMs: 500,
  clickIntervalMs: 1000
};

/**
 * Find Antigravity installation path (appRoot equivalent)
 */
function findAntigravityPath() {
  const home = os.homedir();
  const platform = process.platform;

  let candidates = [];

  if (platform === 'win32') {
    const localAppData = process.env.LOCALAPPDATA || path.join(home, 'AppData', 'Local');
    const programFiles = process.env.ProgramFiles || 'C:\\Program Files';
    candidates = [
      path.join(localAppData, 'Programs', 'Antigravity', 'resources', 'app'),
      path.join(programFiles, 'Antigravity', 'resources', 'app'),
      path.join(localAppData, 'Antigravity', 'resources', 'app'),
    ];
  } else if (platform === 'darwin') {
    candidates = [
      '/Applications/Antigravity.app/Contents/Resources/app',
      path.join(home, 'Applications', 'Antigravity.app', 'Contents', 'Resources', 'app'),
    ];
  } else {
    candidates = [
      '/usr/share/antigravity/resources/app',
      '/opt/Antigravity/resources/app',
      '/usr/lib/antigravity/resources/app',
      path.join(home, '.local', 'share', 'antigravity', 'resources', 'app'),
    ];
  }

  for (const c of candidates) {
    if (fs.existsSync(c)) return c;
  }

  return null;
}

/**
 * Find workbench.html inside appRoot
 */
function findWorkbenchHtml(appRoot) {
  if (!appRoot) return null;

  const candidates = [
    path.join(appRoot, 'out', 'vs', 'code', 'electron-browser', 'workbench', 'workbench.html'),
    path.join(appRoot, 'out', 'vs', 'code', 'electron-sandbox', 'workbench', 'workbench.html'),
    path.join(appRoot, 'out', 'vs', 'workbench', 'workbench.html'),
    path.join(appRoot, 'out', 'vs', 'code', 'browser', 'workbench', 'workbench.html'),
    path.join(appRoot, 'out', 'vs', 'code', 'electron-main', 'workbench', 'workbench.html'),
  ];

  for (const p of candidates) {
    if (fs.existsSync(p)) return p;
  }

  // Fallback: recursive search
  const outDir = path.join(appRoot, 'out');
  if (fs.existsSync(outDir)) {
    return findFileRecursive(outDir, 'workbench.html', 6);
  }

  return null;
}

/**
 * Recursive file search with depth limit
 */
function findFileRecursive(dir, filename, maxDepth) {
  if (maxDepth <= 0) return null;
  try {
    const entries = fs.readdirSync(dir, { withFileTypes: true });
    for (const entry of entries) {
      const fullPath = path.join(dir, entry.name);
      if (entry.isFile() && entry.name === filename) return fullPath;
      if (entry.isDirectory()) {
        const result = findFileRecursive(fullPath, filename, maxDepth - 1);
        if (result) return result;
      }
    }
  } catch (_) { }
  return null;
}

/**
 * Write file with elevated permissions on Linux/macOS
 */
function writeFileElevated(filePath, content) {
  try {
    fs.writeFileSync(filePath, content, 'utf8');
  } catch (err) {
    if (err.code !== 'EACCES' && err.code !== 'EPERM') throw err;

    const tmpPath = path.join(os.tmpdir(), 'ag-auto-' + Date.now() + '.tmp');
    fs.writeFileSync(tmpPath, content, 'utf8');

    try {
      if (process.platform === 'linux') {
        execSync(`pkexec bash -c "cp '${tmpPath}' '${filePath}' && chmod 644 '${filePath}'"`, { timeout: 30000 });
      } else if (process.platform === 'darwin') {
        const cmd = `cp '${tmpPath}' '${filePath}' && chmod 644 '${filePath}'`;
        execSync(`osascript -e 'do shell script "${cmd}" with administrator privileges'`, { timeout: 30000 });
      } else {
        throw err; // Windows: need admin
      }
    } catch (elevErr) {
      try { fs.unlinkSync(tmpPath); } catch (_) { }
      if (elevErr === err) throw err;
      throw new Error(`Permission denied. Hãy chạy toolkit với quyền Administrator.`);
    }
    try { fs.unlinkSync(tmpPath); } catch (_) { }
  }
}

function escapeRegex(str) {
  return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

/**
 * Detect if the AG Auto extension is installed in Antigravity
 */
function detectExtension() {
  const home = os.homedir();
  const extensionsDirs = [];

  if (process.platform === 'win32') {
    extensionsDirs.push(path.join(home, '.antigravity', 'extensions'));
    extensionsDirs.push(path.join(home, '.vscode', 'extensions'));
  } else if (process.platform === 'darwin') {
    extensionsDirs.push(path.join(home, '.antigravity', 'extensions'));
    extensionsDirs.push(path.join(home, '.vscode', 'extensions'));
  } else {
    extensionsDirs.push(path.join(home, '.antigravity', 'extensions'));
    extensionsDirs.push(path.join(home, '.vscode', 'extensions'));
  }

  for (const extDir of extensionsDirs) {
    if (!fs.existsSync(extDir)) continue;
    try {
      const entries = fs.readdirSync(extDir);
      const agAutoExt = entries.find(e => 
        e.toLowerCase().includes('ag-auto') || 
        e.toLowerCase().includes('ag-auto-click') ||
        e.toLowerCase().includes('zixfel.ag-auto')
      );
      if (agAutoExt) {
        return {
          found: true,
          path: path.join(extDir, agAutoExt),
          name: agAutoExt
        };
      }
    } catch (_) { }
  }

  return { found: false };
}

/**
 * Check if workbench.html has the OLD extension's injection (not ours)
 */
function hasExtensionInjection(html) {
  // Extension uses different tags or direct script tags
  if (html.includes('<!-- AG-AUTO-SCRIPT-START -->')) return true;
  // Check for extension-injected script that's NOT our toolkit's tag
  if (html.match(/<script[^>]*ag-auto-script[^>]*>/i) && !html.includes(TAG_START)) return true;
  return false;
}

/**
 * Get current AG Auto status
 */
function getStatus() {
  const appRoot = findAntigravityPath();
  if (!appRoot) {
    return {
      found: false,
      injected: false,
      extensionInstalled: false,
      error: 'Không tìm thấy Antigravity. Kiểm tra cài đặt.',
      config: DEFAULT_CONFIG
    };
  }

  const wbPath = findWorkbenchHtml(appRoot);
  if (!wbPath) {
    return {
      found: true,
      appRoot,
      injected: false,
      extensionInstalled: false,
      error: 'Tìm được Antigravity nhưng không tìm thấy workbench.html',
      config: DEFAULT_CONFIG
    };
  }

  const wbDir = path.dirname(wbPath);
  const html = fs.readFileSync(wbPath, 'utf8');
  const injected = html.includes(TAG_START);

  // Detect extension
  const ext = detectExtension();
  const extInjectionInHtml = hasExtensionInjection(html);

  // Read config if exists
  let config = { ...DEFAULT_CONFIG };
  const configPath = path.join(wbDir, CONFIG_FILENAME);
  if (fs.existsSync(configPath)) {
    try {
      config = { ...DEFAULT_CONFIG, ...JSON.parse(fs.readFileSync(configPath, 'utf8')) };
    } catch (_) { }
  }

  return {
    found: true,
    appRoot,
    workbenchPath: wbPath,
    injected,
    extensionInstalled: ext.found,
    extensionPath: ext.found ? ext.path : null,
    extensionName: ext.found ? ext.name : null,
    extensionInjected: extInjectionInHtml,
    config
  };
}

/**
 * Build final script content with config path injected
 */
function buildScriptContent(wbDir) {
  const templatePath = path.join(__dirname, 'auto-script.js');
  let script = fs.readFileSync(templatePath, 'utf8');

  // Inject config file path (use forward slashes for Electron)
  const configFilePath = path.join(wbDir, CONFIG_FILENAME).replace(/\\/g, '/');
  script = script.replace("'/*{{CONFIG_PATH}}*/'", `'${configFilePath}'`);

  return script;
}

/**
 * Update checksums in product.json after modifying workbench files
 */
function updateProductChecksums(appRoot) {
  try {
    const productJsonPath = path.join(appRoot, 'product.json');
    if (!fs.existsSync(productJsonPath)) return false;

    const productJson = JSON.parse(fs.readFileSync(productJsonPath, 'utf8'));
    if (!productJson.checksums) return false;

    const outDir = path.join(appRoot, 'out');
    let updated = false;

    for (const relativePath in productJson.checksums) {
      const nativePath = relativePath.split('/').join(path.sep);
      let filePath = path.join(outDir, nativePath);
      if (!fs.existsSync(filePath)) filePath = path.join(appRoot, nativePath);
      if (fs.existsSync(filePath)) {
        const content = fs.readFileSync(filePath);
        const hash = crypto.createHash('sha256').update(content).digest('base64').replace(/=+$/, '');
        if (productJson.checksums[relativePath] !== hash) {
          productJson.checksums[relativePath] = hash;
          updated = true;
        }
      }
    }

    if (updated) {
      writeFileElevated(productJsonPath, JSON.stringify(productJson, null, '\t'));
    }
    return updated;
  } catch (e) {
    console.error('[AG Auto] Error updating checksums:', e.message);
    return false;
  }
}

/**
 * Clear V8 bytecode cache
 */
function clearV8CodeCache() {
  try {
    let codeCacheDir;
    if (process.platform === 'win32') {
      const appData = process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming');
      codeCacheDir = path.join(appData, 'Antigravity', 'Code Cache', 'js');
    } else if (process.platform === 'darwin') {
      codeCacheDir = path.join(os.homedir(), 'Library', 'Application Support', 'Antigravity', 'Code Cache', 'js');
    } else {
      codeCacheDir = path.join(os.homedir(), '.config', 'Antigravity', 'Code Cache', 'js');
    }

    if (fs.existsSync(codeCacheDir)) {
      fs.rmSync(codeCacheDir, { recursive: true, force: true });
      return true;
    }
  } catch (e) {
    console.error('[AG Auto] Could not clear code cache:', e.message);
  }
  return false;
}

/**
 * Inject script into Antigravity workbench
 */
function inject(config) {
  const appRoot = findAntigravityPath();
  if (!appRoot) {
    return { success: false, error: 'Không tìm thấy Antigravity. Kiểm tra cài đặt.' };
  }

  const wbPath = findWorkbenchHtml(appRoot);
  if (!wbPath) {
    return { success: false, error: 'Không tìm thấy workbench.html trong Antigravity.' };
  }

  const wbDir = path.dirname(wbPath);
  const mergedConfig = { ...DEFAULT_CONFIG, ...config };

  try {
    // Step 1: Write config JSON
    const configPath = path.join(wbDir, CONFIG_FILENAME);
    writeFileElevated(configPath, JSON.stringify(mergedConfig));

    // Step 2: Write auto-script.js
    const scriptContent = buildScriptContent(wbDir);
    const scriptPath = path.join(wbDir, SCRIPT_FILENAME);
    writeFileElevated(scriptPath, scriptContent);

    // Step 3: Inject <script> tag into workbench.html
    let html = fs.readFileSync(wbPath, 'utf8');

    // Remove existing toolkit injection
    const htmlRegex = new RegExp(`${escapeRegex(TAG_START)}[\\s\\S]*?${escapeRegex(TAG_END)}`, 'g');
    html = html.replace(htmlRegex, '');

    // Also remove old AG Auto extension injection (avoid conflict)
    html = html.replace(/<!-- AG-AUTO-SCRIPT-START -->[\s\S]*?<!-- AG-AUTO-SCRIPT-END -->/g, '');
    html = html.replace(/<script[^>]*ag-auto-script[^>]*><\/script>/g, '');

    const ts = Date.now();
    const injection = `\n${TAG_START}\n<script src="${SCRIPT_FILENAME}?v=${ts}"></script>\n${TAG_END}`;
    html = html.replace('</html>', injection + '\n</html>');

    writeFileElevated(wbPath, html);

    // Step 4: Update checksums
    updateProductChecksums(appRoot);

    // Step 5: Clear V8 cache
    clearV8CodeCache();

    return {
      success: true,
      message: 'Inject thành công! Restart Antigravity để kích hoạt.',
      workbenchPath: wbPath,
      config: mergedConfig
    };
  } catch (e) {
    return { success: false, error: e.message };
  }
}

/**
 * Remove injected script
 */
function uninstall() {
  const appRoot = findAntigravityPath();
  if (!appRoot) {
    return { success: false, error: 'Không tìm thấy Antigravity.' };
  }

  const wbPath = findWorkbenchHtml(appRoot);
  if (!wbPath) {
    return { success: false, error: 'Không tìm thấy workbench.html.' };
  }

  const wbDir = path.dirname(wbPath);

  try {
    // Remove from HTML
    let html = fs.readFileSync(wbPath, 'utf8');
    const htmlRegex = new RegExp(`${escapeRegex(TAG_START)}[\\s\\S]*?${escapeRegex(TAG_END)}`, 'g');
    html = html.replace(htmlRegex, '');

    // Also remove old AG Auto extension injection
    html = html.replace(/<!-- AG-AUTO-SCRIPT-START -->[\s\S]*?<!-- AG-AUTO-SCRIPT-END -->/g, '');
    html = html.replace(/<script[^>]*ag-auto-script[^>]*><\/script>/g, '');

    writeFileElevated(wbPath, html);

    // Remove script file
    const scriptPath = path.join(wbDir, SCRIPT_FILENAME);
    if (fs.existsSync(scriptPath)) {
      try { fs.unlinkSync(scriptPath); } catch (_) { }
    }

    // Remove config file
    const configPath = path.join(wbDir, CONFIG_FILENAME);
    if (fs.existsSync(configPath)) {
      try { fs.unlinkSync(configPath); } catch (_) { }
    }

    // Update checksums
    updateProductChecksums(appRoot);

    // Clear V8 cache
    clearV8CodeCache();

    return {
      success: true,
      message: 'Đã gỡ bỏ! Restart Antigravity để hoàn tất.'
    };
  } catch (e) {
    return { success: false, error: e.message };
  }
}

/**
 * Update config (live, without re-inject)
 */
function updateConfig(config) {
  const appRoot = findAntigravityPath();
  if (!appRoot) {
    return { success: false, error: 'Không tìm thấy Antigravity.' };
  }

  const wbPath = findWorkbenchHtml(appRoot);
  if (!wbPath) {
    return { success: false, error: 'Không tìm thấy workbench.html.' };
  }

  const wbDir = path.dirname(wbPath);
  const mergedConfig = { ...DEFAULT_CONFIG, ...config };

  try {
    const configPath = path.join(wbDir, CONFIG_FILENAME);
    writeFileElevated(configPath, JSON.stringify(mergedConfig));
    return { success: true, config: mergedConfig };
  } catch (e) {
    return { success: false, error: e.message };
  }
}

module.exports = {
  findAntigravityPath,
  getStatus,
  inject,
  uninstall,
  updateConfig,
  DEFAULT_CONFIG
};
