// ============================================================
// ADMIN CONFIG ROUTES — CMS for announcements, prompts, emails, flags
// ============================================================

var express = require('express');
var router = express.Router();
var db = require('../db/database');
var { authMiddleware, adminMiddleware } = require('../middleware/auth');
var PROMPTS = require('../utils/prompts');
var logger = require('../utils/logger');
var { gatewayUrl } = require('../utils/errors');
var { getTTSEnvProvider, getLiteLLMTTSDiscoveryItems, getLiteLLMTTSRequestOptions, getLiteLLMTTSVoicesForModel, isLiteLLMTTSVoiceCompatible, getTTSProvider } = require('../utils/ttsProvider');
var { getLiteLLMHeaders, getLiteLLMAdminHeaders } = require('../utils/litellm');
var { getSTTDependencies, getLiteLLMSTTModels, getSTTModelLists, getSTTProvider } = require('../utils/sttProvider');
var { getLiteLLMEmbeddingModels } = require('../utils/embeddings');

router.use(authMiddleware);

function liteLLMBaseUrl() {
  return (process.env.LITELLM_API_BASE || '').replace(/\/+$/, '').replace(/\/v1\/?$/, '');
}

function searchTerms(search) {
  return String(search || '').toLowerCase().split(/[\s,]+/).map(function(term) { return term.trim(); }).filter(Boolean);
}

function matchesDiscoverySearch(item, search, extra) {
  var terms = searchTerms(search);
  if (terms.length === 0) return true;
  var haystack = [item.id, item.name, item.source, item.kind, item.mode, item.capability, item.dims, extra].filter(Boolean).join(' ').toLowerCase();
  return terms.some(function(term) { return haystack.indexOf(term) !== -1; });
}

function liteLLMModelId(model) {
  return model && (model.model_name || model.id) ? (model.model_name || model.id) : '';
}

function liteLLMModelMode(model) {
  return model && model.model_info && model.model_info.mode ? String(model.model_info.mode) : '';
}

async function probeLiteLLMEmbeddingDimensions(modelId) {
  try {
    var axios = require('axios');
    var resp = await axios.post(gatewayUrl('/embeddings'), {
      model: modelId,
      input: 'dimension probe'
    }, { headers: getLiteLLMHeaders('application/json'), timeout: 30000 });
    var embedding = resp.data && resp.data.data && resp.data.data[0] && resp.data.data[0].embedding;
    return Array.isArray(embedding) ? embedding.length : '?';
  } catch (e) {
    logger.warn('LiteLLM embedding dimension probe failed for ' + modelId + ': ' + e.message);
    return '?';
  }
}

// ── GET announcement (any authenticated user) ──────────────────────────────
router.get('/config/announcement', async function(req, res) {
  try {
    var enabled = await db.getSetting('announcement.enabled');
    var text    = await db.getSetting('announcement.text');
    var type    = await db.getSetting('announcement.type');
    res.json({
      success: true,
      enabled: enabled === 'true',
      text:    text    || '',
      type:    type    || 'info'
    });
  } catch (e) { res.status(500).json({ error: 'Request failed' }); }
});

router.use(adminMiddleware);

// ── GET all config entries ─────────────────────────────────────────────────
router.get('/config', async function(req, res) {
  try {
    var rows = await db.all(
      "SELECT key, value, updated_at FROM app_settings ORDER BY key",
      []
    );
    // Also include current in-memory prompts (may differ if not yet in DB)
    var promptKeys = PROMPTS.getAllPrompts().map(function(p) { return p.key; });
    var inDb = new Set(rows.filter(function(r) { return r.key.startsWith('prompt.'); }).map(function(r) { return r.key; }));
    promptKeys.forEach(function(key) {
      var dbKey = 'prompt.' + key;
      if (!inDb.has(dbKey)) {
        rows.push({ key: dbKey, value: PROMPTS[key], updated_at: null });
      }
    });
    res.json({ success: true, config: rows });
  } catch (e) { res.status(500).json({ error: 'Request failed' }); }
});

// ── Clinical assistant starter prompt pool ─────────────────────────────────
router.get('/clinical-assistant/prompt-pool', async function(req, res) {
  try {
    var clinicalAssistant = require('./clinicalAssistant');
    var meta = await clinicalAssistant.getPromptPoolMeta();
    var snapshots = await clinicalAssistant.getPromptPoolSnapshots(20);
    res.json({ success: true, meta: meta || null, snapshots: snapshots });
  } catch (e) {
    res.status(500).json({ error: e.message || 'Prompt pool status failed' });
  }
});

router.get('/clinical-assistant/prompt-pool/snapshots', async function(req, res) {
  try {
    var clinicalAssistant = require('./clinicalAssistant');
    var snapshots = await clinicalAssistant.getPromptPoolSnapshots(req.query.limit || 20);
    res.json({ success: true, snapshots: snapshots });
  } catch (e) {
    res.status(500).json({ error: e.message || 'Prompt pool snapshot listing failed' });
  }
});

