import express from 'express'
import { Client } from 'ssh2'
import cors from 'cors'

const app = express()
app.use(cors())
app.use(express.json())

// Store active connections
const connections = new Map<string, Client>()

// POST /ssh/connect
app.post('/ssh/connect', async (req, res) => {
  const { host, port, username, password, testOnly } = req.body
  
  // Security: Only allow connections to Bandit server
  if (host !== 'bandit.labs.overthewire.org' || port !== 2220) {
    return res.status(403).json({ 
      success: false, 
      message: 'Only connections to bandit.labs.overthewire.org:2220 are allowed' 
    })
  }

  const client = new Client()
  const connectionId = `conn-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`

  client.on('ready', () => {
    if (testOnly) {
      client.end()
      return res.json({ 
        connectionId: null,
        success: true, 
        message: 'Password validated successfully' 
      })
    }

    connections.set(connectionId, client)
    res.json({ 
      connectionId, 
      success: true, 
      message: 'Connected successfully' 
    })
  })

  client.on('error', (err) => {
    res.status(400).json({ 
      connectionId: null,
      success: false, 
      message: `Connection failed: ${err.message}` 
    })
  })

  client.connect({
    host,
    port,
    username,
    password,
    readyTimeout: 10000,
  })
})

// POST /ssh/exec - with PTY support for full terminal capture
app.post('/ssh/exec', async (req, res) => {
  const { connectionId, command, timeout = 30000, usePTY = true } = req.body
  const client = connections.get(connectionId)

  if (!client) {
    return res.status(404).json({ 
      success: false, 
      error: 'Connection not found' 
    })
  }

  let output = ''
  let stderr = ''
  
  const timeoutHandle = setTimeout(() => {
    res.json({
      output: output + '\n[Command timed out]',
      exitCode: 124,
      success: false,
      duration: timeout,
    })
  }, timeout)

  if (usePTY) {
    // Use PTY mode for full terminal emulation with ANSI codes
    client.exec(command, {
      pty: {
        term: 'xterm-256color',
        cols: 120,
        rows: 40,
      }
    }, (err, stream) => {
      if (err) {
        clearTimeout(timeoutHandle)
        return res.status(500).json({ 
          success: false, 
          error: err.message 
        })
      }

      stream.on('data', (data: Buffer) => {
        output += data.toString() // Includes ANSI codes and prompts
      })

      stream.on('close', (code: number) => {
        clearTimeout(timeoutHandle)
        res.json({
          output, // Full terminal output with ANSI
          exitCode: code || 0,
          success: (code || 0) === 0,
          duration: Date.now() % timeout,
        })
      })
    })
  } else {
    // Legacy mode without PTY
    client.exec(command, (err, stream) => {
      if (err) {
        clearTimeout(timeoutHandle)
        return res.status(500).json({ 
          success: false, 
          error: err.message 
        })
      }

      stream.on('data', (data: Buffer) => {
        output += data.toString()
      })

      stream.stderr.on('data', (data: Buffer) => {
        stderr += data.toString()
      })

      stream.on('close', (code: number) => {
        clearTimeout(timeoutHandle)
        res.json({
          output: output || stderr,
          exitCode: code,
          success: code === 0,
          duration: Date.now() % timeout,
        })
      })
    })
  }
})

// POST /ssh/disconnect
app.post('/ssh/disconnect', (req, res) => {
  const { connectionId } = req.body
  const client = connections.get(connectionId)

  if (client) {
    client.end()
    connections.delete(connectionId)
    res.json({ success: true, message: 'Disconnected' })
  } else {
    res.status(404).json({ success: false, message: 'Connection not found' })
  }
})

// GET /ssh/health
// POST /agent/run
app.post('/agent/run', async (req, res) => {
  const { runId, modelName, startLevel, endLevel, apiKey } = req.body

  if (!runId || !modelName || !apiKey) {
    return res.status(400).json({ error: 'Missing required parameters' })
  }

  try {
    // Set headers for Server-Sent Events / JSONL streaming
    res.setHeader('Content-Type', 'application/x-ndjson')
    res.setHeader('Cache-Control', 'no-cache')
    res.setHeader('Connection', 'keep-alive')

    // Import and create agent
    const { BanditAgent } = await import('./agent.js')
    
    const agent = new BanditAgent({
      runId,
      modelName,
      apiKey,
      startLevel: startLevel || 0,
      endLevel: endLevel || 33,
      responseSender: res,
    })

    // Run agent (it will stream events to response)
    await agent.run({
      runId,
      currentLevel: startLevel || 0,
      targetLevel: endLevel || 33,
      currentPassword: startLevel === 0 ? 'bandit0' : '',
      nextPassword: null,
      levelGoal: '', // Will be set by agent
      status: 'planning',
      retryCount: 0,
      maxRetries: 3,
      sshConnectionId: null,
      error: null,
    })
  } catch (error) {
    console.error('Agent run error:', error)
    if (!res.headersSent) {
      res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' })
    }
  }
})

app.get('/ssh/health', (req, res) => {
  res.json({ 
    status: 'ok', 
    activeConnections: connections.size 
  })
})

const PORT = process.env.PORT || 3001
app.listen(PORT, () => {
  console.log(`SSH Proxy + LangGraph Agent running on port ${PORT}`)
})