// Antigravity Mobile Connector v2.0 — VSCode Extension
// Bridge-inside-VSCode: pair channel + Gemini CLI spawn
const vscode = require('vscode');
const path = require('path');
const fs = require('fs');
const os = require('os');
const { Bridge, loadConfig, saveConfig } = require('./bridge');

let bridge = null;
let out = null;        // OutputChannel
let statusBar = null;  // StatusBarItem
let ctx = null;        // ExtensionContext
let isProcessing = false;
let welcomePanel = null;

function log(msg) {
  const line = `[${new Date().toISOString().slice(11, 19)}] ${msg}`;
  out?.appendLine(line);
}

// ── Status Bar ──
function updateStatus(state, extra) {
  if (!statusBar) return;
  switch (state) {
    case 'no-auth':
      statusBar.text = '$(key) Gemini: Sign in';
      statusBar.tooltip = 'Gemini CLI is not authenticated. Click to sign in.';
      statusBar.color = '#fbbf24'; break;
    case 'pairing':
      statusBar.text = '$(sync~spin) Waiting for iPhone...';
      statusBar.tooltip = 'Open the Antigravity app and scan/connect';
      statusBar.color = '#60a5fa';
      updateWelcomePanel({ pairing: true });
      break;
    case 'connected':
      statusBar.text = `$(device-mobile) Mobile: Connected`;
      statusBar.tooltip = `Pair: ${bridge?.pairId?.slice(0, 8)}… | Session: ${bridge?.sessionId?.slice(0, 8)}…`;
      statusBar.color = '#4ade80';
      updateWelcomePanel({ connected: true, pairId: bridge?.pairId || '' });
      break;
    case 'processing':
      statusBar.text = '$(loading~spin) Processing...';
      statusBar.tooltip = `Processing message from iPhone`;
      statusBar.color = '#60a5fa'; break;
    case 'error':
      statusBar.text = `$(error) ${extra || 'Error'}`;
      statusBar.tooltip = extra || 'An error occurred';
      statusBar.color = '#f87171'; break;
    case 'disconnected':
      statusBar.text = '$(device-mobile) Not Connected';
      statusBar.tooltip = 'Click to connect your iPhone';
      statusBar.color = undefined;
      updateWelcomePanel({ connected: false });
      break;
  }
}

// ── Gemini Auth Check ──
async function checkGeminiAuth() {
  // Check API key in settings
  const apiKey = vscode.workspace.getConfiguration('antigravity').get('geminiApiKey');
  if (apiKey) { process.env.GEMINI_API_KEY = apiKey; return true; }
  if (process.env.GEMINI_API_KEY) return true;

  // Check if gemini CLI exists and has been authed
  const cli = bridge?.findGeminiCLI();
  if (!cli) {
    log('Gemini CLI not found — will use npx fallback');
    return true; // npx will handle auth
  }

  // Check for OAuth credentials (where Gemini CLI actually stores tokens)
  const geminiDir = path.join(os.homedir(), '.gemini');
  const oauthPath = path.join(geminiDir, 'oauth_creds.json');
  try {
    if (fs.existsSync(oauthPath)) {
      const creds = JSON.parse(fs.readFileSync(oauthPath, 'utf8'));
      if (creds?.refresh_token || creds?.access_token) return true;
    }
  } catch {}

  // Fallback: check settings.json
  const settingsPath = path.join(geminiDir, 'settings.json');
  try {
    if (fs.existsSync(settingsPath)) {
      const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
      if (settings?.security?.auth?.selectedType) return true;
    }
  } catch {}

  return false;
}

async function promptAuth() {
  const choice = await vscode.window.showWarningMessage(
    'Gemini CLI is not authenticated. Sign in to use mobile prompts.',
    'Sign in with Google',
    'Use API Key'
  );
  if (choice === 'Sign in with Google') {
    await vscode.commands.executeCommand('antigravity.signInToGemini');
  } else if (choice === 'Use API Key') {
    const key = await vscode.window.showInputBox({
      prompt: 'Enter your Gemini API key from Google AI Studio',
      placeHolder: 'AIza...',
      password: true
    });
    if (key) {
      await vscode.workspace.getConfiguration('antigravity').update('geminiApiKey', key, true);
      process.env.GEMINI_API_KEY = key;
      vscode.window.showInformationMessage('API key saved!');
    }
  }
}