router.post('/clinical-assistant/prompt-pool/regenerate', async function(req, res) {
  try {
    var clinicalAssistant = require('./clinicalAssistant');
    var examples = await clinicalAssistant.refreshPromptPool(true, req.user && req.user.id);
    var meta = await clinicalAssistant.getPromptPoolMeta();
    var snapshots = await clinicalAssistant.getPromptPoolSnapshots(20);
    res.json({ success: true, count: examples.length, meta: meta || null, snapshots: snapshots });
  } catch (e) {
    res.status(500).json({ error: e.message || 'Prompt pool regeneration failed' });
  }
});

router.post('/clinical-assistant/prompt-pool/restore', async function(req, res) {
  try {
    var clinicalAssistant = require('./clinicalAssistant');
    var id = Number(req.body && req.body.id);
    if (!Number.isFinite(id) || id <= 0) return res.status(400).json({ error: 'Snapshot id is required' });
    var payload = await clinicalAssistant.restorePromptPoolSnapshot(id, req.user && req.user.id);
    if (!payload) return res.status(404).json({ error: 'Snapshot not found' });
    var meta = await clinicalAssistant.getPromptPoolMeta();
    var snapshots = await clinicalAssistant.getPromptPoolSnapshots(20);
    res.json({ success: true, count: Array.isArray(payload.examples) ? payload.examples.length : 0, meta: meta || null, snapshots: snapshots });
  } catch (e) {
    res.status(500).json({ error: e.message || 'Prompt pool restore failed' });
  }
});

// ── POST send test email ───────────────────────────────────────────────────
router.post('/config/test-email', async function(req, res) {
  try {
    var { to, template } = req.body;
    if (!to) return res.status(400).json({ error: 'Recipient email required' });

    var subjectKey = 'email.' + (template || 'verify') + '.subject';
    var bodyKey    = 'email.' + (template || 'verify') + '.body';

    var subject = await db.getSetting(subjectKey) || 'Test email from Pediatric AI Scribe';
    var bodyText = await db.getSetting(bodyKey)   || 'This is a test email.';

    var sendEmail = require('./auth').__sendEmail;
    if (!sendEmail) {
      return res.status(400).json({ error: 'Email not configured (SMTP_HOST missing)' });
    }

    var emailWrapper = require('./auth').__emailWrapper;
    var btnHtml      = require('./auth').__btnHtml;

    var html = emailWrapper(
      '<p style="margin:0 0 6px;font-size:18px;font-weight:600;color:#111827;">Test Email</p>' +
      '<p style="color:#6b7280;margin:12px 0 20px;line-height:1.6;font-size:14px;">' + bodyText.replace(/\n/g, '<br>') + '</p>' +
      btnHtml('#', 'Test Button')
    );

    var ok = await sendEmail(to, '[TEST] ' + subject, html);
    res.json({ success: ok, message: ok ? 'Test email sent to ' + to : 'SMTP not configured' });
  } catch (e) { res.status(500).json({ error: 'Request failed' }); }
});

// ── GET prompt list (for editor UI) ───────────────────────────────────────
router.get('/config/prompts', async function(req, res) {
  try {
    var prompts = PROMPTS.getAllPrompts();
    // Load any DB overrides
    var dbRows = await db.all("SELECT key, value FROM app_settings WHERE key LIKE 'prompt.%'", []);
    var dbMap = {};
    dbRows.forEach(function(r) { dbMap[r.key] = r.value; });
    prompts.forEach(function(p) {
      var dbKey = 'prompt.' + p.key;
      if (dbMap[dbKey]) p.value = dbMap[dbKey];
      p.dbKey = dbKey;
    });
    res.json({ success: true, prompts: prompts });
  } catch (e) { res.status(500).json({ error: 'Request failed' }); }
});

// ── POST reset a prompt to hardcoded default ──────────────────────────────
router.post('/config/prompts/:key/reset', async function(req, res) {
  try {
    var key = req.params.key;
    if (!Object.prototype.hasOwnProperty.call(PROMPTS, key) || typeof PROMPTS[key] !== 'string') {
      return res.status(404).json({ error: 'Prompt not found' });
    }
    // Delete DB override so hardcoded default is used
    await db.run("DELETE FROM app_settings WHERE key = ?", ['prompt.' + key]);
    // Reload from hardcoded source (re-require)
    delete require.cache[require.resolve('../utils/prompts')];
    var fresh = require('../utils/prompts');
    PROMPTS[key] = fresh[key] || PROMPTS[key];

    logger.audit(req.user.id, 'admin_config_reset', 'Reset prompt to default: ' + key, req, { category: 'admin' });
    res.json({ success: true, value: PROMPTS[key] });
  } catch (e) { res.status(500).json({ error: 'Request failed' }); }
});

