import { Router } from 'express';
import multer from 'multer';
import path from 'path';
import fs from 'fs';
import { fileURLToPath } from 'url';
import { getDb } from '../config/database.js';
import { authenticateToken, requireAdmin } from '../middleware/auth.js';
import { log } from '../config/logger.js';
import { getOAuthConfig } from '../config/oauth.js';
import { isTranscriptionEnabled } from '../config/transcription.js';

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

const router = Router();

const SAFE_ID_RE = /^[a-zA-Z0-9_-]{1,50}$/;

// Validate that a URL uses a safe scheme (http/https only)
function isSafeUrl(url) {
  try {
    const parsed = new URL(url);
    return parsed.protocol === 'https:' || parsed.protocol === 'http:';
  } catch {
    return false;
  }
}

// Ensure uploads/branding directory exists
const brandingDir = path.join(__dirname, '..', '..', 'uploads', 'branding');
if (!fs.existsSync(brandingDir)) {
  fs.mkdirSync(brandingDir, { recursive: true });
}

// Multer config for logo upload
const storage = multer.diskStorage({
  destination: (req, file, cb) => cb(null, brandingDir),
  filename: (req, file, cb) => {
    const ext = path.extname(file.originalname).toLowerCase() || '.png';
    cb(null, `logo${ext}`);
  },
});

const upload = multer({
  storage,
  limits: { fileSize: 5 * 1024 * 1024 }, // 5MB
  fileFilter: (req, file, cb) => {
    const allowed = /\.(jpg|jpeg|png|gif|svg|webp|ico)$/i;
    const mimeAllowed = /^image\/(jpeg|png|gif|svg\+xml|webp|x-icon|vnd\.microsoft\.icon)$/;
    if (allowed.test(path.extname(file.originalname)) && mimeAllowed.test(file.mimetype)) {
      cb(null, true);
    } else {
      cb(new Error('Only image files are allowed'));
    }
  },
});

// Helper: get setting from DB
async function getSetting(key) {
  const db = getDb();
  const row = await db.get('SELECT value FROM settings WHERE key = ?', [key]);
  return row?.value || null;
}

// Helper: set setting in DB
async function setSetting(key, value) {
  const db = getDb();
  // Try update first, then insert if nothing was updated
  const result = await db.run('UPDATE settings SET value = ?, updated_at = CURRENT_TIMESTAMP WHERE key = ?', [value, key]);
  if (result.changes === 0) {
    // Use INSERT with a dummy RETURNING to satisfy PG adapter, or just use exec-style
    await db.run('INSERT INTO settings (key, value) VALUES (?, ?) RETURNING key', [key, value]);
  }
}

// Helper: delete setting from DB
async function deleteSetting(key) {
  const db = getDb();
  await db.run('DELETE FROM settings WHERE key = ?', [key]);
}

// Helper: find current logo file on disk
function findLogoFile() {
  if (!fs.existsSync(brandingDir)) return null;
  const files = fs.readdirSync(brandingDir);
  const logo = files.find(f => f.startsWith('logo.'));
  return logo ? path.join(brandingDir, logo) : null;
}

// GET /api/branding - Get branding settings (public)
router.get('/', async (req, res) => {
  try {
    const appName = await getSetting('app_name');
    const defaultTheme = await getSetting('default_theme');
    const logoFile = findLogoFile();

    const registrationMode = await getSetting('registration_mode');
    const imprintUrl = await getSetting('imprint_url');
    const privacyUrl = await getSetting('privacy_url');

    // OAuth: expose whether OAuth is enabled + display name for login page
    let oauthEnabled = false;
    let oauthDisplayName = null;
    try {
      const oauthConfig = await getOAuthConfig();
      if (oauthConfig) {
        oauthEnabled = true;
        oauthDisplayName = oauthConfig.displayName || 'SSO';
      }
    } catch { /* not configured */ }

    const hideAppName = await getSetting('hide_app_name');

    res.json({
      appName: appName || 'Redlight',
      hasLogo: !!logoFile,
      logoUrl: logoFile ? '/api/branding/logo' : null,
      defaultTheme: defaultTheme || null,
      registrationMode: registrationMode || 'open',
      imprintUrl: imprintUrl || null,
      privacyUrl: privacyUrl || null,
      oauthEnabled,
      oauthDisplayName,
      hideAppName: hideAppName === 'true',
      transcriptionEnabled: isTranscriptionEnabled(),
    });
  } catch (err) {
    log.branding.error('Get branding error:', err);
    res.status(500).json({ error: 'Could not load branding' });
  }
});

// GET /api/branding/logo - Serve logo file (public)
router.get('/logo', (req, res) => {
  const logoFile = findLogoFile();
  if (!logoFile) {
    return res.status(404).json({ error: 'No logo found' });
  }
  // H5: serve SVG as attachment (Content-Disposition) to prevent in-browser script execution.
  // For non-SVG images, inline display is fine.
  const ext = path.extname(logoFile).toLowerCase();
  if (ext === '.svg') {
    res.setHeader('Content-Type', 'image/svg+xml');
    res.setHeader('Content-Disposition', 'attachment; filename="logo.svg"');
    res.setHeader('X-Content-Type-Options', 'nosniff');
    return res.sendFile(logoFile);
  }
  res.sendFile(logoFile);
});

