import { Client } from 'ssh2'
import type { IntegrationAdapter, Resource } from './types.js'

const PROBE_CMD =
  'echo HOSTNAME:$(hostname); echo DISK:$(df -P / | awk \'NR==2{print $5}\' | tr -d \'%\'); echo MEM:$(free | awk \'/Mem:/{printf "%.0f", $3/$2*100}\'); echo LOAD:$(cut -d\' \' -f1 /proc/loadavg)'

function connect(config: Record<string, string>, secrets: Record<string, string>): Promise<Client> {
  return new Promise((resolve, reject) => {
    const conn = new Client()
    conn.on('ready', () => resolve(conn))
    conn.on('error', (err) => reject(err))
    conn.connect({
      host: config.host,
      port: Number(config.port) || 22,
      username: config.username,
      password: secrets.password || undefined,
      privateKey: secrets.privateKey || undefined,
      passphrase: secrets.passphrase || undefined,
      readyTimeout: 8000,
    })
  })
}

function exec(conn: Client, command: string): Promise<string> {
  return new Promise((resolve, reject) => {
    conn.exec(command, (err, stream) => {
      if (err) return reject(err)
      let output = ''
      stream.on('data', (chunk: Buffer) => { output += chunk.toString() })
      stream.stderr.on('data', () => {})
      stream.on('close', () => resolve(output))
      stream.on('error', reject)
    })
  })
}

function requireConfig(config: Record<string, string>, secrets: Record<string, string>): string | null {
  if (!config.host) return 'Missing host'
  if (!config.username) return 'Missing username'
  if (!secrets.password && !secrets.privateKey) return 'Missing password or private key'
  return null
}

export const ssh: IntegrationAdapter = {
  async testConnection(config, secrets) {
    const missing = requireConfig(config, secrets)
    if (missing) return { ok: false, message: missing }
    try {
      const conn = await connect(config, secrets)
      conn.end()
      return { ok: true, message: 'Connected' }
    } catch (err) {
      return { ok: false, message: err instanceof Error ? err.message : 'Connection failed' }
    }
  },

  async listResources(config, secrets): Promise<Resource[]> {
    if (requireConfig(config, secrets)) return []
    let conn: Client
    try {
      conn = await connect(config, secrets)
    } catch {
      return []
    }
    try {
      const output = await exec(conn, PROBE_CMD)
      const hostname = output.match(/HOSTNAME:(.+)/)?.[1]?.trim() || config.host
      const disk = Number(output.match(/DISK:(\d+)/)?.[1])
      const mem = Number(output.match(/MEM:(\d+)/)?.[1])
      const load = output.match(/LOAD:([\d.]+)/)?.[1]

      const parts: string[] = []
      if (!Number.isNaN(disk)) parts.push(`Disk ${disk}%`)
      if (!Number.isNaN(mem)) parts.push(`Mem ${mem}%`)
      if (load) parts.push(`Load ${load}`)

      const critical = disk >= 90 || mem >= 90
      const warning = disk >= 75 || mem >= 75

      return [
        {
          name: hostname,
          status: critical ? 'critical' : warning ? 'warning' : 'healthy',
          detail: parts.join(' · ') || undefined,
        },
      ]
    } catch {
      return []
    } finally {
      conn.end()
    }
  },
}