// ── POST reset all non-prompt settings to defaults ──────────────────────
router.post('/config/reset-defaults', async function(req, res) {
  try {
    var defaults = [
      ['registration_enabled', 'true'],
      ['announcement.enabled', 'false'],
      ['announcement.text',    ''],
      ['announcement.type',    'info'],
      ['feature.read_aloud',   'true'],
      ['feature.nextcloud',    'true'],
      ['feature.memories',     'true'],
      ['email.verify.subject', 'Verify your Pediatric AI Scribe account'],
      ['email.verify.body',    'Thank you for signing up! Click the button below to verify your email address. This link expires in 24 hours.'],
      ['email.reset.subject',  'Reset your password — Pediatric AI Scribe'],
      ['email.reset.body',     'Someone requested a password reset for your Pediatric AI Scribe account. If that was you, click the button below to choose a new password. This link expires in 1 hour. If you did not request a password reset, no action is needed — your password has not been changed.'],
      ['site.name', 'Pediatric AI Scribe'],
      ['site.auto_delete_days', '7'],
    ];
    for (var d = 0; d < defaults.length; d++) {
      await db.setSetting(defaults[d][0], defaults[d][1]);
    }
    logger.audit(req.user.id, 'admin_config_reset_all', 'Reset all settings to defaults', req, { category: 'admin' });
    res.json({ success: true, message: 'Settings reset to defaults (SMTP and custom models preserved)' });
  } catch (e) { res.status(500).json({ error: 'Request failed' }); }
});

// ── GET SMTP status ──────────────────────────────────────────────────────
router.get('/config/smtp/status', async function(req, res) {
  try {
    var host = await db.getSetting('smtp.host') || process.env.SMTP_HOST || '';
    var user = await db.getSetting('smtp.user') || process.env.SMTP_USER || '';
    var from = await db.getSetting('smtp.from') || process.env.SMTP_FROM || process.env.SMTP_USER || '';
    var port = await db.getSetting('smtp.port') || process.env.SMTP_PORT || '587';
    // Never return password
    res.json({
      success: true,
      configured: !!host,
      host: host,
      port: port,
      user: user,
      from: from,
      source: process.env.SMTP_HOST ? 'env' : (host ? 'database' : 'none')
    });
  } catch (e) { res.status(500).json({ error: 'Request failed' }); }
});

// ── PUT update SMTP settings ─────────────────────────────────────────────
// NOTE: This must come BEFORE the wildcard PUT /config/:key(*) below
router.put('/config/smtp', async function(req, res) {
  try {
    var { host, port, user, pass, from, secure } = req.body;
    if (!host) return res.status(400).json({ error: 'SMTP host required' });

    await db.setSetting('smtp.host', host.trim());
    await db.setSetting('smtp.port', String(port || '587').trim());
    await db.setSetting('smtp.user', (user || '').trim());
    await db.setSetting('smtp.from', (from || user || '').trim());
    await db.setSetting('smtp.secure', String(secure === true || secure === 'true'));
    if (pass && pass.trim()) {
      await db.setSetting('smtp.pass', pass.trim());
    }

    logger.audit(req.user.id, 'admin_smtp_update', 'Updated SMTP settings', req, { category: 'admin' });
    res.json({ success: true });
  } catch (e) { res.status(500).json({ error: 'Request failed' }); }
});

// ── DELETE SMTP settings (clear DB override, fall back to env) ───────────
router.delete('/config/smtp', async function(req, res) {
  try {
    var keys = ['smtp.host', 'smtp.port', 'smtp.user', 'smtp.pass', 'smtp.from', 'smtp.secure'];
    for (var k of keys) {
      await db.run('DELETE FROM app_settings WHERE key = $1', [k]);
    }
    logger.audit(req.user.id, 'admin_smtp_clear', 'Cleared SMTP DB overrides', req, { category: 'admin' });
    res.json({ success: true, message: 'SMTP DB settings cleared (env vars still apply if set)' });
  } catch (e) { res.status(500).json({ error: 'Request failed' }); }
});

// ============================================================
// MODEL ROUTES — All must come BEFORE the wildcard PUT /config/:key(*)
// The wildcard uses :key(*) which matches slashes, so it would intercept
// /config/models/toggle and /config/models/default if registered after it.
// ============================================================

// ── GET models config ────────────────────────────────────────────────────
router.get('/config/models', async function(req, res) {
  try {
    var { OPENROUTER_MODELS, BEDROCK_MODELS, AZURE_MODELS, VERTEX_MODELS, activeProvider } = require('../utils/models');
    var providerModels;
    switch (activeProvider) {
      case 'bedrock':  providerModels = BEDROCK_MODELS;  break;
      case 'azure':    providerModels = AZURE_MODELS;    break;
      case 'vertex':   providerModels = VERTEX_MODELS;   break;
      case 'litellm':  providerModels = [];               break; // LiteLLM: no built-ins — use discover
      default:         providerModels = OPENROUTER_MODELS;
    }

    var disabledRaw = await db.getSetting('models.disabled') || '[]';
    var customRaw   = await db.getSetting('models.custom')   || '[]';
    var defaultModel = await db.getSetting('models.default') || '';
    var disabled, custom;
    try { disabled = JSON.parse(disabledRaw); } catch(e) { disabled = []; }
    try { custom   = JSON.parse(customRaw);   } catch(e) { custom   = []; }

    var models = providerModels.map(function(m) {
      return Object.assign({}, m, { enabled: !disabled.includes(m.id) });
    });

    res.json({
      success: true,
      provider: activeProvider,
      models: models,
      custom: custom,
      defaultModel: defaultModel,
      litellmHint: activeProvider === 'litellm'
    });
  } catch (e) { res.status(500).json({ error: 'Request failed' }); }
});