// ── File Tree ──
async function generateFileTree(rootUri, maxDepth = 3) {
  const nodes = [];
  async function walk(uri, depth) {
    if (depth > maxDepth) return;
    try {
      const entries = await vscode.workspace.fs.readDirectory(uri);
      for (const [name, type] of entries) {
        if (name.startsWith('.') || name === 'node_modules' || name === '__pycache__') continue;
        const childUri = vscode.Uri.joinPath(uri, name);
        const isDir = type === vscode.FileType.Directory;
        const node = { name, path: childUri.fsPath, isDirectory: isDir };
        if (isDir && depth < maxDepth) {
          node.children = [];
          const saved = nodes;
          await walk(childUri, depth + 1);
          // Collect children from recursive walk
        }
        nodes.push(node);
      }
    } catch {}
  }

  // Better implementation: build tree properly
  async function buildTree(uri, depth) {
    if (depth > maxDepth) return [];
    const result = [];
    try {
      const entries = await vscode.workspace.fs.readDirectory(uri);
      entries.sort((a, b) => {
        if (a[1] !== b[1]) return a[1] === vscode.FileType.Directory ? -1 : 1;
        return a[0].localeCompare(b[0]);
      });
      for (const [name, type] of entries) {
        if (name.startsWith('.') || name === 'node_modules' || name === '__pycache__' || name === '.git') continue;
        const childUri = vscode.Uri.joinPath(uri, name);
        const isDir = type === vscode.FileType.Directory;
        const node = { name, path: childUri.fsPath, isDirectory: isDir };
        if (isDir) { node.children = await buildTree(childUri, depth + 1); }
        result.push(node);
      }
    } catch {}
    return result;
  }

  const folders = vscode.workspace.workspaceFolders;
  if (folders?.length) return buildTree(folders[0].uri, 0);

  // Fallback: use saved project path or cwd (not homedir to avoid exposing entire Mac)
  const cfg = loadConfig();
  const fallbackPath = cfg.projectPath || process.cwd();
  if (fallbackPath && fs.existsSync(fallbackPath)) {
    return buildTree(vscode.Uri.file(fallbackPath), 0);
  }
  return [];
}

// ── File Content ──
async function readFileContent(filePath) {
  try {
    // Security: only allow reading files within workspace or project path
    const folders = vscode.workspace.workspaceFolders;
    const cfg = loadConfig();
    const allowedRoot = folders?.[0]?.uri.fsPath || cfg.projectPath || '';
    const resolved = path.resolve(filePath);
    if (allowedRoot && !resolved.startsWith(allowedRoot)) {
      return { content: `Access denied: file is outside the project directory`, language: 'text' };
    }

    const uri = vscode.Uri.file(resolved);
    const content = await vscode.workspace.fs.readFile(uri);
    const text = Buffer.from(content).toString('utf8');
    const ext = path.extname(filePath).slice(1);
    const langMap = { js: 'javascript', ts: 'typescript', py: 'python', rb: 'ruby', rs: 'rust', go: 'go', java: 'java', swift: 'swift', kt: 'kotlin', md: 'markdown', json: 'json', html: 'html', css: 'css', yaml: 'yaml', yml: 'yaml', xml: 'xml', sh: 'shell', sql: 'sql' };
    return { content: text, language: langMap[ext] || ext || 'text' };
  } catch (e) {
    return { content: `Error reading file: ${e.message}`, language: 'text' };
  }
}

