/**
 * tts-voice skill
 * 文字轉語音：透過本機 LuxTTS API 合成語音
 *
 * 依賴：
 *   - LuxTTS server running on port 7860 (systemd user service)
 *   - ~/LuxTTS/.env (LUXTTS_USER, LUXTTS_PASS)
 *   - curl CLI
 */

import { spawnSync } from 'child_process';
import { readFileSync, existsSync, unlinkSync } from 'fs';

const LUXTTS_BASE = 'http://localhost:7860';
const REF_AUDIO = '/home/selig/LuxTTS/ref_speech.wav';
const ENV_PATH = '/home/selig/LuxTTS/.env';
const COOKIE_JAR = '/tmp/luxtts-skill-cookie';

// Trigger keywords to strip from user message
const TRIGGER_WORDS = [
  '文字轉語音', '語音合成', '唸出來', '說出來', '轉語音',
  'tts', 'voice',
];

// Speed/quality modifiers
const MODIFIERS: Record<string, Partial<TtsParams>> = {
  '慢速': { speed: 0.8 },
  'slow': { speed: 0.8 },
  '快速': { speed: 1.3 },
  'fast': { speed: 1.3 },
  '高品質': { numSteps: 6, tShift: 0.95 },
  'hq': { numSteps: 6, tShift: 0.95 },
};

interface TtsParams {
  speed: number;
  numSteps: number;
  tShift: number;
}

/** Read credentials from .env */
function loadCredentials(): { user: string; pass: string } {
  try {
    const content = readFileSync(ENV_PATH, 'utf-8');
    const user = content.match(/^LUXTTS_USER=(.+)$/m)?.[1]?.trim() || 'admin';
    const pass = content.match(/^LUXTTS_PASS=(.+)$/m)?.[1]?.trim() || '';
    return { user, pass };
  } catch {
    return { user: 'admin', pass: '' };
  }
}

/** Ensure we have a valid session cookie */
function ensureCookie(): boolean {
  const { user, pass } = loadCredentials();
  if (!pass) return false;

  try {
    const result = spawnSync(
      'curl',
      [
        '-s',
        '-o', '/dev/null',
        '-w', '%{http_code}',
        '-c', COOKIE_JAR,
        '-X', 'POST',
        '-d', `username=${user}&password=${pass}`,
        `${LUXTTS_BASE}/luxtts/login`,
      ],
      { timeout: 10000, encoding: 'utf-8' }
    );

    const httpCode = (result.stdout || '').trim();
    return result.status === 0 && httpCode === '200' && existsSync(COOKIE_JAR);
  } catch {
    return false;
  }
}

/** Check if LuxTTS service is alive */
function healthCheck(): boolean {
  try {
    const result = spawnSync(
      'curl',
      ['-s', '-o', '/dev/null', '-w', '%{http_code}', `${LUXTTS_BASE}/luxtts/api/health`],
      { timeout: 5000, encoding: 'utf-8' }
    );

    const httpCode = (result.stdout || '').trim();
    return result.status === 0 && httpCode === '200';
  } catch {
    return false;
  }
}

/** Extract text and modifiers from user message */
function parseMessage(message: string): { text: string; params: TtsParams } {
  let cleaned = message;
  const params: TtsParams = { speed: 1.0, numSteps: 4, tShift: 0.9 };

  // Remove trigger words
  for (const trigger of TRIGGER_WORDS) {
    const re = new RegExp(trigger, 'gi');
    cleaned = cleaned.replace(re, '');
  }

  // Extract and apply modifiers
  for (const [mod, overrides] of Object.entries(MODIFIERS)) {
    const re = new RegExp(mod, 'gi');
    if (re.test(cleaned)) {
      cleaned = cleaned.replace(re, '');
      Object.assign(params, overrides);
    }
  }

  // Clean up
  cleaned = cleaned.replace(/^[\s：:，,、]+/, '').replace(/[\s：:，,、]+$/, '').trim();

  return { text: cleaned, params };
}

/** Call LuxTTS API to generate speech */
function generateSpeech(text: string, params: TtsParams): string | null {
  const timestamp = Date.now();
  const outPath = `/tmp/tts_output_${timestamp}.wav`;

  try {
    const args = [
      '-s',
      '-o', outPath,
      '-w', '%{http_code}',
      '-b', COOKIE_JAR,
      '-X', 'POST',
      `${LUXTTS_BASE}/luxtts/api/tts`,
      '-F', `ref_audio=@${REF_AUDIO}`,
      '-F', `text=${text}`,
      '-F', `num_steps=${params.numSteps}`,
      '-F', `t_shift=${params.tShift}`,
      '-F', `speed=${params.speed}`,
    ];

    const result = spawnSync('curl', args, {
      timeout: 120000, // 2 min timeout for CPU synthesis
      encoding: 'utf-8',
    });

    const httpCode = (result.stdout || '').trim();

    if (result.status === 0 && httpCode === '200' && existsSync(outPath)) {
      return outPath;
    }

    // Clean up failed output
    if (existsSync(outPath)) unlinkSync(outPath);
    return null;
  } catch {
    if (existsSync(outPath)) unlinkSync(outPath);
    return null;
  }
}

// ─── 主 handler ─────────────────────────────────────────────────────────────

export async function handler(ctx: any) {
  const message = ctx.message?.text || ctx.message?.content || '';

  if (!message.trim()) {
    return { reply: '請提供要合成的文字，例如：「tts 你好，歡迎來到我的頻道」' };
  }

  // Parse user input
  const { text, params } = parseMessage(message);

  if (!text) {
    return { reply: '請提供要合成的文字，例如：「tts 你好，歡迎來到我的頻道」' };
  }

  // Health check
  if (!healthCheck()) {
    return { reply: '❌ LuxTTS 服務未啟動，請先執行：`systemctl --user start luxtts`' };
  }

  // Ensure authentication
  if (!ensureCookie()) {
    return { reply: '❌ LuxTTS 認證失敗，請檢查 ~/LuxTTS/.env 的帳密設定' };
  }

  // Build parameter description
  const paramDesc: string[] = [];
  if (params.speed !== 1.0) paramDesc.push(`語速 ${params.speed}x`);
  if (params.numSteps !== 4) paramDesc.push(`步數 ${params.numSteps}`);
  const paramStr = paramDesc.length ? `（${paramDesc.join('、')}）` : '';

  // Generate
  const wavPath = generateSpeech(text, params);

  if (!wavPath) {
    return {
      reply: `❌ 語音合成失敗，請稍後再試。`,
      metadata: { text, params, error: true },
    };
  }

  return {
    reply: `🔊 語音合成完成${paramStr}\n\n📝 文字：${text}\n📂 檔案：\`${wavPath}\``,
    metadata: {
      text,
      params,
      output: wavPath,
    },
    files: [wavPath],
  };
}