// ── PUT toggle model enabled/disabled ────────────────────────────────────
router.put('/config/models/toggle', async function(req, res) {
  try {
    var { modelId, enabled } = req.body;
    if (!modelId) return res.status(400).json({ error: 'modelId required' });

    var disabledRaw = await db.getSetting('models.disabled') || '[]';
    var disabled;
    try { disabled = JSON.parse(disabledRaw); } catch(e) { disabled = []; }

    if (enabled) {
      disabled = disabled.filter(function(id) { return id !== modelId; });
    } else {
      if (!disabled.includes(modelId)) disabled.push(modelId);
    }

    await db.setSetting('models.disabled', JSON.stringify(disabled));
    require('../utils/models').invalidateAllowedModelsCache();
    logger.audit(req.user.id, 'admin_model_toggle', (enabled ? 'Enabled' : 'Disabled') + ' model: ' + modelId, req, { category: 'admin' });
    res.json({ success: true });
  } catch (e) { res.status(500).json({ error: 'Request failed' }); }
});

// ── PUT set default model ─────────────────────────────────────────────────
router.put('/config/models/default', async function(req, res) {
  try {
    var { modelId } = req.body;
    if (!modelId) return res.status(400).json({ error: 'modelId required' });
    await db.setSetting('models.default', modelId.trim());
    require('../utils/models').invalidateAllowedModelsCache();
    logger.audit(req.user.id, 'admin_model_default', 'Set default model: ' + modelId, req, { category: 'admin' });
    res.json({ success: true });
  } catch (e) { res.status(500).json({ error: 'Request failed' }); }
});

// ── POST add custom model (manual entry) ─────────────────────────────────
router.post('/config/models/custom', async function(req, res) {
  try {
    var { id, name, cost, category } = req.body;
    if (!id || !name) return res.status(400).json({ error: 'id and name required' });

    var trimmedId = id.trim();
    if (trimmedId.length > 200) return res.status(400).json({ error: 'Model ID too long (max 200 chars)' });
    if (!/^[a-zA-Z0-9._\-\/\:]+$/.test(trimmedId)) {
      return res.status(400).json({ error: 'Invalid model ID format. Use only letters, numbers, dots, dashes, slashes, and colons.' });
    }

    // Check if model ID conflicts with a built-in model (all providers)
    var { OPENROUTER_MODELS, BEDROCK_MODELS, AZURE_MODELS, VERTEX_MODELS } = require('../utils/models');
    var allBuiltIn = [].concat(OPENROUTER_MODELS, BEDROCK_MODELS, AZURE_MODELS, VERTEX_MODELS);
    if (allBuiltIn.find(function(m) { return m.id === trimmedId; })) {
      return res.status(400).json({ error: 'Model ID conflicts with a built-in model. Use the toggle to enable/disable built-in models.' });
    }

    var validCategories = ['free', 'fast', 'smart', 'premium'];
    var cat = validCategories.includes(category) ? category : 'smart';

    var customRaw = await db.getSetting('models.custom') || '[]';
    var custom;
    try { custom = JSON.parse(customRaw); } catch(e) { custom = []; }

    var existing = custom.find(function(m) { return m.id === trimmedId; });
    custom = custom.filter(function(m) { return m.id !== trimmedId; });
    custom.push({ id: trimmedId, name: name.trim().substring(0, 100), cost: (cost || '?').substring(0, 20), category: cat, tag: 'CUSTOM' });

    await db.setSetting('models.custom', JSON.stringify(custom));
    require('../utils/models').invalidateAllowedModelsCache();
    logger.audit(req.user.id, existing ? 'admin_model_update' : 'admin_model_add', (existing ? 'Updated' : 'Added') + ' custom model: ' + trimmedId, req, { category: 'admin' });
    res.json({ success: true });
  } catch (e) { res.status(500).json({ error: 'Request failed' }); }
});