// ── Message Processing ──
async function processMessage(msg) {
  if (isProcessing) { log('Already processing, skipping'); return; }
  isProcessing = true;
  updateStatus('processing');

  const messageId = msg.id;
  const content = msg.content;
  log(`Processing: "${content.slice(0, 60)}…"`);

  try {
    await bridge.markAsProcessing(messageId);

    const folders = vscode.workspace.workspaceFolders;
    const cfg = loadConfig();
    const cwd = folders?.[0]?.uri.fsPath || cfg.projectPath || process.cwd();
    const model = msg.model || null;

    const { process: proc, result } = bridge.runGemini(content, cwd, model, (delta) => {
      bridge.sendChunk(messageId, delta);
    });

    const res = await result;
    bridge.geminiProc = null;

    if (res.errors?.length && !res.output.trim()) {
      const errMsg = res.errors.join('\n');
      await bridge.sendReply(`Error: ${errMsg}`, messageId, model, 'error');
      log(`Error: ${errMsg}`);
    } else {
      const reply = res.output.trim() || '(empty response)';
      await bridge.sendReply(reply, messageId, model, 'completed');
      log(`Reply sent (${reply.length} chars)`);
    }
  } catch (e) {
    log(`Process error: ${e.message}`);
    try { await bridge.sendReply(`Extension error: ${e.message}`, messageId, null, 'error'); } catch {}
  } finally {
    isProcessing = false;
    updateStatus('connected');
  }
}

// ── Connect Flow ──
async function fullConnect(pId) {
  try {
    log('Signing in to Supabase...');
    await bridge.signIn();
    log(`Auth: ${bridge.bridgeAuthUserId?.slice(0, 8)}…`);

    bridge.pairId = pId;
    if (!await bridge.isPairActive(pId)) {
      updateStatus('error', 'Pair inactive');
      log('Pair is not active'); return false;
    }

    const folders = vscode.workspace.workspaceFolders;
    const projectPath = folders?.[0]?.uri.fsPath || '';
    saveConfig({ pairId: pId, projectPath });

    log('Creating session...');
    await bridge.ensureSession(projectPath);
    log(`Session: ${bridge.sessionId?.slice(0, 8)}…`);

    log('Joining channel...');
    await bridge.joinPairChannel({
      onUserMessage: (msg) => processMessage(msg),
      onCancel: (mid) => {
        log(`Cancel: ${mid}`);
        bridge.terminateGemini();
      },
      onFileTreeCmd: async (action, p) => {
        log('File tree requested');
        const tree = await generateFileTree(folders?.[0]?.uri);
        await bridge.sendFileTree(tree, p || projectPath);
      },
      onReadFile: async (p) => {
        log(`File read: ${p}`);
        const { content, language } = await readFileContent(p);
        await bridge.sendFileContent(p, content, language);
      },
      onFocusDir: (p) => { log(`Focus dir: ${p}`); }
    });

    updateStatus('connected');
    log('✅ Connected and listening');

    // Catch up on pending messages
    const pending = await bridge.getPendingMessages();
    if (pending.length) {
      log(`${pending.length} pending message(s), processing first...`);
      processMessage(pending[0]);
    }
    return true;
  } catch (e) {
    log(`Connect error: ${e.message}`);
    updateStatus('error', e.message.slice(0, 40));
    return false;
  }
}

// ── Commands ──
async function cmdConnect() {
  const hasAuth = await checkGeminiAuth();
  if (!hasAuth) { await promptAuth(); }

  bridge = new Bridge(log);
  updateStatus('pairing');

  try {
    await bridge.signIn();
    await bridge.startPairingServer(async (pId) => {
      bridge.stopPairingServer();
      await fullConnect(pId);
    });
    vscode.window.showInformationMessage(
      'Waiting for iPhone connection. Open the Antigravity app to pair.',
      'Cancel'
    ).then(choice => {
      if (choice === 'Cancel') { bridge.stopPairingServer(); updateStatus('disconnected'); }
    });
  } catch (e) {
    vscode.window.showErrorMessage(e.message);
    updateStatus('disconnected');
  }
}

function cmdSignIn() {
  // Use -p with a minimal prompt to trigger the OAuth flow when credentials are missing.
  // --list-sessions doesn't trigger OAuth, but -p does (headless mode triggers browser auth).
  // stderr is redirected to hide noisy extension warnings.
  const terminal = vscode.window.createTerminal({
    name: 'Gemini Auth',
    message: 'Signing in to Google...'
  });
  terminal.show();
  terminal.sendText('gemini --skip-trust -p "hi" 2>/dev/null && echo "\\n✅ Gemini authenticated! You can close this terminal."');
}