// POST /api/branding/logo - Upload logo (admin only)
router.post('/logo', authenticateToken, requireAdmin, (req, res) => {
  upload.single('logo')(req, res, async (err) => {
    if (err) {
      if (err instanceof multer.MulterError) {
        return res.status(400).json({ error: err.code === 'LIMIT_FILE_SIZE' ? 'File too large (max 5MB)' : err.message });
      }
      return res.status(400).json({ error: err.message });
    }

    if (!req.file) {
      return res.status(400).json({ error: 'No file uploaded' });
    }

    // Remove old logo files that don't match the new extension
    const files = fs.readdirSync(brandingDir);
    for (const f of files) {
      if (f.startsWith('logo.') && f !== req.file.filename) {
        fs.unlinkSync(path.join(brandingDir, f));
      }
    }

    res.json({
      logoUrl: '/api/branding/logo',
      message: 'Logo uploaded',
    });
  });
});

// DELETE /api/branding/logo - Remove logo (admin only)
router.delete('/logo', authenticateToken, requireAdmin, async (req, res) => {
  try {
    const logoFile = findLogoFile();
    if (logoFile) {
      fs.unlinkSync(logoFile);
    }
    res.json({ message: 'Logo removed' });
  } catch (err) {
    log.branding.error('Delete logo error:', err);
    res.status(500).json({ error: 'Could not remove logo' });
  }
});

// PUT /api/branding/name - Update app name (admin only)
router.put('/name', authenticateToken, requireAdmin, async (req, res) => {
  try {
    const { appName } = req.body;
    if (!appName || !appName.trim()) {
      return res.status(400).json({ error: 'App name is required' });
    }
    if (appName.trim().length > 100) {
      return res.status(400).json({ error: 'App name must not exceed 100 characters' });
    }
    await setSetting('app_name', appName.trim());
    res.json({ appName: appName.trim() });
  } catch (err) {
    log.branding.error('Update app name error:', err);
    res.status(500).json({ error: 'Could not update app name' });
  }
});

// PUT /api/branding/default-theme - Set default theme for unauthenticated pages (admin only)
router.put('/default-theme', authenticateToken, requireAdmin, async (req, res) => {
  try {
    const { defaultTheme } = req.body;
    if (!defaultTheme || !defaultTheme.trim()) {
      return res.status(400).json({ error: 'defaultTheme is required' });
    }
    // Basic format validation for theme ID
    if (!SAFE_ID_RE.test(defaultTheme.trim())) {
      return res.status(400).json({ error: 'Invalid theme' });
    }
    await setSetting('default_theme', defaultTheme.trim());
    res.json({ defaultTheme: defaultTheme.trim() });
  } catch (err) {
    log.branding.error('Update default theme error:', err);
    res.status(500).json({ error: 'Could not update default theme' });
  }
});

// PUT /api/branding/registration-mode - Set registration mode (admin only)
router.put('/registration-mode', authenticateToken, requireAdmin, async (req, res) => {
  try {
    const { registrationMode } = req.body;
    if (!registrationMode || !['open', 'invite'].includes(registrationMode)) {
      return res.status(400).json({ error: 'registrationMode must be "open" or "invite"' });
    }
    await setSetting('registration_mode', registrationMode);
    res.json({ registrationMode });
  } catch (err) {
    log.branding.error('Update registration mode error:', err);
    res.status(500).json({ error: 'Could not update registration mode' });
  }
});

// PUT /api/branding/imprint-url - Set imprint URL (admin only)
router.put('/imprint-url', authenticateToken, requireAdmin, async (req, res) => {
  try {
    const { imprintUrl } = req.body;
    if (imprintUrl && imprintUrl.length > 500) {
      return res.status(400).json({ error: 'URL must not exceed 500 characters' });
    }
    if (imprintUrl && imprintUrl.trim() && !isSafeUrl(imprintUrl.trim())) {
      return res.status(400).json({ error: 'URL must start with http:// or https://' });
    }
    if (imprintUrl && imprintUrl.trim()) {
      await setSetting('imprint_url', imprintUrl.trim());
    } else {
      await deleteSetting('imprint_url');
    }
    res.json({ imprintUrl: imprintUrl?.trim() || null });
  } catch (err) {
    log.branding.error('Update imprint URL error:', err);
    res.status(500).json({ error: 'Could not update imprint URL' });
  }
});

// PUT /api/branding/privacy-url - Set privacy policy URL (admin only)
router.put('/privacy-url', authenticateToken, requireAdmin, async (req, res) => {
  try {
    const { privacyUrl } = req.body;
    if (privacyUrl && privacyUrl.length > 500) {
      return res.status(400).json({ error: 'URL must not exceed 500 characters' });
    }
    if (privacyUrl && privacyUrl.trim() && !isSafeUrl(privacyUrl.trim())) {
      return res.status(400).json({ error: 'URL must start with http:// or https://' });
    }
    if (privacyUrl && privacyUrl.trim()) {
      await setSetting('privacy_url', privacyUrl.trim());
    } else {
      await deleteSetting('privacy_url');
    }
    res.json({ privacyUrl: privacyUrl?.trim() || null });
  } catch (err) {
    log.branding.error('Update privacy URL error:', err);
    res.status(500).json({ error: 'Could not update privacy URL' });
  }
});

// PUT /api/branding/hide-app-name - Toggle app name visibility (admin only)
router.put('/hide-app-name', authenticateToken, requireAdmin, async (req, res) => {
  try {
    const { hideAppName } = req.body;
    if (typeof hideAppName !== 'boolean') {
      return res.status(400).json({ error: 'hideAppName must be a boolean' });
    }
    if (hideAppName) {
      await setSetting('hide_app_name', 'true');
    } else {
      await deleteSetting('hide_app_name');
    }
    res.json({ hideAppName });
  } catch (err) {
    log.branding.error('Update hide app name error:', err);
    res.status(500).json({ error: 'Could not update setting' });
  }
});

export default router;
