/**
 * Claude Code Vietnamese IME Fix — Node.js Port
 * 
 * Fixes Vietnamese input bug in Claude Code CLI by patching
 * the backspace handling logic to also insert replacement text.
 * 
 * Ported from: https://github.com/manhit96/claude-code-vietnamese-fix
 */

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

const PATCH_MARKER = '/* Vietnamese IME fix */';
const DEL_CHAR = '\x7f'; // 0x7F - character used by Vietnamese IME

/**
 * Auto-detect Claude Code cli.js location
 */
function findCliJs() {
  const home = os.homedir();
  const isWindows = process.platform === 'win32';

  const searchDirs = isWindows
    ? [
        path.join(process.env.LOCALAPPDATA || '', 'npm-cache', '_npx'),
        path.join(process.env.APPDATA || '', 'npm', 'node_modules'),
      ]
    : [
        path.join(home, '.npm', '_npx'),
        path.join(home, '.nvm', 'versions', 'node'),
        '/usr/local/lib/node_modules',
        '/opt/homebrew/lib/node_modules',
      ];

  for (const dir of searchDirs) {
    if (!fs.existsSync(dir)) continue;
    const result = findRecursive(dir, '@anthropic-ai/claude-code/cli.js');
    if (result) return result;
  }

  return null;
}

/**
 * Recursively find a file matching the suffix pattern
 */
function findRecursive(dir, suffix, depth = 0) {
  if (depth > 8) return null; // limit depth
  try {
    const entries = fs.readdirSync(dir, { withFileTypes: true });
    for (const entry of entries) {
      const fullPath = path.join(dir, entry.name);
      if (entry.isDirectory()) {
        const result = findRecursive(fullPath, suffix, depth + 1);
        if (result) return result;
      } else if (fullPath.replace(/\\/g, '/').endsWith(suffix)) {
        return fullPath;
      }
    }
  } catch (e) {
    // Permission denied, etc.
  }
  return null;
}

/**
 * Find the if-block containing the Vietnamese IME bug pattern
 */
function findBugBlock(content) {
  const pattern = `.includes("${DEL_CHAR}")`;
  const idx = content.indexOf(pattern);

  if (idx === -1) {
    return { error: 'Không tìm thấy bug pattern .includes("\\x7f"). Claude Code có thể đã được Anthropic fix.' };
  }

  // Find containing if(
  let blockStart = content.lastIndexOf('if(', idx);
  if (blockStart === -1 || idx - blockStart > 150) {
    return { error: 'Không tìm thấy block if chứa pattern' };
  }

  // Find matching closing brace
  let depth = 0;
  let blockEnd = idx;
  const searchArea = content.substring(blockStart, blockStart + 800);
  for (let i = 0; i < searchArea.length; i++) {
    if (searchArea[i] === '{') depth++;
    else if (searchArea[i] === '}') {
      depth--;
      if (depth === 0) {
        blockEnd = blockStart + i + 1;
        break;
      }
    }
  }

  if (depth !== 0) {
    return { error: 'Không tìm thấy closing brace của block if' };
  }

  return {
    start: blockStart,
    end: blockEnd,
    block: content.substring(blockStart, blockEnd)
  };
}

/**
 * Extract dynamic variable names from the bug block
 */