async function cmdDisconnect() {
  try {
    bridge?.terminateGemini();
    await bridge?.endSession();
    await bridge?.leaveChannel();
    bridge?.stopPairingServer();
  } catch {}
  bridge = null;
  updateStatus('disconnected');
  vscode.window.showInformationMessage('Disconnected from mobile.');
  log('Disconnected');
}

async function cmdLogout() {
  // Disconnect bridge first
  try {
    bridge?.terminateGemini();
    await bridge?.endSession();
    await bridge?.leaveChannel();
    bridge?.stopPairingServer();
  } catch {}
  bridge = null;

  // Clear Gemini CLI cached credentials
  const geminiDir = path.join(os.homedir(), '.gemini');
  const filesToRemove = ['oauth_creds.json', 'google_accounts.json', 'state.json', 'settings.json', '.credentials.json'];
  for (const f of filesToRemove) {
    const fp = path.join(geminiDir, f);
    try { if (fs.existsSync(fp)) fs.unlinkSync(fp); } catch {}
  }

  // Clear API key
  delete process.env.GEMINI_API_KEY;
  try {
    await vscode.workspace.getConfiguration('antigravity').update('geminiApiKey', undefined, true);
  } catch {}

  updateStatus('no-auth');
  updateWelcomePanel({ loggedOut: true });
  vscode.window.showInformationMessage('Signed out of Google. You can sign in again anytime.');
  log('Logged out of Gemini');
}

function cmdShowInfo() {
  const cfg = loadConfig();
  const info = [
    `Pair ID: ${bridge?.pairId || cfg.pairId || 'none'}`,
    `Session: ${bridge?.sessionId || 'none'}`,
    `Auth: ${bridge?.bridgeAuthUserId?.slice(0, 8) || 'none'}…`,
    `Gemini CLI: ${bridge?.findGeminiCLI() || 'not found (npx fallback)'}`,
    `API Key: ${process.env.GEMINI_API_KEY ? 'set' : 'not set'}`,
    `Processing: ${isProcessing ? 'yes' : 'no'}`,
  ].join('\n');
  vscode.window.showInformationMessage(info, { modal: true });
}

// ── Welcome Panel (Webview UI) ──
async function showWelcomePanel() {
  if (welcomePanel) { welcomePanel.reveal(); return; }

  welcomePanel = vscode.window.createWebviewPanel(
    'antigravityWelcome', 'Antigravity Mobile', vscode.ViewColumn.One,
    { enableScripts: true, retainContextWhenHidden: true }
  );

  const hasAuth = await checkGeminiAuth();
  const isConnected = !!(bridge?.channel);
  const pairId = bridge?.pairId || loadConfig().pairId || '';

  welcomePanel.webview.html = getWelcomeHTML(hasAuth, isConnected, pairId);

  welcomePanel.webview.onDidReceiveMessage(async (msg) => {
    switch (msg.command) {
      case 'signIn':
        await vscode.commands.executeCommand('antigravity.signInToGemini');
        break;
      case 'apiKey':
        const key = await vscode.window.showInputBox({
          prompt: 'Enter your Gemini API key from Google AI Studio',
          placeHolder: 'AIza...', password: true
        });
        if (key) {
          await vscode.workspace.getConfiguration('antigravity').update('geminiApiKey', key, true);
          process.env.GEMINI_API_KEY = key;
          updateWelcomePanel({ authDone: true });
          vscode.window.showInformationMessage('API key saved!');
        }
        break;
      case 'connect':
        await vscode.commands.executeCommand('antigravity.connectMobile');
        break;
      case 'disconnect':
        await vscode.commands.executeCommand('antigravity.disconnect');
        break;
      case 'logout':
        await cmdLogout();
        break;
      case 'showInfo':
        cmdShowInfo();
        break;
    }
  }, undefined, ctx.subscriptions);
welcomePanel.onDidDispose(() => { welcomePanel = null; });
}

function updateWelcomePanel(state) {
  if (!welcomePanel) return;
  welcomePanel.webview.postMessage({ type: 'update', ...state });
}

