import 'server-only'
import { spawn } from 'node:child_process'
import { db } from '@/lib/db'

export interface AISearchSessionRow {
  id: string
  query: string
  audioPath: string | null
  audioMime: string | null
  transcript: string | null
  status: 'PENDING' | 'RUNNING' | 'SUCCEEDED' | 'FAILED'
  resultItemIds: string[]
  reasoning: string | null
  errorMessage: string | null
  logTail: string | null
  startedAt: Date
  runningAt: Date | null
  finishedAt: Date | null
}

export async function getSearchSession(id: string): Promise<AISearchSessionRow | null> {
  const row = await db.aISearchSession.findUnique({ where: { id } })
  return row as AISearchSessionRow | null
}

export async function getLatestSearchSession(): Promise<AISearchSessionRow | null> {
  const row = await db.aISearchSession.findFirst({ orderBy: { startedAt: 'desc' } })
  return row as AISearchSessionRow | null
}

interface StartArgs {
  query: string
  audioPath?: string | null
  audioMime?: string | null
  triggeredById?: string
}

interface StartResult {
  sessionId: string
}

export async function startSearchSession(args: StartArgs): Promise<StartResult> {
  if (!args.query.trim() && !args.audioPath) {
    throw new Error('Provide a description or record a voice clip first.')
  }

  // Reject if another search session is already in-flight so we don't pile
  // up concurrent `claude -p` runs on the Mac.
  const inflight = await db.aISearchSession.findFirst({
    where: { status: { in: ['PENDING', 'RUNNING'] } },
  })
  if (inflight) {
    throw new Error(`A search is already ${inflight.status.toLowerCase()} (started ${inflight.startedAt.toISOString()}).`)
  }

  const session = await db.aISearchSession.create({
    data: {
      query: args.query.trim(),
      audioPath: args.audioPath ?? null,
      audioMime: args.audioMime ?? null,
      triggeredById: args.triggeredById ?? null,
    },
  })

  try {
    await launchHomeServerScript(session.id)
  } catch (err) {
    await db.aISearchSession.update({
      where: { id: session.id },
      data: {
        status: 'FAILED',
        finishedAt: new Date(),
        errorMessage: err instanceof Error ? err.message.slice(0, 500) : 'launch failed',
      },
    })
    throw err
  }

  return { sessionId: session.id }
}

// Same shape as ai-review-session.launchHomeServerScript, but uses a separate
// SSH key whose forced command on the home Mac points at run-ai-search-local.sh.
async function launchHomeServerScript(sessionId: string): Promise<void> {
  const cfg = readSshConfig()

  const args = [
    '-i', cfg.keyPath,
    '-p', String(cfg.port),
    '-o', 'BatchMode=yes',
    '-o', 'ConnectTimeout=10',
    '-o', 'StrictHostKeyChecking=accept-new',
    '-o', `UserKnownHostsFile=${cfg.knownHostsPath}`,
    `${cfg.user}@${cfg.host}`,
    sessionId,
  ]

  await new Promise<void>((resolve, reject) => {
    const child = spawn('ssh', args, { stdio: ['ignore', 'pipe', 'pipe'] })
    let stderr = ''
    const timer = setTimeout(() => {
      child.kill('SIGTERM')
      reject(new Error('ssh timed out launching ai-search script'))
    }, 15_000)

    child.stderr.on('data', chunk => {
      stderr += chunk.toString()
      if (stderr.length > 2000) stderr = stderr.slice(-2000)
    })
    child.on('error', err => {
      clearTimeout(timer)
      reject(new Error(`ssh spawn failed: ${err.message}`))
    })
    child.on('exit', code => {
      clearTimeout(timer)
      if (code === 0) resolve()
      else reject(new Error(`ssh exited ${code}: ${stderr.trim() || '(no stderr)'}`))
    })
  })
}

interface SshConfig {
  user: string
  host: string
  port: number
  keyPath: string
  knownHostsPath: string
}

function readSshConfig(): SshConfig {
  // Falls back to the identify-pass connection settings so single-key setups
  // keep working — only AI_SEARCH_HOME_SSH_KEY needs to differ if you put the
  // two forced commands on separate keys.
  return {
    user: process.env.AI_SEARCH_HOME_SSH_USER || process.env.IDENTIFY_HOME_SSH_USER || 'erictran',
    host: process.env.AI_SEARCH_HOME_SSH_HOST || process.env.IDENTIFY_HOME_SSH_HOST || '67.182.44.118',
    port: Number(process.env.AI_SEARCH_HOME_SSH_PORT || process.env.IDENTIFY_HOME_SSH_PORT || 2222),
    keyPath: process.env.AI_SEARCH_HOME_SSH_KEY || '/etc/eorganize/ai_search_to_home',
    knownHostsPath: process.env.AI_SEARCH_HOME_KNOWN_HOSTS || process.env.IDENTIFY_HOME_KNOWN_HOSTS || '/etc/eorganize/known_hosts',
  }
}