// ── DELETE custom model ───────────────────────────────────────────────────
router.delete('/config/models/custom/:modelId(*)', async function(req, res) {
  try {
    var modelId = req.params.modelId;
    var customRaw = await db.getSetting('models.custom') || '[]';
    var custom;
    try { custom = JSON.parse(customRaw); } catch(e) { custom = []; }
    custom = custom.filter(function(m) { return m.id !== modelId; });
    await db.setSetting('models.custom', JSON.stringify(custom));
    require('../utils/models').invalidateAllowedModelsCache();
    logger.audit(req.user.id, 'admin_model_delete', 'Removed custom model: ' + modelId, req, { category: 'admin' });
    res.json({ success: true });
  } catch (e) { res.status(500).json({ error: 'Request failed' }); }
});

// ── POST clear all custom/discovered models ───────────────────────────────
router.post('/config/models/clear-all', async function(req, res) {
  try {
    await db.setSetting('models.custom', '[]');
    await db.setSetting('models.disabled', '[]');
    await db.setSetting('models.default', '');
    require('../utils/models').invalidateAllowedModelsCache();
    logger.audit(req.user.id, 'admin_models_clear_all', 'Cleared all custom models and disabled list', req, { category: 'admin' });
    res.json({ success: true });
  } catch (e) { res.status(500).json({ error: 'Request failed' }); }
});

// ── GET discover models from provider API ─────────────────────────────────
router.get('/config/models/discover', async function(req, res) {
  try {
    var { discoverModels } = require('../utils/ai');
    var discovered = await discoverModels();
    var search = (req.query.q || '').toLowerCase().trim();
    if (search) {
      discovered = discovered.filter(function(m) {
        return m.id.toLowerCase().indexOf(search) !== -1 || m.name.toLowerCase().indexOf(search) !== -1;
      });
    }
    res.json({ success: true, models: discovered, count: discovered.length });
  } catch (e) { res.status(500).json({ error: 'Request failed' }); }
});

// ── POST add discovered model to custom list ──────────────────────────────
router.post('/config/models/add-discovered', async function(req, res) {
  try {
    var { id, name, cost, category } = req.body;
    if (!id || !name) return res.status(400).json({ error: 'id and name required' });

    var trimmedId = id.trim();
    if (trimmedId.length > 200) return res.status(400).json({ error: 'Model ID too long (max 200 chars)' });

    var validCategories = ['free', 'fast', 'smart', 'premium'];
    var cat = validCategories.includes(category) ? category : 'smart';

    var customRaw = await db.getSetting('models.custom') || '[]';
    var custom;
    try { custom = JSON.parse(customRaw); } catch(e) { custom = []; }

    custom = custom.filter(function(m) { return m.id !== trimmedId; });
    custom.push({ id: trimmedId, name: name.trim().substring(0, 100), cost: (cost || '?').substring(0, 20), category: cat, tag: 'DISCOVERED' });

    await db.setSetting('models.custom', JSON.stringify(custom));
    require('../utils/models').invalidateAllowedModelsCache();
    logger.audit(req.user.id, 'admin_model_discover_add', 'Added discovered model: ' + trimmedId, req, { category: 'admin' });
    res.json({ success: true, id: trimmedId });
  } catch (e) { res.status(500).json({ error: 'Request failed' }); }
});

// ── POST test a model with a simple prompt ────────────────────────────────
router.post('/config/models/test', async function(req, res) {
  try {
    var { modelId } = req.body;
    if (!modelId) return res.status(400).json({ error: 'modelId required' });
    var { callAI } = require('../utils/ai');
    var start = Date.now();
    var result = await callAI(
      [{ role: 'user', content: 'Reply with only the word: OK' }],
      { model: modelId, maxTokens: 20, temperature: 0, skipAllowlistCheck: true }
    );
    res.json({
      success: true,
      response: (result.content || '').trim(),
      model: result.model,
      provider: result.provider,
      duration: Date.now() - start,
      tokens: result.usage
    });
  } catch (e) {
    res.json({ success: false, error: e.message });
  }
});

// ── GET discover image-capable models from provider ───────────────────────
router.get('/config/image-models/discover', async function(req, res) {
  try {
    var axios = require('axios');
    var discovered = [];
    if (process.env.LITELLM_API_BASE) {
      var resp = await axios.get(liteLLMBaseUrl() + '/model/info', { headers: getLiteLLMAdminHeaders(), timeout: 15000 });
      var models = (resp.data && resp.data.data) || [];
      models.forEach(function(m) {
        if (liteLLMModelMode(m) === 'image_generation') {
          var id = liteLLMModelId(m);
          if (id) discovered.push({ id: id, name: id, source: 'gateway-api', mode: 'image_generation', capability: 'image' });
        }
      });
    }
    var search = (req.query.q || '').toLowerCase().trim();
    if (search) discovered = discovered.filter(function(m) { return matchesDiscoverySearch(m, search, 'image generation'); });
    res.json({ success: true, models: discovered, count: discovered.length });
  } catch (e) {
    res.status(500).json({ error: e.message || 'Request failed' });
  }
});

