import { Client, type ConnectConfig } from 'ssh2'
import { db } from '../db/index.js'
import { loadSecrets } from '../db/secrets.js'

interface IntegrationRow {
  id: number
  type: string
  config_json: string
}

export function loadSshHost(integrationId: number) {
  const row = db
    .prepare('SELECT id, type, config_json FROM integrations WHERE id = ?')
    .get(integrationId) as IntegrationRow | undefined
  if (!row || row.type !== 'ssh') return null
  const config = JSON.parse(row.config_json) as Record<string, string>
  const secrets = loadSecrets(row.id)
  return { id: row.id, config, secrets }
}

export type SshHost = NonNullable<ReturnType<typeof loadSshHost>>

function makeHostVerifier(integrationId: number): ConnectConfig['hostVerifier'] {
  return (keyHash: string): boolean => {
    const row = db
      .prepare('SELECT fingerprint FROM ssh_host_keys WHERE integration_id = ?')
      .get(integrationId) as { fingerprint: string } | undefined
    if (!row) {
      db.prepare('INSERT INTO ssh_host_keys (integration_id, fingerprint) VALUES (?, ?)').run(integrationId, keyHash)
      return true
    }
    return row.fingerprint === keyHash
  }
}

export function baseConnectConfig(host: SshHost): ConnectConfig {
  return {
    host: host.config.host,
    port: Number(host.config.port) || 22,
    username: host.config.username,
    password: host.secrets.password || undefined,
    privateKey: host.secrets.privateKey || undefined,
    passphrase: host.secrets.passphrase || undefined,
    readyTimeout: 8000,
    hostHash: 'sha256',
    hostVerifier: makeHostVerifier(host.id),
  }
}

/** Connects to `target`, transparently chaining through its jump host (if configured) via forwardOut(). */
export function connectTarget(
  target: SshHost,
  onReady: (client: Client) => void,
  onError: (message: string) => void,
): { conn: Client; jumpConn: Client | null } {
  const jumpHostId = target.config.jumpHostIntegrationId ? Number(target.config.jumpHostIntegrationId) : null

  if (jumpHostId) {
    const jumpHost = loadSshHost(jumpHostId)
    if (!jumpHost) {
      onError('Jump host integration not found')
      return { conn: new Client(), jumpConn: null }
    }
    const jumpConn = new Client()
    jumpConn.on('ready', () => {
      jumpConn.forwardOut('127.0.0.1', 0, target.config.host, Number(target.config.port) || 22, (err, sock) => {
        if (err) {
          onError(`Jump host forward failed: ${err.message}`)
          return
        }
        client.connect({ ...baseConnectConfig(target), sock })
      })
    })
    jumpConn.on('error', (err) => onError(`Jump host error: ${err.message}`))
    const client = new Client()
    client.on('ready', () => onReady(client))
    client.on('error', (err) => onError(err.message))
    jumpConn.connect(baseConnectConfig(jumpHost))
    return { conn: client, jumpConn }
  }

  const client = new Client()
  client.on('ready', () => onReady(client))
  client.on('error', (err) => onError(err.message))
  client.connect(baseConnectConfig(target))
  return { conn: client, jumpConn: null }
}
