#!/usr/bin/env node
/**
 * GhostFoundry-Syndicate Local Agent
 * 
 * This agent runs on your local machine and enables:
 * - Remote command execution from Telegram
 * - Automated deployments
 * - Bidirectional communication with the Ghost
 * 
 * Usage:
 *   1. Set environment variables (or create .env file):
 *      - GFS_SERVER_URL: Your GFS server URL (e.g., https://spookysoftwaresyndicate.com)
 *      - GFS_AGENT_TOKEN: Your agent authentication token
 *   2. Run: node agent.js
 */

const { exec, spawn } = require('child_process');
const crypto = require('crypto');
const https = require('https');
const http = require('http');
const fs = require('fs');
const path = require('path');

// Load .env file if exists
const envPath = path.join(__dirname, '.env');
if (fs.existsSync(envPath)) {
  const envContent = fs.readFileSync(envPath, 'utf8');
  envContent.split('\n').forEach(line => {
    const [key, ...valueParts] = line.split('=');
    if (key && valueParts.length > 0) {
      process.env[key.trim()] = valueParts.join('=').trim();
    }
  });
}

// Configuration
const CONFIG = {
  serverUrl: process.env.GFS_SERVER_URL || 'https://spookysoftwaresyndicate.com',
  agentToken: process.env.GFS_AGENT_TOKEN || '',
  pollInterval: parseInt(process.env.GFS_POLL_INTERVAL || '5000', 10),
  machineId: process.env.GFS_MACHINE_ID || crypto.randomBytes(8).toString('hex'),
  allowedCommands: [
    'status',
    'deploy',
    'git_pull',
    'git_status',
    'npm_install',
    'yarn_install',
    'run_script',
    'system_info',
    'ping',
  ],
};

console.log('\n👻 GhostFoundry-Syndicate Local Agent');
console.log('=====================================');
console.log(`Server: ${CONFIG.serverUrl}`);
console.log(`Machine ID: ${CONFIG.machineId}`);
console.log(`Poll Interval: ${CONFIG.pollInterval}ms`);
console.log('=====================================\n');

// Utility: Make HTTP request
function makeRequest(method, urlPath, data = null) {
  return new Promise((resolve, reject) => {
    const url = new URL(urlPath, CONFIG.serverUrl);
    const isHttps = url.protocol === 'https:';
    const lib = isHttps ? https : http;

    const options = {
      hostname: url.hostname,
      port: url.port || (isHttps ? 443 : 80),
      path: url.pathname + url.search,
      method,
      headers: {
        'Content-Type': 'application/json',
        'X-Agent-Token': CONFIG.agentToken,
        'X-Machine-ID': CONFIG.machineId,
      },
    };

    const req = lib.request(options, (res) => {
      let body = '';
      res.on('data', chunk => body += chunk);
      res.on('end', () => {
        try {
          resolve({ status: res.statusCode, data: JSON.parse(body) });
        } catch {
          resolve({ status: res.statusCode, data: body });
        }
      });
    });

    req.on('error', reject);

    if (data) {
      req.write(JSON.stringify(data));
    }
    req.end();
  });
}

// Execute local command
function executeCommand(cmd, args = [], cwd = process.cwd()) {
  return new Promise((resolve) => {
    const startTime = Date.now();
    
    exec(`${cmd} ${args.join(' ')}`, { cwd, timeout: 120000 }, (error, stdout, stderr) => {
      resolve({
        success: !error,
        stdout: stdout?.toString() || '',
        stderr: stderr?.toString() || '',
        error: error?.message || null,
        duration: Date.now() - startTime,
      });
    });
  });
}

