import express from 'express';
import multer from 'multer';
import path from 'path';
import os from 'os';
import { promises as fs } from 'fs';
import { isWhisperAvailable, transcribeLocal } from '../services/whisperService.js';

const router = express.Router();
const upload = multer({ storage: multer.memoryStorage(), limits: { fileSize: 25 * 1024 * 1024 } }); // 25MB max

/**
 * Helper: Get user's OpenAI key (BYOK) or server env key.
 * Imported lazily to avoid circular deps.
 */
async function getOpenAIKey(userId) {
  try {
    const { credentialsDb } = await import('../database/db.js');
    const creds = await credentialsDb.getCredentials(userId, 'openai_key');
    const active = creds.find(c => c.is_active);
    return active?.credential_value || process.env.OPENAI_API_KEY || null;
  } catch {
    return process.env.OPENAI_API_KEY || null;
  }
}

/**
 * Transcribe audio via self-hosted Whisper (WHISPER_URL) or OpenAI API.
 * Returns { text, source } or null if all methods fail.
 */
async function transcribeAudio(fileBuffer, originalname, mimetype, userId) {
  const FormData = (await import('form-data')).default;

  // Strategy 1: Self-hosted Whisper
  const whisperUrl = process.env.WHISPER_URL;
  if (whisperUrl) {
    try {
      const formData = new FormData();
      formData.append('file', fileBuffer, {
        filename: originalname || 'audio.webm',
        contentType: mimetype || 'audio/webm'
      });
      formData.append('model', 'whisper-1');
      formData.append('response_format', 'json');
      formData.append('language', 'en');

      const endpoint = whisperUrl.replace(/\/+$/, '') + '/v1/audio/transcriptions';
      const response = await fetch(endpoint, {
        method: 'POST',
        headers: formData.getHeaders(),
        body: formData
      });

      if (response.ok) {
        const data = await response.json();
        if (data.text) return { text: data.text, source: 'self-hosted' };
      }
    } catch {
      // Self-hosted failed, try next
    }
  }

  // Strategy 2: OpenAI Whisper API (BYOK or server key)
  const apiKey = await getOpenAIKey(userId);
  if (apiKey) {
    try {
      const formData = new FormData();
      formData.append('file', fileBuffer, {
        filename: originalname || 'audio.webm',
        contentType: mimetype || 'audio/webm'
      });
      formData.append('model', 'whisper-1');
      formData.append('response_format', 'json');
      formData.append('language', 'en');

      const response = await fetch('https://api.openai.com/v1/audio/transcriptions', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${apiKey}`,
          ...formData.getHeaders()
        },
        body: formData
      });

      if (response.ok) {
        const data = await response.json();
        if (data.text) return { text: data.text, source: 'openai' };
      }
    } catch {
      // OpenAI failed, try next
    }
  }

  // Strategy 3: Local nodejs-whisper fallback
  const localAvailable = await isWhisperAvailable();
  if (localAvailable) {
    const tmpFile = path.join(os.tmpdir(), `stt-${Date.now()}.webm`);
    await fs.writeFile(tmpFile, fileBuffer);
    try {
      const text = await transcribeLocal(tmpFile);
      if (text) return { text, source: 'local' };
    } finally {
      await fs.unlink(tmpFile).catch(() => {});
    }
  }

  return null;
}

/**
 * POST /api/voice/stt — Speech-to-text
 * Priority: 1) Self-hosted Whisper (WHISPER_URL), 2) OpenAI API, 3) Local nodejs-whisper
 */
router.post('/stt', upload.single('audio'), async (req, res) => {
  try {
    if (!req.file) {
      return res.status(400).json({ error: 'No audio file provided' });
    }

    const mode = req.body?.mode || 'default';
    const result = await transcribeAudio(req.file.buffer, req.file.originalname, req.file.mimetype, req.user?.id);

    if (!result) {
      return res.status(500).json({ error: 'Speech recognition is temporarily unavailable. Please try again.' });
    }

    let text = result.text;

    // Enhancement modes (prompt, vibe, architect)
    if (text && mode !== 'default') {
      const apiKey = await getOpenAIKey(req.user?.id);
      if (apiKey) {
        text = await enhanceTranscription(text, mode, apiKey);
      }
    }

    return res.json({ text, source: result.source });
  } catch (error) {
    res.status(500).json({ error: 'Transcription failed' });
  }
});

/**
 * POST /api/voice/tts — Text-to-speech using Edge-TTS
 * Returns audio/mp3 blob
 */
router.post('/tts', async (req, res) => {
  try {
    const { text, voice = 'en-US-AriaNeural' } = req.body;
    if (!text || typeof text !== 'string') {
      return res.status(400).json({ error: 'Text is required' });
    }

    // Truncate very long text to avoid excessive audio generation
    const truncated = text.length > 5000 ? text.slice(0, 5000) : text;

    const { EdgeTTS } = await import('edge-tts-universal');
    const tts = new EdgeTTS(truncated, voice);
    const result = await tts.synthesize();
    const audioBuffer = Buffer.from(await result.audio.arrayBuffer());

    res.setHeader('Content-Type', 'audio/mp3');
    res.setHeader('Content-Length', audioBuffer.length);
    res.send(audioBuffer);
  } catch (error) {
    res.status(500).json({ error: 'TTS synthesis failed' });
  }
});

/**
 * GET /api/voice/voices — List available TTS voices
 */
router.get('/voices', async (req, res) => {
  try {
    const { listVoices } = await import('edge-tts-universal');
    const voices = await listVoices();

    // Filter to English voices and simplify the response
    const englishVoices = voices
      .filter(v => v.Locale?.startsWith('en-'))
      .map(v => ({
        id: v.ShortName,
        name: v.FriendlyName || v.ShortName,
        locale: v.Locale,
        gender: v.Gender
      }));

    res.json({ voices: englishVoices });
  } catch (error) {
    res.status(500).json({ error: 'Failed to fetch voices' });
  }
});

/**
 * Enhance transcribed text using GPT (prompt engineering, vibe coding, etc.)
 */
async function enhanceTranscription(text, mode, apiKey) {
  try {
    const OpenAI = (await import('openai')).default;
    const openai = new OpenAI({ apiKey });

    let systemMessage, prompt, temperature = 0.7, maxTokens = 800;

    switch (mode) {
      case 'prompt':
        systemMessage = 'You are an expert prompt engineer who creates clear, detailed, and effective prompts.';
        prompt = `Transform the following rough instruction into a clear, detailed, and context-aware AI prompt.\n\nYour enhanced prompt should:\n1. Be specific and unambiguous\n2. Include relevant context and constraints\n3. Specify the desired output format\n4. Use clear, actionable language\n\nRough instruction: "${text}"\n\nEnhanced prompt:`;
        break;
      case 'vibe':
      case 'instructions':
      case 'architect':
        systemMessage = 'You are a helpful assistant that formats ideas into clear, actionable instructions for AI agents.';
        temperature = 0.5;
        prompt = `Transform the following idea into clear, well-structured instructions that an AI agent can easily understand and execute.\n\nIMPORTANT RULES:\n- Format as clear, step-by-step instructions\n- Add reasonable implementation details based on common patterns\n- Only include details directly related to what was asked\n- Do NOT add features or functionality not mentioned\n- Keep the original intent and scope intact\n\nIdea: "${text}"\n\nAgent instructions:`;
        break;
      default:
        return text;
    }

    const completion = await openai.chat.completions.create({
      model: 'gpt-4o-mini',
      messages: [
        { role: 'system', content: systemMessage },
        { role: 'user', content: prompt }
      ],
      temperature,
      max_tokens: maxTokens
    });

    return completion.choices[0]?.message?.content || text;
  } catch {
    return text; // Fallback to original on error
  }
}

export default router;