// ── POST test image generation model ──────────────────────────────────────
router.post('/config/image-models/test', async function(req, res) {
  try {
    if (!process.env.LITELLM_API_BASE) return res.json({ success: false, error: 'LiteLLM not configured' });
    var modelId = String(req.body.modelId || '').trim();
    if (!modelId) return res.status(400).json({ error: 'modelId required' });
    var axios = require('axios');
    var started = Date.now();
    var imgResp = await axios.post(gatewayUrl('/images/generations'), {
      model: modelId,
      prompt: 'A simple child-friendly icon of lungs and an inhaler, flat medical illustration, no text',
      size: '1024x1024'
    }, { headers: getLiteLLMHeaders('application/json'), timeout: 120000 });
    var item = imgResp.data && imgResp.data.data && imgResp.data.data[0] ? imgResp.data.data[0] : {};
    res.json({
      success: true,
      model: modelId,
      duration: Date.now() - started,
      imageUrl: item.url || null,
      base64: item.b64_json || null
    });
  } catch (e) {
    var detail = e.response && e.response.data ? JSON.stringify(e.response.data).substring(0, 500) : e.message;
    res.json({ success: false, error: detail });
  }
});

// ── GET TTS provider status, voice list, and DB overrides ────────────────
router.get('/config/tts', async function(req, res) {
  try {
    var envProvider = getTTSEnvProvider();
    var activeProvider = getTTSProvider();
    var dbVoice = await db.getSetting('tts.voice') || '';
    var dbModel = await db.getSetting('tts.model') || '';
    var envVoice = process.env.LITELLM_TTS_VOICE || '';
    var envModel = process.env.LITELLM_TTS_MODEL || '';
    var currentModel = dbModel || envModel;
    var voices = getLiteLLMTTSVoicesForModel(currentModel, { currentVoice: dbVoice });
    var currentVoice = [dbVoice, envVoice, voices[0]].find(function(voice) {
      return isLiteLLMTTSVoiceCompatible(currentModel, voice);
    }) || '';
    res.json({
      success: true,
      provider: activeProvider,
      envProvider: envProvider,
      currentVoice: currentVoice,
      currentModel: currentModel,
      dbVoice: dbVoice,
      dbModel: dbModel,
      envVoice: envVoice,
      envModel: envModel,
      configured: {
        litellm: !!process.env.LITELLM_API_BASE
      },
      voices: {
        litellm: voices
      }
    });
  } catch (e) { res.status(500).json({ error: 'Request failed' }); }
});

// ── GET discover TTS voices from provider ────────────────────────────────
router.get('/config/tts/discover', async function(req, res) {
  try {
    var search = (req.query.q || '').toLowerCase().trim();
    var axios = require('axios');
    var discovered = [];

    var provider = getTTSProvider();

    if (provider === 'litellm' && process.env.LITELLM_API_BASE) {
      var dbVoice = await db.getSetting('tts.voice') || '';
      var dbModel = await db.getSetting('tts.model') || '';
      var currentVoice = dbVoice || process.env.LITELLM_TTS_VOICE || '';
      var currentModel = dbModel || process.env.LITELLM_TTS_MODEL || '';
      var modelInfo = [];
      try {
        var lResp = await axios.get(liteLLMBaseUrl() + '/model/info', { headers: getLiteLLMAdminHeaders(), timeout: 10000 });
        modelInfo = lResp.data && lResp.data.data ? lResp.data.data : [];
      } catch (e) { logger.warn('LiteLLM TTS model list failed: ' + e.message); }
      getLiteLLMTTSDiscoveryItems(modelInfo, { currentModel: currentModel, currentVoice: currentVoice }).forEach(function(item) {
        discovered.push(item);
      });
    }

    if (search) {
      discovered = discovered.filter(function(d) {
        return d.id.toLowerCase().indexOf(search) !== -1 || d.name.toLowerCase().indexOf(search) !== -1;
      });
    }
    res.json({ success: true, provider: provider, voices: discovered, count: discovered.length });
  } catch (e) { res.status(500).json({ error: 'Request failed' }); }
});

