// 语音服务进程管理：启动/停止 Python whisper_server.py
// 兼容开发模式和 Electron 打包后（asarUnpack）两种路径
import { spawn, spawnSync } from 'child_process'
import fs from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'

const __dirname = path.dirname(fileURLToPath(import.meta.url))

export const VOICE_WS_PORT = 3723

let proc = null
let status = 'stopped'  // 'stopped' | 'starting' | 'running' | 'error'
let statusMessage = ''

// 当前引擎（用于 ensureLocalVoiceServer 判断 engine 变更是否需要重启）
let activeEngine = 'whisper'

// Windows 上 `python` 可能是 Microsoft Store 的假占位符（无输出、退出码 49），
// 真实解释器用 py launcher 探测；开发/打包两端统一走这个解析。
let _pyCmd = null
export function resolvePython() {
  if (_pyCmd) return _pyCmd
  if (process.platform === 'win32') {
    const r = spawnSync('py', ['-3', '--version'], {
      windowsHide: true, encoding: 'utf-8', timeout: 5000,
    })
    if (!r.error && r.status === 0) {
      _pyCmd = { cmd: 'py', prefixArgs: ['-3'] }
      return _pyCmd
    }
  }
  _pyCmd = { cmd: process.platform === 'win32' ? 'python' : 'python3', prefixArgs: [] }
  return _pyCmd
}

// 本地模型目录解析（kind: 'sense-voice' | 'piper' | 'espeak-ng-data'）
// 与 resolveServer 同构的双模式：
//   打包模式 → resources/voice/models/<kind>（extraResources）或 app.asar.unpacked/src/voice/models/<kind>
//   开发模式 → src/voice/models/<kind>
export function resolveLocalModelDir(kind) {
  const resourcesDir = process.env.BAILONGMA_RESOURCES_DIR
  if (resourcesDir && resourcesDir.endsWith('.asar')) {
    const resourcesPath = path.dirname(resourcesDir)
    const extra = path.join(resourcesPath, 'voice', 'models', kind)
    if (fs.existsSync(extra)) return extra
    const unpacked = path.join(
      resourcesDir.replace(/\.asar$/, '.asar.unpacked'),
      'src', 'voice', 'models', kind
    )
    if (fs.existsSync(unpacked)) return unpacked
  }
  return path.join(__dirname, 'models', kind)
}

// 解析语音服务的启动方式：
//   打包模式 → 优先用 extraResources 中的 whisper_server.exe（无需 Python）
//   开发模式 → 用 Python + whisper_server.py
function resolveServer() {
  const resourcesDir = process.env.BAILONGMA_RESOURCES_DIR
  if (resourcesDir && resourcesDir.endsWith('.asar')) {
    // 打包后 extraResources 落在 app.asar 的上一级目录（resources/）
    const resourcesPath = path.dirname(resourcesDir)
    const exe = path.join(resourcesPath, 'voice', 'whisper_server.exe')
    if (fs.existsSync(exe)) return { mode: 'exe', path: exe }

    // 兜底：.py 在 asar.unpacked 里（仍需用户安装 Python）
    const py = path.join(
      resourcesDir.replace(/\.asar$/, '.asar.unpacked'),
      'src', 'voice', 'whisper_server.py'
    )
    if (fs.existsSync(py)) return { mode: 'python', path: py }
  }
  // 开发模式
  return { mode: 'python', path: path.join(__dirname, 'whisper_server.py') }
}

export function getVoiceStatus() {
  return { status, message: statusMessage, port: VOICE_WS_PORT, pid: proc?.pid ?? null }
}