function extractVariables(block) {
  const normalized = block.replace(/\x7f/g, '\\x7f');

  // Match: let COUNT=(INPUT.match(/\x7f/g)||[]).length,STATE=CURSTATE;
  const m1 = normalized.match(
    /let ([\w$]+)=\(\w+\.match\(\/\\x7f\/g\)\|\|\[\]\)\.length[,;]([\w$]+)=([\w$]+)[;,]/
  );
  if (!m1) return { error: 'Không trích xuất được biến count/state' };

  const state = m1[2];
  const curState = m1[3];

  // Match: UPDATETEXT(STATE.text);UPDATEOFFSET(STATE.offset)
  const stateEsc = state.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
  const m2 = block.match(
    new RegExp(`([\\w$]+)\\(${stateEsc}\\.text\\);([\\w$]+)\\(${stateEsc}\\.offset\\)`)
  );
  if (!m2) return { error: 'Không trích xuất được update functions' };

  // Match: INPUT.includes("
  const m3 = block.match(/([\w$]+)\.includes\("/);
  if (!m3) return { error: 'Không trích xuất được input variable' };

  return {
    input: m3[1],
    state,
    curState,
    updateText: m2[1],
    updateOffset: m2[2]
  };
}

/**
 * Generate the fix code
 */
function generateFix(v) {
  return (
    `${PATCH_MARKER}` +
    `if(${v.input}.includes("\\x7f")){` +
    `let _n=(${v.input}.match(/\\x7f/g)||[]).length,` +
    `_vn=${v.input}.replace(/\\x7f/g,""),` +
    `${v.state}=${v.curState};` +
    `for(let _i=0;_i<_n;_i++)${v.state}=${v.state}.backspace();` +
    `for(const _c of _vn)${v.state}=${v.state}.insert(_c);` +
    `if(!${v.curState}.equals(${v.state})){` +
    `if(${v.curState}.text!==${v.state}.text)` +
    `${v.updateText}(${v.state}.text);` +
    `${v.updateOffset}(${v.state}.offset)` +
    `}return;}`
  );
}

/**
 * Apply patch to cli.js
 */
function patch(filePath) {
  if (!fs.existsSync(filePath)) {
    return { success: false, error: `File không tồn tại: ${filePath}` };
  }

  const content = fs.readFileSync(filePath, 'utf-8');

  // Already patched?
  if (content.includes(PATCH_MARKER)) {
    return { success: true, alreadyPatched: true, message: 'Đã patch trước đó.' };
  }

  // Backup
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
  const backupPath = `${filePath}.backup-${timestamp}`;
  fs.copyFileSync(filePath, backupPath);

  try {
    // Find bug block
    const bugResult = findBugBlock(content);
    if (bugResult.error) return { success: false, error: bugResult.error, backup: backupPath };

    // Extract variables
    const vars = extractVariables(bugResult.block);
    if (vars.error) return { success: false, error: vars.error, backup: backupPath };

    // Generate fix
    const fixCode = generateFix(vars);
    const patched = content.substring(0, bugResult.start) + fixCode + content.substring(bugResult.end);

    // Write
    fs.writeFileSync(filePath, patched, 'utf-8');

    // Verify
    const verify = fs.readFileSync(filePath, 'utf-8');
    if (!verify.includes(PATCH_MARKER)) {
      throw new Error('Verify failed: patch marker not found after write');
    }

    return {
      success: true,
      message: 'Patch thành công! Khởi động lại Claude Code.',
      backup: backupPath,
      vars: { input: vars.input, state: vars.state, curState: vars.curState }
    };
  } catch (e) {
    // Rollback
    if (fs.existsSync(backupPath)) {
      fs.copyFileSync(backupPath, filePath);
      fs.unlinkSync(backupPath);
    }
    return { success: false, error: e.message, rolledBack: true };
  }
}

/**
 * Restore from latest backup
 */
function restore(filePath) {
  const dir = path.dirname(filePath);
  const basename = path.basename(filePath);

  const backups = fs.readdirSync(dir)
    .filter(f => f.startsWith(`${basename}.backup-`))
    .sort()
    .reverse();

  if (backups.length === 0) {
    return { success: false, error: 'Không tìm thấy backup' };
  }

  const latest = path.join(dir, backups[0]);
  fs.copyFileSync(latest, filePath);

  return {
    success: true,
    message: `Đã khôi phục từ: ${backups[0]}`,
    backup: latest
  };
}

/**
 * Check patch status
 */
function checkStatus(filePath) {
  if (!filePath || !fs.existsSync(filePath)) {
    return { found: false };
  }

  const content = fs.readFileSync(filePath, 'utf-8');
  const patched = content.includes(PATCH_MARKER);
  const hasBug = content.includes(`.includes("${DEL_CHAR}")`);

  return {
    found: true,
    path: filePath,
    patched,
    hasBug: !patched && hasBug,
    needsFix: !patched && hasBug
  };
}

module.exports = { findCliJs, patch, restore, checkStatus };