// ── POST test TTS — returns base64 audio ─────────────────────────────────
router.post('/config/tts/test', async function(req, res) {
  try {
    var text = ((req.body.text || 'Hello, this is a TTS test for Pediatric AI Scribe.')).substring(0, 500);
    var voice = req.body.voice;
    var axios = require('axios');

    var provider = getTTSProvider();
    if (provider === 'none') return res.json({ success: false, error: 'No TTS provider configured' });
    if (provider !== 'litellm') return res.json({ success: false, error: 'TTS is configured for LiteLLM only' });
    if (!process.env.LITELLM_API_BASE) return res.json({ success: false, error: 'LITELLM_API_BASE not set' });

    var adminModel = await db.getSetting('tts.model') || '';
    var adminVoice = await db.getSetting('tts.voice') || '';
    var ttsModel = adminModel || process.env.LITELLM_TTS_MODEL || '';
    var defaultVoices = getLiteLLMTTSVoicesForModel(ttsModel, { currentVoice: adminVoice });
    var usedVoice = [voice, adminVoice, process.env.LITELLM_TTS_VOICE || '', defaultVoices[0]].find(function(candidate) {
      return isLiteLLMTTSVoiceCompatible(ttsModel, candidate);
    }) || '';
    if (!ttsModel) return res.json({ success: false, error: 'No LiteLLM TTS model configured' });
    var payload = Object.assign({ model: ttsModel, voice: usedVoice, input: text }, getLiteLLMTTSRequestOptions(ttsModel));

    var ttsResp = await axios.post(gatewayUrl('/audio/speech'),
      payload,
      { headers: getLiteLLMHeaders('application/json'), responseType: 'arraybuffer', timeout: 60000 }
    );
    var buffer = Buffer.from(ttsResp.data);
    res.json({ success: true, audio: buffer.toString('base64'), provider: provider, voice: usedVoice });
  } catch (e) {
    var detail = e.response && e.response.data
      ? (Buffer.isBuffer(e.response.data) ? e.response.data.toString('utf8').substring(0, 300) : JSON.stringify(e.response.data).substring(0, 300))
      : e.message;
    res.json({ success: false, error: detail });
  }
});

// ── GET STT provider status and model list ─────────────────────────────────
router.get('/config/stt', async function(req, res) {
  try {
    var envProvider = process.env.TRANSCRIBE_PROVIDER || 'auto';
    var configured = getSTTDependencies();
    var activeProvider = getSTTProvider(configured);
    var dbModel   = await db.getSetting('stt.model') || '';
    var envModel  = process.env.LITELLM_STT_MODEL || '';
    res.json({
      success: true,
      provider: activeProvider,
      envProvider: envProvider,
      currentModel: dbModel || envModel,
      dbModel: dbModel,
      envModel: envModel,
      configured: configured,
      models: getSTTModelLists()
    });
  } catch (e) { res.status(500).json({ error: 'Request failed' }); }
});

// ── GET discover STT models from provider ────────────────────────────────
router.get('/config/stt/discover', async function(req, res) {
  try {
    var search = (req.query.q || '').toLowerCase().trim();
    var axios = require('axios');
    var discovered = [];

    var provider = getSTTProvider();

    if (provider === 'litellm' && process.env.LITELLM_API_BASE) {
      try {
        var sResp = await axios.get(liteLLMBaseUrl() + '/model/info', { headers: getLiteLLMAdminHeaders(), timeout: 10000 });
        getLiteLLMSTTModels(sResp.data && sResp.data.data).forEach(function(id) {
          discovered.push({ id: id, name: id, source: 'gateway-api' });
        });
      } catch(e) { logger.warn('STT discovery failed: ' + e.message); }
      if (discovered.length === 0) {
        getSTTModelLists().litellm.forEach(function(id) {
          discovered.push({ id: id, name: id, source: 'configured-default' });
        });
      }
    }

    if (search) {
      discovered = discovered.filter(function(d) {
        return d.id.toLowerCase().indexOf(search) !== -1 || d.name.toLowerCase().indexOf(search) !== -1;
      });
    }
    res.json({ success: true, provider: provider, models: discovered, count: discovered.length });
  } catch (e) { res.status(500).json({ error: 'Request failed' }); }
});

// ── POST test STT — accepts base64 audio, returns transcription ───────────
router.post('/config/stt/test', async function(req, res) {
  try {
    var { audioBase64, mimeType } = req.body;
    if (!audioBase64) return res.json({ success: false, error: 'audioBase64 required' });
    var audioBuffer = Buffer.from(audioBase64, 'base64');
    var mime = mimeType || 'audio/webm';

    var provider = getSTTProvider();
    if (provider === 'none') return res.json({ success: false, error: 'No STT provider configured' });
    if (provider !== 'litellm') return res.json({ success: false, error: 'STT is configured for LiteLLM only' });
    if (!process.env.LITELLM_API_BASE) return res.json({ success: false, error: 'LITELLM_API_BASE not set' });

    var start = Date.now();
    var text = '';

    var adminSttModel = await db.getSetting('stt.model') || '';
    var sttModel = adminSttModel || process.env.LITELLM_STT_MODEL || '';
    if (!sttModel) return res.json({ success: false, error: 'No LiteLLM STT model configured' });

    var ext = mime.split('/')[1] || 'webm';
    var file = new File([audioBuffer], 'audio.' + ext, { type: mime });
    var form = new FormData();
    form.append('file', file);
    form.append('model', sttModel);
    var sttResp = await fetch(gatewayUrl('/audio/transcriptions'), {
      method: 'POST', headers: getLiteLLMHeaders(), body: form
    }).then(function(r) { return r.json().then(function(d) { return { data: d }; }); });
    text = sttResp.data && sttResp.data.text ? sttResp.data.text : '';

    res.json({ success: true, text: text.trim(), provider: provider, duration: Date.now() - start });
  } catch (e) {
    res.json({ success: false, error: e.message });
  }
});