function getWelcomeHTML(hasAuth, isConnected, pairId) {
  const state = isConnected ? 'connected' : (hasAuth ? 'ready' : 'needAuth');
  return `<!DOCTYPE html>
<html><head><style>
  * { box-sizing: border-box; margin: 0; padding: 0; }
  body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; padding: 48px 24px; max-width: 420px; margin: 0 auto; color: var(--vscode-foreground); background: var(--vscode-editor-background); text-align: center; }
  .header { margin-bottom: 32px; }
  .header h1 { font-size: 24px; font-weight: 700; margin-bottom: 6px; }
  .header p { color: var(--vscode-descriptionForeground); font-size: 13px; line-height: 1.5; }
  .action-card { background: var(--vscode-editorWidget-background); border: 1px solid var(--vscode-editorWidget-border, #333); border-radius: 14px; padding: 28px 24px; margin-bottom: 16px; }
  .action-icon { font-size: 40px; margin-bottom: 12px; }
  .action-title { font-size: 17px; font-weight: 700; margin-bottom: 6px; }
  .action-desc { font-size: 13px; color: var(--vscode-descriptionForeground); margin-bottom: 18px; line-height: 1.5; }
  .btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; padding: 12px 28px; border-radius: 10px; font-size: 14px; font-weight: 600; cursor: pointer; border: none; transition: all 0.15s; min-width: 200px; }
  .btn:hover { opacity: 0.88; transform: translateY(-1px); }
  .btn:active { transform: translateY(0); }
  .btn-big { background: #2563EB; color: #fff; font-size: 15px; padding: 14px 32px; }
  .btn-outline { background: transparent; color: var(--vscode-foreground); border: 1px solid var(--vscode-editorWidget-border, #555); font-size: 13px; padding: 9px 20px; min-width: auto; }
  .btn-danger { background: transparent; color: #f87171; border: 1px solid #f8717144; font-size: 12px; padding: 7px 16px; min-width: auto; }
  .or-divider { color: var(--vscode-descriptionForeground); font-size: 11px; margin: 10px 0; }
  .status-pill { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; padding: 5px 14px; border-radius: 20px; margin-top: 12px; }
  .status-pill.green { background: #dcfce7; color: #166534; }
  .status-pill.yellow { background: #fef3c7; color: #92400e; }
  .dot { width: 7px; height: 7px; border-radius: 50%; }
  .dot-green { background: #22c55e; }
  .dot-yellow { background: #f59e0b; animation: pulse 2s ease-in-out infinite; }
  @keyframes pulse { 0%,100%{opacity:1;} 50%{opacity:0.3;} }
  .connected-card { border-color: #22c55e44; }
  .checkmark { width: 48px; height: 48px; background: #22c55e; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 12px; font-size: 24px; color: #fff; }
  .footer { margin-top: 20px; }
  .footer a { color: var(--vscode-descriptionForeground); font-size: 11px; text-decoration: none; cursor: pointer; }
  .footer a:hover { color: var(--vscode-foreground); }
</style></head><body>
  <div class="header">
    <h1>Antigravity Mobile</h1>
    <p>Code from your iPhone — prompts, files, and AI responses</p>
  </div>

  <div class="action-card" id="card-auth" style="display:${state === 'needAuth' ? 'block' : 'none'}">
    <div class="action-icon">&#128273;</div>
    <div class="action-title">Sign in to get started</div>
    <div class="action-desc">Connect your Google account to enable Gemini AI responses from your phone.</div>
    <button class="btn btn-big" onclick="send('signIn')">Sign in with Google</button>
    <div class="or-divider">or</div>
    <button class="btn btn-outline" onclick="send('apiKey')">Paste API Key instead</button>
  </div>

  <div class="action-card" id="card-connect" style="display:${state === 'ready' ? 'block' : 'none'}">
    <div class="action-icon">&#128242;</div>
    <div class="action-title">Connect your iPhone</div>
    <div class="action-desc">Open the Antigravity app on your phone and tap Connect.<br>The bridge starts automatically.</div>
    <button class="btn btn-big" onclick="send('connect')">Connect Mobile</button>
    <div id="connect-status"></div>
    <div style="margin-top:14px;"><a class="footer-link" onclick="send('logout')" style="color:var(--vscode-descriptionForeground);font-size:11px;cursor:pointer;text-decoration:none;">Sign out of Google</a></div>
  </div>

  <div class="action-card connected-card" id="card-done" style="display:${state === 'connected' ? 'block' : 'none'}">
    <div class="checkmark">&#10003;</div>
    <div class="action-title">You're all set!</div>
    <div class="action-desc">Your iPhone is connected. Open the Antigravity app and start sending prompts.</div>
    <div class="status-pill green"><span class="dot dot-green"></span> Connected${pairId ? ' — ' + pairId.slice(0, 8) + '…' : ''}</div>
    <div style="margin-top:16px;"><button class="btn btn-danger" onclick="send('disconnect')">Disconnect</button></div>
  </div>

  <div class="footer"><a onclick="send('showInfo')">Connection details</a></div>

  <script>
    const vscode = acquireVsCodeApi();
    function send(cmd) { vscode.postMessage({ command: cmd }); }
    function showCard(id) {
      ['card-auth','card-connect','card-done'].forEach(c => {
        document.getElementById(c).style.display = c === id ? 'block' : 'none';
      });
    }
    window.addEventListener('message', e => {
      const msg = e.data;
      if (msg.type !== 'update') return;
      if (msg.loggedOut) showCard('card-auth');
      if (msg.authDone) showCard('card-connect');
      if (msg.connected === true) {
        const pill = document.querySelector('#card-done .status-pill');
        if (msg.pairId) pill.innerHTML = '<span class="dot dot-green"></span> Connected — ' + msg.pairId.slice(0,8) + '…';
        showCard('card-done');
      }
      if (msg.connected === false) {
        showCard('card-connect');
        document.getElementById('connect-status').innerHTML = '';
      }
      if (msg.pairing) {
        document.getElementById('connect-status').innerHTML = '<div class="status-pill yellow" style="margin-top:12px;"><span class="dot dot-yellow"></span> Waiting for iPhone…</div>';
      }
    });
  </script>
</body></html>`;
}

