/**
 * Whisper Service - Singleton persistent GPU speech-to-text server
 * Auto-starts with the system, auto-restarts on crash.
 * Single instance processes all client requests.
 */

import { join } from 'path'
import { Subprocess } from 'bun'

const WHISPER_PORT = 4104
const WHISPER_SCRIPT = join(import.meta.dir, '..', 'whisper_server.py')
const RESTART_DELAY_MS = 3000  // Wait before auto-restart after crash

interface WhisperState {
  enabled: boolean
  running: boolean
  starting: boolean
  process: Subprocess | null
  model: string
  device: string
}

const state: WhisperState = {
  enabled: true,   // Always enabled by default
  running: false,
  starting: false,
  process: null,
  model: 'large-v3',
  device: 'cuda'
}

/**
 * Kill any process using the Whisper port
 */
async function killProcessOnPort(port: number): Promise<void> {
  try {
    const proc = Bun.spawn(['powershell', '-Command',
      `Get-NetTCPConnection -LocalPort ${port} -ErrorAction SilentlyContinue | ForEach-Object { Stop-Process -Id $_.OwningProcess -Force -ErrorAction SilentlyContinue }`
    ], { stdout: 'ignore', stderr: 'ignore' })
    await proc.exited
    await new Promise(resolve => setTimeout(resolve, 1000))
  } catch {
    // Ignore errors
  }
}

/**
 * Check if Whisper WebSocket is ready using PowerShell
 */
async function checkPort(port: number): Promise<boolean> {
  try {
    const proc = Bun.spawn(['powershell', '-NoProfile', '-Command',
      `$c = Get-NetTCPConnection -LocalPort ${port} -State Listen -ErrorAction SilentlyContinue; if ($c) { Write-Output 'LISTENING' }`
    ], {
      stdout: 'pipe',
      stderr: 'ignore'
    })

    const output = await new Response(proc.stdout).text()
    await proc.exited

    return output.trim() === 'LISTENING'
  } catch {
    return false
  }
}

/**
 * Monitor the Whisper process and auto-restart on crash
 */
function monitorProcess(proc: Subprocess) {
  proc.exited.then((exitCode) => {
    console.error(`[Whisper] Process exited with code ${exitCode}`)
    state.process = null
    state.running = false
    state.starting = false

    // Auto-restart after delay
    console.log(`[Whisper] Auto-restarting in ${RESTART_DELAY_MS / 1000}s...`)
    setTimeout(() => {
      startWhisperServer().catch(err => {
        console.error('[Whisper] Auto-restart failed:', err)
      })
    }, RESTART_DELAY_MS)
  })
}

/**
 * Start the Whisper Python server (singleton - only one instance)
 */
export async function startWhisperServer(): Promise<boolean> {
  // Prevent multiple simultaneous start attempts
  if (state.starting) {
    console.log('[Whisper] Already starting, skipping')
    return false
  }

  // Already running
  if (state.running && state.process) {
    console.log('[Whisper] Already running')
    return true
  }

  // Check if an external instance is already listening
  const alreadyListening = await checkPort(WHISPER_PORT)
  if (alreadyListening) {
    console.log('[Whisper] External instance already running on port', WHISPER_PORT)
    state.running = true
    state.enabled = true
    return true
  }

  state.starting = true
  console.log(`[Whisper] Starting singleton server (${state.model})...`)

  // Kill any orphan process on the port
  await killProcessOnPort(WHISPER_PORT)

  try {
    const proc = Bun.spawn(['python', '-u', WHISPER_SCRIPT], {
      cwd: join(import.meta.dir, '..'),
      stdout: 'inherit',
      stderr: 'inherit',
      env: { ...process.env, PYTHONUNBUFFERED: '1' }
    })

    state.process = proc

    // Monitor for crashes and auto-restart
    monitorProcess(proc)

    // Wait for initial startup
    await new Promise(resolve => setTimeout(resolve, 2000))

    // Check if process died immediately
    if (proc.exitCode !== null) {
      console.error('[Whisper] Process exited immediately with code:', proc.exitCode)
      state.process = null
      state.starting = false
      return false
    }

    // Check if WebSocket is ready
    const isListening = await checkPort(WHISPER_PORT)
    if (isListening) {
      console.log('[Whisper] Server ready (GPU)')
      state.running = true
      state.enabled = true
      state.starting = false
      return true
    }

    // Wait for model loading (up to 120 seconds for large-v3)
    for (let i = 0; i < 40; i++) {
      await new Promise(resolve => setTimeout(resolve, 3000))

      if (proc.exitCode !== null) {
        console.error('[Whisper] Process died during model loading')
        state.process = null
        state.starting = false
        return false
      }

      const ready = await checkPort(WHISPER_PORT)
      if (ready) {
        console.log('[Whisper] Server ready (GPU)')
        state.running = true
        state.enabled = true
        state.starting = false
        return true
      }
    }

    console.error('[Whisper] Timeout waiting for server (120s)')
    state.starting = false
    return false

  } catch (err: any) {
    console.error('[Whisper] Start error:', err.message)
    state.process = null
    state.starting = false
    return false
  }
}

/**
 * Stop the Whisper server (only for manual override, not used in normal flow)
 */
export function stopWhisperServer(): boolean {
  if (!state.process) {
    return true
  }

  try {
    state.process.kill()
    state.process = null
    state.running = false
    console.log('[Whisper] Stopped manually')
    return true
  } catch (err) {
    console.error('[Whisper] Stop error:', err)
    return false
  }
}

/**
 * Toggle is now a no-op for stop - Whisper always stays on.
 * If not running, triggers a start.
 */
export async function toggleWhisperServer(): Promise<{ enabled: boolean; success: boolean; starting: boolean }> {
  if (state.starting) {
    return { enabled: true, success: false, starting: true }
  }

  if (state.running) {
    // Already running - just confirm it's on
    return { enabled: true, success: true, starting: false }
  }

  // Not running - start it
  startWhisperServer().catch(err => {
    console.error('[Whisper] Start error:', err)
    state.starting = false
  })

  return { enabled: true, success: true, starting: true }
}

/**
 * Get current Whisper state (checks real port status)
 */
export async function getWhisperState(): Promise<{
  enabled: boolean
  running: boolean
  starting: boolean
  port: number
  model: string
  device: string
}> {
  // Check if port is actually listening (skip if starting to avoid interference)
  if (!state.starting) {
    const isListening = await checkPort(WHISPER_PORT)

    // Sync state with reality
    if (isListening && !state.running) {
      state.running = true
      state.enabled = true
    } else if (!isListening && state.running) {
      state.running = false
      // Keep enabled=true since we auto-restart
    }
  }

  return {
    enabled: true,  // Always enabled
    running: state.running,
    starting: state.starting,
    port: WHISPER_PORT,
    model: state.model,
    device: state.device
  }
}

/**
 * Check if Whisper is running
 */
export function isWhisperEnabled(): boolean {
  return state.running
}

export function getWhisperPort(): number {
  return WHISPER_PORT
}