// ── GET embedding model config ────────────────────────────────────────────
router.get('/config/embeddings', async function(req, res) {
  try {
    var { isEmbeddingsAvailable, DEFAULT_MODEL, DEFAULT_DIMS } = require('../utils/embeddings');
    var dbModel = await db.getSetting('embeddings.model') || '';
    var dbDims  = await db.getSetting('embeddings.dimensions') || '';
    var envModel = process.env.EMBEDDING_MODEL || DEFAULT_MODEL;
    var envDims  = parseInt(process.env.EMBEDDING_DIMENSIONS) || DEFAULT_DIMS;

    var provider = 'none';
    if (process.env.LITELLM_API_BASE) provider = 'litellm';

    res.json({
      success: true,
      provider: provider,
      configured: isEmbeddingsAvailable(),
      currentModel: dbModel || envModel,
      currentDimensions: dbDims ? parseInt(dbDims) : envDims,
      dbModel: dbModel,
      dbDimensions: dbDims,
      envModel: envModel,
      envDimensions: envDims,
      models: []
    });
  } catch (e) { res.status(500).json({ error: 'Request failed' }); }
});

// ── GET discover embedding models from provider ───────────────────────────
router.get('/config/embeddings/discover', async function(req, res) {
  try {
    var search = (req.query.q || '').toLowerCase().trim();
    var axios = require('axios');
    var discovered = [];

    var provider = 'none';
    if (process.env.LITELLM_API_BASE) provider = 'litellm';

    if (provider === 'litellm') {
      try {
        var eResp = await axios.get(liteLLMBaseUrl() + '/model/info', { headers: getLiteLLMAdminHeaders(), timeout: 10000 });
        var embeddingModels = getLiteLLMEmbeddingModels(eResp.data && eResp.data.data);
        for (var i = 0; i < embeddingModels.length; i++) {
          var m = embeddingModels[i];
          var dims = m.dims === '?' ? await probeLiteLLMEmbeddingDimensions(m.id) : m.dims;
          discovered.push({ id: m.id, name: m.name, dims: dims, source: 'gateway-api', mode: 'embedding', capability: 'embedding' });
        }
      } catch(e) { logger.warn('LiteLLM embedding metadata discovery failed: ' + e.message); }
    }

    if (search) {
      discovered = discovered.filter(function(d) {
        return matchesDiscoverySearch(d, search, 'embedding vector');
      });
    }
    res.json({ success: true, provider: provider, models: discovered, count: discovered.length });
  } catch (e) { res.status(500).json({ error: 'Request failed' }); }
});

// ── POST test embedding ───────────────────────────────────────────────────
router.post('/config/embeddings/test', async function(req, res) {
  try {
    var text = (req.body.text || 'Pediatric patient with fever').substring(0, 500);
    var { generateEmbedding, DEFAULT_MODEL } = require('../utils/embeddings');
    var db = require('../db/database');
    var dbModel = await db.getSetting('embeddings.model') || '';
    var start = Date.now();
    var vector = await generateEmbedding(text);
    var dims = Array.isArray(vector) ? vector.length : 0;
    var sample = Array.isArray(vector) ? vector.slice(0, 8).map(function(v) { return v.toFixed(4); }) : [];
    res.json({
      success: true,
      dimensions: dims,
      sample: sample,
      model: dbModel || process.env.EMBEDDING_MODEL || DEFAULT_MODEL,
      duration: Date.now() - start
    });
  } catch (e) {
    res.json({ success: false, error: e.message });
  }
});

// ============================================================
// WILDCARD CONFIG — Must come AFTER all specific model routes above
// :key(*) matches slashes, so it would intercept /config/models/toggle
// and /config/models/default if registered before them.
// ============================================================

// ── PUT update a single config entry ──────────────────────────────────────
router.put('/config/:key(*)', async function(req, res) {
  try {
    var key = req.params.key;
    var value = req.body.value;

    if (value === undefined || value === null) {
      return res.status(400).json({ error: 'value is required' });
    }

    // Security: only allow known key prefixes
    var allowed = ['announcement.', 'feature.', 'email.', 'prompt.', 'registration_enabled', 'site.', 'smtp.', 'models.', 'tts.', 'stt.', 'embeddings.', 'clinical_assistant.'];
    var isAllowed = allowed.some(function(p) { return key === p || key.startsWith(p); });
    if (!isAllowed) {
      return res.status(400).json({ error: 'Unknown config key' });
    }

    await db.setSetting(key, String(value));

    // Update in-memory prompt immediately if it's a prompt key
    if (key.startsWith('prompt.')) {
      var promptKey = key.replace('prompt.', '');
      PROMPTS.updatePrompt(promptKey, String(value));
    }

    logger.audit(req.user.id, 'admin_config_update', 'Updated config: ' + key, req, { category: 'admin' });

    res.json({ success: true });
  } catch (e) { res.status(500).json({ error: 'Request failed' }); }
});

module.exports = router;