// Command handlers
const commandHandlers = {
  ping: async () => ({
    success: true,
    message: 'pong',
    timestamp: new Date().toISOString(),
    machineId: CONFIG.machineId,
  }),

  status: async () => {
    const uptime = process.uptime();
    const memory = process.memoryUsage();
    return {
      success: true,
      uptime: `${Math.floor(uptime / 60)}m ${Math.floor(uptime % 60)}s`,
      memory: `${Math.round(memory.heapUsed / 1024 / 1024)}MB`,
      platform: process.platform,
      nodeVersion: process.version,
      machineId: CONFIG.machineId,
    };
  },

  system_info: async () => {
    const os = require('os');
    return {
      success: true,
      hostname: os.hostname(),
      platform: os.platform(),
      arch: os.arch(),
      cpus: os.cpus().length,
      totalMemory: `${Math.round(os.totalmem() / 1024 / 1024 / 1024)}GB`,
      freeMemory: `${Math.round(os.freemem() / 1024 / 1024 / 1024)}GB`,
      uptime: `${Math.floor(os.uptime() / 3600)}h`,
    };
  },

  git_status: async (args) => {
    const cwd = args.cwd || process.cwd();
    return executeCommand('git', ['status', '--short'], cwd);
  },

  git_pull: async (args) => {
    const cwd = args.cwd || process.cwd();
    return executeCommand('git', ['pull'], cwd);
  },

  npm_install: async (args) => {
    const cwd = args.cwd || process.cwd();
    return executeCommand('npm', ['install'], cwd);
  },

  yarn_install: async (args) => {
    const cwd = args.cwd || process.cwd();
    return executeCommand('yarn', ['install'], cwd);
  },

  run_script: async (args) => {
    if (!args.script) {
      return { success: false, error: 'Missing script argument' };
    }
    const cwd = args.cwd || process.cwd();
    return executeCommand(args.script, args.scriptArgs || [], cwd);
  },

  deploy: async (args) => {
    console.log('🚀 Executing deploy command...');
    
    // This is a placeholder - customize for your deployment workflow
    const steps = [];
    
    // Step 1: Git pull (if enabled)
    if (args.gitPull !== false) {
      const pullResult = await executeCommand('git', ['pull'], args.cwd);
      steps.push({ step: 'git_pull', ...pullResult });
      if (!pullResult.success) {
        return { success: false, error: 'Git pull failed', steps };
      }
    }

    // Step 2: Install dependencies
    if (args.install !== false) {
      const installResult = await executeCommand('yarn', ['install'], args.cwd);
      steps.push({ step: 'yarn_install', ...installResult });
      if (!installResult.success) {
        return { success: false, error: 'Yarn install failed', steps };
      }
    }

    // Step 3: Build (if requested)
    if (args.build) {
      const buildResult = await executeCommand('yarn', ['build'], args.cwd);
      steps.push({ step: 'build', ...buildResult });
      if (!buildResult.success) {
        return { success: false, error: 'Build failed', steps };
      }
    }

    return {
      success: true,
      message: 'Deployment completed',
      steps,
    };
  },
};

// Process a command from the queue
async function processCommand(command) {
  const { id, command: cmd, args } = command;
  console.log(`\n⚡ Processing command: ${cmd} (ID: ${id.slice(0, 8)})`);

  if (!CONFIG.allowedCommands.includes(cmd)) {
    return {
      success: false,
      error: `Command not allowed: ${cmd}`,
    };
  }

  const handler = commandHandlers[cmd];
  if (!handler) {
    return {
      success: false,
      error: `No handler for command: ${cmd}`,
    };
  }

  try {
    const result = await handler(args || {});
    console.log(`✅ Command ${cmd} completed: ${result.success ? 'success' : 'failed'}`);
    return result;
  } catch (error) {
    console.error(`❌ Command ${cmd} error:`, error.message);
    return {
      success: false,
      error: error.message,
    };
  }
}

// Poll for commands
async function pollCommands() {
  try {
    const response = await makeRequest('GET', '/api/gfs/local-agent/commands');
    
    if (response.status === 200 && response.data.commands?.length > 0) {
      for (const command of response.data.commands) {
        const result = await processCommand(command);
        
        // Report result back to server
        await makeRequest('POST', '/api/gfs/local-agent/result', {
          commandId: command.id,
          result,
        });
      }
    }
  } catch (error) {
    // Silent fail - server might be temporarily unavailable
    if (error.code !== 'ECONNREFUSED') {
      console.error('Poll error:', error.message);
    }
  }
}

// Register agent with server
async function registerAgent() {
  try {
    const response = await makeRequest('POST', '/api/gfs/local-agent/register', {
      machineId: CONFIG.machineId,
      platform: process.platform,
      nodeVersion: process.version,
      capabilities: CONFIG.allowedCommands,
    });
    
    if (response.status === 200) {
      console.log('✅ Agent registered with server');
      return true;
    } else {
      console.error('❌ Failed to register agent:', response.data);
      return false;
    }
  } catch (error) {
    console.error('❌ Registration error:', error.message);
    return false;
  }
}

// Main loop
async function main() {
  if (!CONFIG.agentToken) {
    console.error('\n❌ Error: GFS_AGENT_TOKEN is required');
    console.log('\nTo generate a token:');
    console.log('1. Go to Ghost Control Dashboard');
    console.log('2. Navigate to Settings > Local Agent');
    console.log('3. Generate a new agent token');
    console.log('4. Set GFS_AGENT_TOKEN environment variable\n');
    process.exit(1);
  }

  console.log('🔄 Starting polling loop...');
  
  // Initial registration
  await registerAgent();
  
  // Poll loop
  setInterval(pollCommands, CONFIG.pollInterval);
  
  // Initial poll
  await pollCommands();
  
  console.log('\n👻 Agent is running. Press Ctrl+C to stop.\n');
}

main().catch(console.error);