export function startVoiceServer({ model = 'small', engine = 'whisper', modelDir = null } = {}) {
  if (proc) return getVoiceStatus()

  const server = resolveServer()

  if (server.mode !== 'exe' && !fs.existsSync(server.path)) {
    status = 'error'
    statusMessage = `找不到语音服务脚本: ${server.path}`
    console.error(`[Voice] ${statusMessage}`)
    return getVoiceStatus()
  }

  // SenseVoice 模式必须能找到模型目录，否则直接报错（面板可见）
  if (engine === 'sensevoice') {
    const resolved = modelDir || resolveLocalModelDir('sense-voice')
    if (!fs.existsSync(path.join(resolved, 'model.int8.onnx')) || !fs.existsSync(path.join(resolved, 'tokens.txt'))) {
      status = 'error'
      statusMessage = `找不到 SenseVoice 模型: ${resolved}（需 model.int8.onnx + tokens.txt）`
      console.error(`[Voice] ${statusMessage}`)
      return getVoiceStatus()
    }
  }

  activeEngine = engine
  status = 'starting'
  statusMessage = engine === 'sensevoice'
    ? '正在加载 SenseVoice 模型…'
    : `正在加载 Whisper (${model})…`

  const spawnArgs = ['--model', model, '--port', String(VOICE_WS_PORT), '--engine', engine]
  if (engine === 'sensevoice') {
    spawnArgs.push('--model-dir', modelDir || resolveLocalModelDir('sense-voice'))
  }
  const { cmd, prefixArgs } = resolvePython()
  if (server.mode === 'exe') {
    console.log(`[Voice] 启动语音服务 (exe): ${server.path} --engine ${engine}`)
    proc = spawn(server.path, spawnArgs, {
      stdio: ['ignore', 'pipe', 'pipe'],
      windowsHide: true,
      env: { ...process.env, PYTHONIOENCODING: 'utf-8', PYTHONUTF8: '1' },
    })
  } else {
    console.log(`[Voice] 启动语音服务 (python): ${cmd} ${prefixArgs.join(' ')} ${server.path} --engine ${engine}`)
    proc = spawn(cmd, [...prefixArgs, server.path, ...spawnArgs], {
      stdio: ['ignore', 'pipe', 'pipe'],
      windowsHide: true,
      env: { ...process.env, PYTHONIOENCODING: 'utf-8', PYTHONUTF8: '1' },
    })
  }

  proc.stdout.on('data', (data) => {
    for (const line of data.toString('utf8').split('\n')) {
      const trimmed = line.trim()
      if (!trimmed) continue
      console.log(`[Voice] ${trimmed}`)
      // 同时检测中文和 ASCII 部分，避免 Windows 编码导致中文匹配失败
      if (trimmed.includes('WebSocket 服务启动') || trimmed.includes('ws://')) {
        status = 'running'
        statusMessage = `运行中 (port ${VOICE_WS_PORT})`
      } else if (trimmed.includes('加载') || trimmed.includes('load')) {
        statusMessage = trimmed.replace('[语音] ', '')
      }
    }
  })

  proc.stderr.on('data', (data) => {
    const text = data.toString().trim()
    if (text) console.error(`[Voice] ${text}`)
  })

  proc.on('exit', (code, signal) => {
    console.log(`[Voice] 进程退出: code=${code} signal=${signal}`)
    proc = null
    status = code === 0 ? 'stopped' : 'error'
    statusMessage = code === 0 ? '已停止' : `异常退出 (code ${code})`
  })

  proc.on('error', (err) => {
    console.error('[Voice] 无法启动语音服务:', err.message)
    proc = null
    status = 'error'
    statusMessage = `语音服务启动失败: ${err.message}`
  })

  return getVoiceStatus()
}

export function stopVoiceServer() {
  if (!proc) return getVoiceStatus()
  try { proc.kill('SIGTERM') } catch {}
  proc = null
  status = 'stopped'
  statusMessage = '已停止'
  return getVoiceStatus()
}

export function restartVoiceServer({ model, engine, modelDir } = {}) {
  stopVoiceServer()
  // 给进程一点时间完全退出，再用新模型启动
  setTimeout(() => startVoiceServer({ model, engine, modelDir }), 500)
  return getVoiceStatus()
}

// 按需启动：无进程 → start；engine 变了 → restart；相同 → 直接返回状态
export function ensureLocalVoiceServer({ engine = 'sensevoice', modelDir = null } = {}) {
  if (!proc) {
    return startVoiceServer({ model: 'small', engine, modelDir })
  }
  if (activeEngine !== engine) {
    return restartVoiceServer({ model: 'small', engine, modelDir })
  }
  return getVoiceStatus()
}

// 轮询等待语音服务就绪（Python 先加载模型后起 WS —— 等端口即等模型）
export function waitForVoiceReady(timeoutMs = 90000) {
  const deadline = Date.now() + timeoutMs
  return new Promise((resolve) => {
    const check = () => {
      const s = getVoiceStatus()
      if (s.status === 'running') return resolve(true)
      if (s.status === 'error') return resolve(false)
      if (Date.now() >= deadline) return resolve(false)
      setTimeout(check, 300)
    }
    check()
  })
}
