import { Response } from 'express';
import prisma from '../lib/prisma.js';
import * as appSettingService from '../services/appSetting.service.js';
import { logChange } from '../services/audit.service.js';
import { ApiResponse, AuthRequest } from '../types/index.js';

export async function getAllSettings(req: AuthRequest, res: Response): Promise<void> {
  try {
    const settings = await appSettingService.getAllSettings();
    res.json({ success: true, data: settings } as ApiResponse);
  } catch (error) {
    res.status(500).json({
      success: false,
      error: 'Fehler beim Laden der Einstellungen',
    } as ApiResponse);
  }
}

export async function getPublicSettings(req: AuthRequest, res: Response): Promise<void> {
  try {
    const settings = await appSettingService.getPublicSettings();
    res.json({ success: true, data: settings } as ApiResponse);
  } catch (error) {
    res.status(500).json({
      success: false,
      error: 'Fehler beim Laden der Einstellungen',
    } as ApiResponse);
  }
}

export async function updateSetting(req: AuthRequest, res: Response): Promise<void> {
  try {
    const { key } = req.params;
    const { value } = req.body;

    if (value === undefined) {
      res.status(400).json({
        success: false,
        error: 'Wert ist erforderlich',
      } as ApiResponse);
      return;
    }

    // Whitelist-Check (Pentest Runde 11, M1)
    if (!appSettingService.isAllowedSettingKey(key)) {
      res.status(400).json({
        success: false,
        error: `Unbekannter Setting-Key: ${key}`,
      } as ApiResponse);
      return;
    }

    // Vorherigen Stand laden für Audit
    const before = await prisma.appSetting.findUnique({ where: { key } });
    const oldValue = before?.value ?? '-';
    // HTML-Tags aus Plain-Text-Keys strippen, bevor sie in der DB landen.
    // Pentest 2026-05-19, MEDIUM: companyName="<img onerror=...>" landete
    // sonst ungefiltert in E-Mail-Templates / PDFs.
    const stripped = appSettingService.sanitizeSettingValue(key, String(value));
    // Schema-spezifische Validierung (URL/Email/Int/Bool). Pentest
    // 2026-05-28, LOW 34.5: portalLoginUrl nahm `/relative/path` und
    // `http://192.168.1.1` ungefiltert entgegen → Open-Redirect /
    // SSRF in der versendeten Mail.
    const validation = appSettingService.validateSettingValue(key, stripped);
    if (!validation.ok) {
      res.status(400).json({ success: false, error: validation.error } as ApiResponse);
      return;
    }
    const newValue = validation.value;

    await appSettingService.setSetting(key, newValue);

    const label = oldValue !== newValue
      ? `Einstellung "${key}" geändert: ${oldValue} → ${newValue}`
      : `Einstellung "${key}" geändert`;
    await logChange({
      req, action: 'UPDATE', resourceType: 'AppSetting',
      resourceId: key,
      label,
      details: oldValue !== newValue ? { [key]: { von: oldValue, nach: newValue } } : undefined,
    });
    res.json({ success: true, message: 'Einstellung gespeichert' } as ApiResponse);
  } catch (error) {
    res.status(400).json({
      success: false,
      error: error instanceof Error ? error.message : 'Fehler beim Speichern der Einstellung',
    } as ApiResponse);
  }
}

export async function updateSettings(req: AuthRequest, res: Response): Promise<void> {
  try {
    const settings = req.body;

    if (!settings || typeof settings !== 'object') {
      res.status(400).json({
        success: false,
        error: 'Einstellungen sind erforderlich',
      } as ApiResponse);
      return;
    }

    // Whitelist-Check für jeden Key (Pentest Runde 11, M1: Mass Assignment)
    const unknownKeys = Object.keys(settings).filter(
      (k) => !appSettingService.isAllowedSettingKey(k),
    );
    if (unknownKeys.length > 0) {
      res.status(400).json({
        success: false,
        error: `Unbekannte Setting-Keys: ${unknownKeys.join(', ')}`,
      } as ApiResponse);
      return;
    }

    // Vorherige Werte laden für Audit. Validierung erfolgt vor dem
    // ersten Schreibzugriff, damit ein Bulk-PUT mit einem ungültigen
    // Wert nicht die anderen Werte halb-committed liegen lässt.
    const changes: Record<string, { von: unknown; nach: unknown }> = {};
    const sanitizedEntries: Array<{ key: string; oldValue: string; newValue: string }> = [];
    for (const [key, value] of Object.entries(settings)) {
      const before = await prisma.appSetting.findUnique({ where: { key } });
      const oldValue = before?.value ?? '-';
      const stripped = appSettingService.sanitizeSettingValue(key, String(value));
      const validation = appSettingService.validateSettingValue(key, stripped);
      if (!validation.ok) {
        res.status(400).json({ success: false, error: `${key}: ${validation.error}` } as ApiResponse);
        return;
      }
      sanitizedEntries.push({ key, oldValue, newValue: validation.value });
    }
    for (const { key, oldValue, newValue } of sanitizedEntries) {
      if (oldValue !== newValue) {
        changes[key] = { von: oldValue, nach: newValue };
      }
      await appSettingService.setSetting(key, newValue);
    }

    const changeList = Object.entries(changes).map(([k, c]) => `${k}: ${c.von} → ${c.nach}`).join(', ');
    await logChange({
      req, action: 'UPDATE', resourceType: 'AppSetting',
      label: changeList
        ? `Einstellungen aktualisiert: ${changeList}`
        : `Einstellungen aktualisiert (${Object.keys(settings).join(', ')})`,
      details: Object.keys(changes).length > 0 ? changes : undefined,
    });

    res.json({ success: true, message: 'Einstellungen gespeichert' } as ApiResponse);
  } catch (error) {
    res.status(400).json({
      success: false,
      error: error instanceof Error ? error.message : 'Fehler beim Speichern der Einstellungen',
    } as ApiResponse);
  }
}