// ── Activate ──
async function activate(context) {
  ctx = context;
  out = vscode.window.createOutputChannel('Antigravity Mobile');
  log('═══ Antigravity Mobile Connector v2.0 ═══');

  // Status bar
  statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 100);
  statusBar.command = 'antigravity.welcome';
  statusBar.show();
  context.subscriptions.push(statusBar);

  // Commands
  context.subscriptions.push(
    vscode.commands.registerCommand('antigravity.connectMobile', cmdConnect),
    vscode.commands.registerCommand('antigravity.signInToGemini', cmdSignIn),
    vscode.commands.registerCommand('antigravity.disconnect', cmdDisconnect),
    vscode.commands.registerCommand('antigravity.showInfo', cmdShowInfo),
    vscode.commands.registerCommand('antigravity.welcome', showWelcomePanel),
  );

  // Load API key from settings
  const apiKey = vscode.workspace.getConfiguration('antigravity').get('geminiApiKey');
  if (apiKey) process.env.GEMINI_API_KEY = apiKey;

  // Auto-reconnect if previously paired
  const cfg = loadConfig();
  if (cfg.pairId) {
    log(`Found saved pair: ${cfg.pairId.slice(0, 8)}…`);
    bridge = new Bridge(log);

    const hasAuth = await checkGeminiAuth();
    if (!hasAuth) {
      updateStatus('no-auth');
      log('Gemini not authenticated — prompting user');
      promptAuth();
      return;
    }

    updateStatus('pairing');
    const ok = await fullConnect(cfg.pairId);
    if (!ok) {
      updateStatus('disconnected');
      log('Auto-reconnect failed — pair may be inactive');
    }
  } else {
    updateStatus('disconnected');
    // First install — show welcome panel
    if (!context.globalState.get('welcomed')) {
      context.globalState.update('welcomed', true);
      showWelcomePanel();
    }
  }
}

async function deactivate() {
  try {
    bridge?.terminateGemini();
    await bridge?.endSession();
    await bridge?.leaveChannel();
    bridge?.stopPairingServer();
  } catch {}
}

module.exports = { activate, deactivate };
