/**
 * Datenbank Backup Service
 *
 * Ermöglicht Backup und Restore der Datenbank und Uploads über die Web-Oberfläche.
 */

import prisma from '../lib/prisma.js';
import * as fs from 'fs';
import * as path from 'path';
import archiver from 'archiver';
import AdmZip from 'adm-zip';
import bcrypt from 'bcryptjs';

// Verzeichnisse
const BACKUPS_DIR = path.join(__dirname, '../../prisma/backups');
const UPLOADS_DIR = path.join(process.cwd(), 'uploads');

// Stelle sicher, dass das Backup-Verzeichnis existiert
if (!fs.existsSync(BACKUPS_DIR)) {
  fs.mkdirSync(BACKUPS_DIR, { recursive: true });
}

export interface BackupInfo {
  name: string;
  timestamp: string;
  totalRecords: number;
  tables: { table: string; count: number }[];
  sizeBytes: number;
  hasUploads: boolean;
  uploadSizeBytes: number;
}

export interface BackupResult {
  success: boolean;
  backupName?: string;
  error?: string;
}

export interface RestoreResult {
  success: boolean;
  restoredRecords?: number;
  restoredFiles?: number;
  error?: string;
}

// Hilfsfunktion: Datum-Strings zu Date-Objekten konvertieren
function convertDates(obj: any): any {
  if (obj === null || obj === undefined) return obj;
  if (typeof obj === 'string') {
    if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(obj)) {
      return new Date(obj);
    }
    return obj;
  }
  if (Array.isArray(obj)) {
    return obj.map(convertDates);
  }
  if (typeof obj === 'object') {
    const result: any = {};
    for (const [key, value] of Object.entries(obj)) {
      result[key] = convertDates(value);
    }
    return result;
  }
  return obj;
}

// Hilfsfunktion: JSON-Datei lesen
function readJsonFile<T>(filePath: string): T[] {
  if (!fs.existsSync(filePath)) {
    return [];
  }
  const content = fs.readFileSync(filePath, 'utf-8');
  return JSON.parse(content);
}

// Hilfsfunktion: Ordnergröße berechnen (rekursiv)
function getDirectorySize(dirPath: string): number {
  if (!fs.existsSync(dirPath)) return 0;

  let size = 0;
  const items = fs.readdirSync(dirPath);

  for (const item of items) {
    const itemPath = path.join(dirPath, item);
    const stats = fs.statSync(itemPath);

    if (stats.isFile()) {
      size += stats.size;
    } else if (stats.isDirectory()) {
      size += getDirectorySize(itemPath);
    }
  }

  return size;
}

// Hilfsfunktion: Ordner rekursiv kopieren
function copyDirectory(src: string, dest: string): number {
  if (!fs.existsSync(src)) return 0;

  let fileCount = 0;
  fs.mkdirSync(dest, { recursive: true });

  const items = fs.readdirSync(src);
  for (const item of items) {
    const srcPath = path.join(src, item);
    const destPath = path.join(dest, item);
    const stats = fs.statSync(srcPath);

    if (stats.isDirectory()) {
      fileCount += copyDirectory(srcPath, destPath);
    } else {
      fs.copyFileSync(srcPath, destPath);
      fileCount++;
    }
  }

  return fileCount;
}

// Hilfsfunktion: Ordner rekursiv löschen
function deleteDirectory(dirPath: string): void {
  if (!fs.existsSync(dirPath)) return;

  const items = fs.readdirSync(dirPath);
  for (const item of items) {
    const itemPath = path.join(dirPath, item);
    const stats = fs.statSync(itemPath);

    if (stats.isDirectory()) {
      deleteDirectory(itemPath);
    } else {
      fs.unlinkSync(itemPath);
    }
  }

  fs.rmdirSync(dirPath);
}

// Wie deleteDirectory, ABER das Ziel-Verzeichnis selbst bleibt stehen –
// nur die Inhalte verschwinden. Notwendig für Docker-Bind-Mounts wie
// `/app/uploads`: dort wirft `rmdir` ein EBUSY, weil das Volume vom Host
// gemountet ist und sich nicht aushängen lässt.
function emptyDirectory(dirPath: string): void {
  if (!fs.existsSync(dirPath)) return;
  const items = fs.readdirSync(dirPath);
  for (const item of items) {
    const itemPath = path.join(dirPath, item);
    const stats = fs.lstatSync(itemPath);
    if (stats.isDirectory()) {
      deleteDirectory(itemPath);
    } else {
      fs.unlinkSync(itemPath);
    }
  }
}

/**
 * Liste aller verfügbaren Backups
 */
export async function listBackups(): Promise<BackupInfo[]> {
  const backups: BackupInfo[] = [];

  if (!fs.existsSync(BACKUPS_DIR)) {
    return backups;
  }

  const dirs = fs.readdirSync(BACKUPS_DIR)
    .filter(f => {
      const fullPath = path.join(BACKUPS_DIR, f);
      return fs.statSync(fullPath).isDirectory() && f !== '.gitkeep' && !f.startsWith('.');
    })
    .sort()
    .reverse();

  for (const dir of dirs) {
    const backupDir = path.join(BACKUPS_DIR, dir);
    const infoPath = path.join(backupDir, '_backup-info.json');
    const uploadsPath = path.join(backupDir, 'uploads');

    if (fs.existsSync(infoPath)) {
      try {
        const info = JSON.parse(fs.readFileSync(infoPath, 'utf-8'));
        const hasUploads = fs.existsSync(uploadsPath);
        const uploadSize = hasUploads ? getDirectorySize(uploadsPath) : 0;

        backups.push({
          name: dir,
          timestamp: info.timestamp,
          totalRecords: info.totalRecords,
          tables: info.tables,
          sizeBytes: getDirectorySize(backupDir),
          hasUploads,
          uploadSizeBytes: uploadSize,
        });
      } catch {
        backups.push({
          name: dir,
          timestamp: dir,
          totalRecords: 0,
          tables: [],
          sizeBytes: getDirectorySize(backupDir),
          hasUploads: fs.existsSync(uploadsPath),
          uploadSizeBytes: 0,
        });
      }
    }
  }

  return backups;
}

/**
 * Neues Backup erstellen (inkl. Uploads)
 */
export async function createBackup(): Promise<BackupResult> {
  try {
    const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
    const backupDir = path.join(BACKUPS_DIR, timestamp);

    fs.mkdirSync(backupDir, { recursive: true });

    // Tabellen in Abhängigkeitsreihenfolge
    const tables = [
      { name: 'Permission', query: () => prisma.permission.findMany() },
      { name: 'Role', query: () => prisma.role.findMany() },
      { name: 'SalesPlatform', query: () => prisma.salesPlatform.findMany() },
      { name: 'ContractCategory', query: () => prisma.contractCategory.findMany() },
      { name: 'CancellationPeriod', query: () => prisma.cancellationPeriod.findMany() },
      { name: 'ContractDuration', query: () => prisma.contractDuration.findMany() },
      { name: 'AppSetting', query: () => prisma.appSetting.findMany() },
      { name: 'EmailProviderConfig', query: () => prisma.emailProviderConfig.findMany() },
      { name: 'Provider', query: () => prisma.provider.findMany() },
      { name: 'RolePermission', query: () => prisma.rolePermission.findMany() },
      { name: 'User', query: () => prisma.user.findMany() },
      { name: 'Customer', query: () => prisma.customer.findMany() },
      { name: 'Tariff', query: () => prisma.tariff.findMany() },
      { name: 'UserRole', query: () => prisma.userRole.findMany() },
      { name: 'CustomerRepresentative', query: () => prisma.customerRepresentative.findMany() },
      { name: 'StressfreiEmail', query: () => prisma.stressfreiEmail.findMany() },
      { name: 'Contract', query: () => prisma.contract.findMany() },
      { name: 'Meter', query: () => prisma.meter.findMany() },
      { name: 'CachedEmail', query: () => prisma.cachedEmail.findMany() },
      { name: 'ContractTask', query: () => prisma.contractTask.findMany() },
      { name: 'MeterReading', query: () => prisma.meterReading.findMany() },
      { name: 'ContractTaskSubtask', query: () => prisma.contractTaskSubtask.findMany() },
      { name: 'EnergyContractDetails', query: () => prisma.energyContractDetails.findMany() },
      { name: 'InternetContractDetails', query: () => prisma.internetContractDetails.findMany() },
      { name: 'MobileContractDetails', query: () => prisma.mobileContractDetails.findMany() },
      { name: 'TvContractDetails', query: () => prisma.tvContractDetails.findMany() },
      { name: 'CarInsuranceDetails', query: () => prisma.carInsuranceDetails.findMany() },
      { name: 'PhoneNumber', query: () => prisma.phoneNumber.findMany() },
      { name: 'SimCard', query: () => prisma.simCard.findMany() },
      { name: 'Invoice', query: () => prisma.invoice.findMany() },
      { name: 'ContractHistoryEntry', query: () => prisma.contractHistoryEntry.findMany() },
      { name: 'Address', query: () => prisma.address.findMany() },
      { name: 'BankCard', query: () => prisma.bankCard.findMany() },
      { name: 'IdentityDocument', query: () => prisma.identityDocument.findMany() },
      // Neue Tabellen
      { name: 'PdfTemplate', query: () => prisma.pdfTemplate.findMany() },
      { name: 'ContractMeter', query: () => prisma.contractMeter.findMany() },
      { name: 'ContractDocument', query: () => prisma.contractDocument.findMany() },
      { name: 'RepresentativeAuthorization', query: () => prisma.representativeAuthorization.findMany() },
      { name: 'CustomerConsent', query: () => prisma.customerConsent.findMany() },
      { name: 'DataDeletionRequest', query: () => prisma.dataDeletionRequest.findMany() },
      { name: 'EmailLog', query: () => prisma.emailLog.findMany() },
      { name: 'AuditRetentionPolicy', query: () => prisma.auditRetentionPolicy.findMany() },
      { name: 'AuditLog', query: () => prisma.auditLog.findMany() },
      { name: 'SecurityEvent', query: () => prisma.securityEvent.findMany() },
    ];

    let totalRecords = 0;
    const stats: { table: string; count: number }[] = [];

    for (const table of tables) {
      try {
        const data = await table.query();
        const count = data.length;
        totalRecords += count;
        stats.push({ table: table.name, count });

        const filePath = path.join(backupDir, `${table.name}.json`);
        fs.writeFileSync(filePath, JSON.stringify(data, null, 2));
      } catch {
        stats.push({ table: table.name, count: 0 });
      }
    }

    // Uploads-Ordner kopieren (falls vorhanden)
    let uploadFileCount = 0;
    if (fs.existsSync(UPLOADS_DIR)) {
      const uploadsBackupDir = path.join(backupDir, 'uploads');
      uploadFileCount = copyDirectory(UPLOADS_DIR, uploadsBackupDir);
    }

    // Backup-Info speichern
    const backupInfo = {
      timestamp: new Date().toISOString(),
      totalRecords,
      tables: stats,
      uploadFiles: uploadFileCount,
    };
    fs.writeFileSync(path.join(backupDir, '_backup-info.json'), JSON.stringify(backupInfo, null, 2));

    return { success: true, backupName: timestamp };
  } catch (error: any) {
    return { success: false, error: error.message };
  }
}

/**
 * Backup wiederherstellen (inkl. Uploads)
 */
export async function restoreBackup(backupName: string): Promise<RestoreResult> {
  const backupDir = path.join(BACKUPS_DIR, backupName);

  if (!fs.existsSync(backupDir)) {
    return { success: false, error: 'Backup nicht gefunden' };
  }

  try {
    // Foreign Key Checks deaktivieren
    await prisma.$executeRawUnsafe('SET FOREIGN_KEY_CHECKS = 0');

    // WICHTIG: Alle Tabellen vor dem Restore leeren, damit keine alten Daten übrig bleiben
    console.log('[Restore] Lösche alle bestehenden Daten...');

    // Logs & Audit zuerst (hängen an allem)
    await prisma.auditLog.deleteMany({});
    await prisma.emailLog.deleteMany({});
    await prisma.securityEvent.deleteMany({});

    // Detail-Tabellen
    await prisma.carInsuranceDetails.deleteMany({});
    await prisma.tvContractDetails.deleteMany({});
    await prisma.simCard.deleteMany({});
    await prisma.mobileContractDetails.deleteMany({});
    await prisma.phoneNumber.deleteMany({});
    await prisma.internetContractDetails.deleteMany({});
    await prisma.invoice.deleteMany({});
    await prisma.energyContractDetails.deleteMany({});
    await prisma.meterReading.deleteMany({});
    await prisma.contractHistoryEntry.deleteMany({});

    // Neue Contract-bezogene Tabellen
    await prisma.contractDocument.deleteMany({});
    await prisma.contractMeter.deleteMany({});

    // E-Mail & Verträge
    await prisma.cachedEmail.deleteMany({});
    await prisma.contractTaskSubtask.deleteMany({});
    await prisma.contractTask.deleteMany({});
    await prisma.contract.deleteMany({});

    // DSGVO + Vollmachten (abhängig von Customer)
    await prisma.representativeAuthorization.deleteMany({});
    await prisma.customerConsent.deleteMany({});
    await prisma.dataDeletionRequest.deleteMany({});

    // Kunden-bezogene Daten
    await prisma.stressfreiEmail.deleteMany({});
    await prisma.meter.deleteMany({});
    await prisma.identityDocument.deleteMany({});
    await prisma.bankCard.deleteMany({});
    await prisma.address.deleteMany({});
    await prisma.customerRepresentative.deleteMany({});

    // Benutzer & Rollen
    await prisma.userRole.deleteMany({});
    await prisma.user.deleteMany({});
    await prisma.customer.deleteMany({});

    // Stammdaten & Kataloge
    await prisma.pdfTemplate.deleteMany({});
    await prisma.tariff.deleteMany({});
    await prisma.provider.deleteMany({});
    await prisma.rolePermission.deleteMany({});
    await prisma.role.deleteMany({});
    await prisma.permission.deleteMany({});
    await prisma.salesPlatform.deleteMany({});
    await prisma.cancellationPeriod.deleteMany({});
    await prisma.contractDuration.deleteMany({});
    await prisma.contractCategory.deleteMany({});
    await prisma.emailProviderConfig.deleteMany({});
    await prisma.appSetting.deleteMany({});
    await prisma.auditRetentionPolicy.deleteMany({});

    console.log('[Restore] Alle Daten gelöscht, starte Wiederherstellung...');

    // Restore-Reihenfolge
    const restoreOrder = [
      {
        name: 'Permission',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.permission.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'Role',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.role.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'SalesPlatform',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.salesPlatform.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'ContractCategory',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.contractCategory.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'CancellationPeriod',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.cancellationPeriod.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'ContractDuration',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.contractDuration.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'AppSetting',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.appSetting.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'EmailProviderConfig',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.emailProviderConfig.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'Provider',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.provider.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'RolePermission',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.rolePermission.upsert({
              where: { roleId_permissionId: { roleId: item.roleId, permissionId: item.permissionId } },
              update: {},
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'User',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.user.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'Customer',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.customer.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'Tariff',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.tariff.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'UserRole',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.userRole.upsert({
              where: { userId_roleId: { userId: item.userId, roleId: item.roleId } },
              update: {},
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'CustomerRepresentative',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.customerRepresentative.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'StressfreiEmail',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.stressfreiEmail.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'Contract',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.contract.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'Meter',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.meter.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'CachedEmail',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.cachedEmail.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'ContractTask',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.contractTask.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'MeterReading',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.meterReading.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'ContractTaskSubtask',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.contractTaskSubtask.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'EnergyContractDetails',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.energyContractDetails.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'InternetContractDetails',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.internetContractDetails.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'MobileContractDetails',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.mobileContractDetails.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'TvContractDetails',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.tvContractDetails.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'CarInsuranceDetails',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.carInsuranceDetails.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'PhoneNumber',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.phoneNumber.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'SimCard',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.simCard.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'Invoice',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.invoice.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'ContractHistoryEntry',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.contractHistoryEntry.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'Address',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.address.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'BankCard',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.bankCard.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'IdentityDocument',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.identityDocument.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      // Neue Tabellen
      {
        name: 'PdfTemplate',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.pdfTemplate.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'ContractMeter',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.contractMeter.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'ContractDocument',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.contractDocument.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'RepresentativeAuthorization',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.representativeAuthorization.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'CustomerConsent',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.customerConsent.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'DataDeletionRequest',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.dataDeletionRequest.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'EmailLog',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.emailLog.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'AuditRetentionPolicy',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.auditRetentionPolicy.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'AuditLog',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.auditLog.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
      {
        name: 'SecurityEvent',
        restore: async (data: any[]) => {
          for (const item of data) {
            await prisma.securityEvent.upsert({
              where: { id: item.id },
              update: convertDates(item),
              create: convertDates(item),
            });
          }
        },
      },
    ];

    let totalRestored = 0;

    for (const table of restoreOrder) {
      const filePath = path.join(backupDir, `${table.name}.json`);
      const data = readJsonFile(filePath);

      if (data.length === 0) continue;

      try {
        await table.restore(data);
        totalRestored += data.length;
      } catch {
        // Fehler bei einzelner Tabelle, weitermachen
      }
    }

    // Foreign Key Checks wieder aktivieren
    await prisma.$executeRawUnsafe('SET FOREIGN_KEY_CHECKS = 1');

    // Uploads wiederherstellen
    let restoredFiles = 0;
    const uploadsBackupDir = path.join(backupDir, 'uploads');
    if (fs.existsSync(uploadsBackupDir)) {
      // Inhalte leeren, das Verzeichnis selbst NICHT löschen –
      // UPLOADS_DIR ist im Container ein Bind-Mount auf den Host und
      // `rmdir` darauf liefert EBUSY (siehe emptyDirectory()).
      emptyDirectory(UPLOADS_DIR);
      restoredFiles = copyDirectory(uploadsBackupDir, UPLOADS_DIR);
    }

    return { success: true, restoredRecords: totalRestored, restoredFiles };
  } catch (error: any) {
    try {
      await prisma.$executeRawUnsafe('SET FOREIGN_KEY_CHECKS = 1');
    } catch {}
    return { success: false, error: error.message };
  }
}

/**
 * Backup löschen
 */
export async function deleteBackup(backupName: string): Promise<{ success: boolean; error?: string }> {
  const backupDir = path.join(BACKUPS_DIR, backupName);

  if (!fs.existsSync(backupDir)) {
    return { success: false, error: 'Backup nicht gefunden' };
  }

  try {
    deleteDirectory(backupDir);
    return { success: true };
  } catch (error: any) {
    return { success: false, error: error.message };
  }
}

/**
 * Backup als ZIP-Datei erstellen und Stream zurückgeben
 */
export async function createBackupZip(backupName: string): Promise<{ stream: archiver.Archiver; filename: string } | { error: string }> {
  const backupDir = path.join(BACKUPS_DIR, backupName);

  if (!fs.existsSync(backupDir)) {
    return { error: 'Backup nicht gefunden' };
  }

  const archive = archiver('zip', { zlib: { level: 9 } });

  // Alle Dateien im Backup-Ordner hinzufügen
  archive.directory(backupDir, false);

  return {
    stream: archive,
    filename: `opencrm-backup-${backupName}.zip`,
  };
}

/**
 * ZIP-Backup hochladen und extrahieren
 */
export async function uploadBackupZip(zipBuffer: Buffer): Promise<BackupResult> {
  try {
    const zip = new AdmZip(zipBuffer);
    const entries = zip.getEntries();

    // Prüfen ob gültiges Backup (muss _backup-info.json enthalten)
    const hasBackupInfo = entries.some(entry => entry.entryName === '_backup-info.json');
    if (!hasBackupInfo) {
      return { success: false, error: 'Ungültiges Backup-Archiv: _backup-info.json fehlt' };
    }

    // Backup-Info lesen um Namen zu bestimmen
    const infoEntry = entries.find(entry => entry.entryName === '_backup-info.json');
    let backupName: string;

    try {
      const infoContent = infoEntry!.getData().toString('utf8');
      const info = JSON.parse(infoContent);
      // Backup-Name aus Timestamp generieren
      backupName = new Date(info.timestamp).toISOString().replace(/[:.]/g, '-').slice(0, 19);
    } catch {
      // Fallback: Aktueller Timestamp
      backupName = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19) + '-uploaded';
    }

    const backupDir = path.join(BACKUPS_DIR, backupName);

    // Falls Backup mit diesem Namen existiert, Suffix hinzufügen
    let finalBackupDir = backupDir;
    let suffix = 1;
    while (fs.existsSync(finalBackupDir)) {
      finalBackupDir = `${backupDir}-${suffix}`;
      suffix++;
    }

    const finalBackupName = path.basename(finalBackupDir);

    // ZIP entpacken – mit Schutz gegen Zip-Slip (../../etc/passwd Angriff).
    // Pentest 57.8 (2026-06-01): Mehrstufige Verteidigung gegen Path-
    // Traversal-Varianten, die `path.resolve` allein eventuell durchlässt
    // (z.B. Backslash-Mischformen auf Cross-OS, Null-Bytes, leere Namen,
    // explizite `..`-Segmente). Plus Zip-Bomb-Schutz per Entry-Größenlimit.
    const absBackupDir = path.resolve(finalBackupDir);
    fs.mkdirSync(absBackupDir, { recursive: true });
    const MAX_ENTRY_SIZE = 500 * 1024 * 1024; // 500 MB pro Entry
    let totalUncompressed = 0;
    const MAX_TOTAL_UNCOMPRESSED = 5 * 1024 * 1024 * 1024; // 5 GB Gesamt

    for (const entry of entries) {
      const entryName = entry.entryName;
      // Reject: leer, Null-Byte, absoluter Pfad, Backslashes (Cross-OS-
      // Confusion), expliziter `..`-Segment im Original-Namen,
      // Home-Dir-Expansion `~/`.
      if (
        !entryName
        || entryName.includes('\0')
        || entryName.includes('\\')
        || entryName.startsWith('~')
        || path.isAbsolute(entryName)
        || entryName.split('/').some((seg) => seg === '..')
      ) {
        return { success: false, error: `Ungültiger Eintrag im ZIP: ${entryName}` };
      }

      const targetPath = path.resolve(absBackupDir, entryName);
      // Zip-Slip-Check: aufgelöster Pfad muss strikt im Backup-Verzeichnis
      // liegen. path.relative gibt "../..." zurück wenn target außerhalb
      // liegt – das ist robuster als startsWith + Separator-Concat.
      const rel = path.relative(absBackupDir, targetPath);
      if (rel === '' && !entry.isDirectory) {
        return { success: false, error: `Datei-Eintrag zeigt auf das Backup-Wurzelverzeichnis` };
      }
      if (rel.startsWith('..') || path.isAbsolute(rel)) {
        return {
          success: false,
          error: `Sicherheitsverletzung im ZIP: Pfad "${entryName}" zeigt außerhalb des Backup-Verzeichnisses`,
        };
      }

      if (entry.isDirectory) {
        fs.mkdirSync(targetPath, { recursive: true });
      } else {
        // Zip-Bomb-Schutz: Entry-Größe begrenzen und Gesamt-Tracking
        if (typeof entry.header?.size === 'number') {
          if (entry.header.size > MAX_ENTRY_SIZE) {
            return { success: false, error: `Eintrag "${entryName}" überschreitet das Größenlimit von 500 MB` };
          }
          totalUncompressed += entry.header.size;
          if (totalUncompressed > MAX_TOTAL_UNCOMPRESSED) {
            return { success: false, error: `Backup-ZIP überschreitet das entpackte Gesamtlimit von 5 GB (Zip-Bomb-Schutz)` };
          }
        }
        // Zielverzeichnis sicherstellen
        fs.mkdirSync(path.dirname(targetPath), { recursive: true });
        // Datei schreiben
        fs.writeFileSync(targetPath, entry.getData());
      }
    }

    return { success: true, backupName: finalBackupName };
  } catch (error: any) {
    return { success: false, error: error.message };
  }
}

/**
 * Pfad zum Backup-Verzeichnis zurückgeben
 */
export function getBackupsDir(): string {
  return BACKUPS_DIR;
}

/**
 * Werkseinstellungen - Alle Daten löschen (außer Backups)
 *
 * Löscht alle Datenbankeinträge und Uploads, behält aber Backups.
 * Erstellt einen Admin-Benutzer und notwendige Stammdaten.
 */
export async function factoryReset(): Promise<{ success: boolean; error?: string }> {
  try {
    // Foreign Key Checks deaktivieren
    await prisma.$executeRawUnsafe('SET FOREIGN_KEY_CHECKS = 0');

    // Tabellen in korrekter Reihenfolge leeren (abhängige zuerst)
    // Detail-Tabellen
    await prisma.carInsuranceDetails.deleteMany({});
    await prisma.tvContractDetails.deleteMany({});
    await prisma.simCard.deleteMany({});
    await prisma.mobileContractDetails.deleteMany({});
    await prisma.phoneNumber.deleteMany({});
    await prisma.internetContractDetails.deleteMany({});
    await prisma.invoice.deleteMany({});
    await prisma.energyContractDetails.deleteMany({});
    await prisma.meterReading.deleteMany({});
    await prisma.contractHistoryEntry.deleteMany({});

    // E-Mail & Verträge
    await prisma.cachedEmail.deleteMany({});
    await prisma.contractTaskSubtask.deleteMany({});
    await prisma.contractTask.deleteMany({});
    await prisma.contract.deleteMany({});

    // Kunden-bezogene Daten
    await prisma.stressfreiEmail.deleteMany({});
    await prisma.meter.deleteMany({});
    await prisma.identityDocument.deleteMany({});
    await prisma.bankCard.deleteMany({});
    await prisma.address.deleteMany({});
    await prisma.customerRepresentative.deleteMany({});

    // Benutzer & Rollen (außer Admin)
    await prisma.userRole.deleteMany({});
    await prisma.user.deleteMany({});
    await prisma.customer.deleteMany({});

    // Stammdaten
    await prisma.tariff.deleteMany({});
    await prisma.provider.deleteMany({});
    await prisma.rolePermission.deleteMany({});
    await prisma.role.deleteMany({});
    await prisma.permission.deleteMany({});
    await prisma.salesPlatform.deleteMany({});
    await prisma.cancellationPeriod.deleteMany({});
    await prisma.contractDuration.deleteMany({});
    await prisma.contractCategory.deleteMany({});
    await prisma.emailProviderConfig.deleteMany({});
    await prisma.appSetting.deleteMany({});

    // Foreign Key Checks wieder aktivieren
    await prisma.$executeRawUnsafe('SET FOREIGN_KEY_CHECKS = 1');

    // Uploads-Verzeichnis leeren (aber nicht das Verzeichnis selbst)
    if (fs.existsSync(UPLOADS_DIR)) {
      const items = fs.readdirSync(UPLOADS_DIR);
      for (const item of items) {
        const itemPath = path.join(UPLOADS_DIR, item);
        const stats = fs.statSync(itemPath);
        if (stats.isDirectory()) {
          deleteDirectory(itemPath);
        } else {
          fs.unlinkSync(itemPath);
        }
      }
    }

    // Grundlegende Stammdaten neu anlegen (aus Seed)
    // Berechtigungen - muss mit seed.ts übereinstimmen!
    const resourcePermissions: Record<string, string[]> = {
      // Haupt-Ressourcen (CRUD)
      customers: ['create', 'read', 'update', 'delete'],
      contracts: ['create', 'read', 'update', 'delete'],
      users: ['create', 'read', 'update', 'delete'],
      platforms: ['create', 'read', 'update', 'delete'],
      providers: ['create', 'read', 'update', 'delete'],
      tariffs: ['create', 'read', 'update', 'delete'],
      // Lookup-Tabellen (nur lesen)
      'contract-categories': ['read'],
      'cancellation-periods': ['read'],
      'contract-durations': ['read'],
      // Einstellungen (nur lesen/ändern)
      settings: ['read', 'update'],
      // Spezial-Permissions
      developer: ['access'],
      emails: ['delete'],
    };

    for (const [resource, actions] of Object.entries(resourcePermissions)) {
      for (const action of actions) {
        await prisma.permission.create({
          data: { resource, action },
        });
      }
    }
    console.log('[FactoryReset] Berechtigungen erstellt');

    // Admin-Rolle mit allen Berechtigungen (außer developer:access)
    const allPermissions = await prisma.permission.findMany();
    const adminRole = await prisma.role.create({
      data: {
        name: 'Admin',
        description: 'Voller Zugriff auf alle Funktionen',
        permissions: {
          create: allPermissions
            .filter(p => !(p.resource === 'developer' && p.action === 'access'))
            .map(p => ({ permissionId: p.id })),
        },
      },
    });

    // Developer-Rolle - ALLE Berechtigungen inkl. developer:access
    await prisma.role.create({
      data: {
        name: 'Developer',
        description: 'Voller Zugriff inkl. Entwickler-Tools',
        permissions: {
          create: allPermissions.map(p => ({ permissionId: p.id })),
        },
      },
    });

    // Mitarbeiter-Rolle - customers, contracts + read-only auf Stammdaten
    const employeePermIds = allPermissions
      .filter(p =>
        p.resource === 'customers' ||
        p.resource === 'contracts' ||
        (p.action === 'read' && ['platforms', 'providers', 'tariffs', 'contract-categories', 'cancellation-periods', 'contract-durations'].includes(p.resource))
      )
      .map(p => p.id);
    await prisma.role.create({
      data: {
        name: 'Mitarbeiter',
        description: 'Kann Kunden und Verträge verwalten',
        permissions: {
          create: employeePermIds.map(id => ({ permissionId: id })),
        },
      },
    });

    // Nur-Lesen Rolle
    const readOnlyResources = ['customers', 'contracts', 'platforms', 'providers', 'tariffs', 'contract-categories', 'cancellation-periods', 'contract-durations'];
    const readOnlyPermIds = allPermissions
      .filter(p => p.action === 'read' && readOnlyResources.includes(p.resource))
      .map(p => p.id);
    await prisma.role.create({
      data: {
        name: 'Mitarbeiter (Nur-Lesen)',
        description: 'Kann nur lesen, keine Änderungen',
        permissions: {
          create: readOnlyPermIds.map(id => ({ permissionId: id })),
        },
      },
    });

    // Kunden-Rolle
    await prisma.role.create({
      data: {
        name: 'Kunde',
        description: 'Kann nur eigene Daten lesen',
        permissions: {
          create: readOnlyPermIds.map(id => ({ permissionId: id })),
        },
      },
    });
    console.log('[FactoryReset] Rollen erstellt');

    // Standard Admin-Benutzer erstellen
    console.log('[FactoryReset] Erstelle Admin-Benutzer...');
    const hashedPassword = await bcrypt.hash('admin', 10);
    console.log('[FactoryReset] Passwort gehasht, Admin-Rolle ID:', adminRole.id);

    const adminUser = await prisma.user.create({
      data: {
        email: 'admin@admin.com',
        password: hashedPassword,
        firstName: 'Admin',
        lastName: 'User',
        roles: {
          create: [{ roleId: adminRole.id }],
        },
      },
    });
    console.log('[FactoryReset] Admin-Benutzer erstellt mit ID:', adminUser.id);

    // Standard Kündigungsfristen (wie in seed.ts)
    const cancellationPeriods = [
      { code: '14D', description: '14 Tage' },
      { code: '1M', description: '1 Monat' },
      { code: '2M', description: '2 Monate' },
      { code: '3M', description: '3 Monate' },
      { code: '6M', description: '6 Monate' },
      { code: '12M', description: '12 Monate' },
      { code: '1W', description: '1 Woche' },
      { code: '2W', description: '2 Wochen' },
      { code: '4W', description: '4 Wochen' },
      { code: '6W', description: '6 Wochen' },
    ];
    for (const cp of cancellationPeriods) {
      await prisma.cancellationPeriod.create({ data: cp });
    }
    console.log('[FactoryReset] Kündigungsfristen erstellt');

    // Standard Vertragslaufzeiten (wie in seed.ts)
    const contractDurations = [
      { code: '1M', description: '1 Monat' },
      { code: '3M', description: '3 Monate' },
      { code: '6M', description: '6 Monate' },
      { code: '12M', description: '12 Monate' },
      { code: '24M', description: '24 Monate' },
      { code: '36M', description: '36 Monate' },
      { code: '1J', description: '1 Jahr' },
      { code: '2J', description: '2 Jahre' },
      { code: '3J', description: '3 Jahre' },
      { code: '4J', description: '4 Jahre' },
      { code: '5J', description: '5 Jahre' },
      { code: 'UNBEFRISTET', description: 'Unbefristet' },
    ];
    for (const cd of contractDurations) {
      await prisma.contractDuration.create({ data: cd });
    }
    console.log('[FactoryReset] Vertragslaufzeiten erstellt');

    // Standard Vertragstypen (wie in seed.ts - WICHTIG: Diese müssen mit den Formularen übereinstimmen!)
    const contractCategories = [
      { code: 'ELECTRICITY', name: 'Strom', icon: 'Zap', color: '#FFC107', sortOrder: 1 },
      { code: 'GAS', name: 'Gas', icon: 'Flame', color: '#FF5722', sortOrder: 2 },
      { code: 'DSL', name: 'DSL', icon: 'Wifi', color: '#2196F3', sortOrder: 3 },
      { code: 'FIBER', name: 'Glasfaser', icon: 'Cable', color: '#9C27B0', sortOrder: 4 },
      { code: 'CABLE', name: 'Kabel Internet (Coax)', icon: 'Cable', color: '#00BCD4', sortOrder: 5 },
      { code: 'MOBILE', name: 'Mobilfunk', icon: 'Smartphone', color: '#4CAF50', sortOrder: 6 },
      { code: 'TV', name: 'TV', icon: 'Tv', color: '#E91E63', sortOrder: 7 },
      { code: 'CAR_INSURANCE', name: 'KFZ-Versicherung', icon: 'Car', color: '#607D8B', sortOrder: 8 },
    ];
    for (const cat of contractCategories) {
      await prisma.contractCategory.create({ data: cat });
    }
    console.log('[FactoryReset] Vertragstypen erstellt');

    // Standard Vertriebsplattformen (wie in seed.ts)
    const platforms = ['Moon Fachhandel', 'Verivox', 'Check24', 'Eigenvermittlung'];
    for (const name of platforms) {
      await prisma.salesPlatform.create({ data: { name, isActive: true } });
    }
    console.log('[FactoryReset] Vertriebsplattformen erstellt');

    // Anbieter mit Tarifen (aus bestehender Datenbank)
    const providersWithTariffs = [
      {
        provider: { name: '1&1', portalUrl: 'https://control-center.1und1.de/' },
        tariffs: [],
      },
      {
        provider: { name: 'Congstar', portalUrl: 'https://www.congstar.de/login/' },
        tariffs: [],
      },
      {
        provider: { name: 'E wie einfach', portalUrl: '' },
        tariffs: ['Grün & Günstig Strom'],
      },
      {
        provider: { name: 'EVD Energieversogung Deutschland', portalUrl: 'https://mein.ev-d.de/selfcare' },
        tariffs: ['EVD Gas Bonus 12'],
      },
      {
        provider: { name: 'Klarmobil', portalUrl: 'https://www.klarmobil.de/login' },
        tariffs: ['Allnet Flat 30GB 5G (+00) Telekom'],
      },
      {
        provider: { name: 'O2', portalUrl: 'https://www.o2online.de/ecare/selfcare' },
        tariffs: [],
      },
      {
        provider: { name: 'Otelo', portalUrl: 'https://www.otelo.de/mein-otelo/login' },
        tariffs: [],
      },
      {
        provider: { name: 'Telekom', portalUrl: 'https://www.telekom.de/kundencenter/startseite' },
        tariffs: [],
      },
      {
        provider: { name: 'Vodafone', portalUrl: 'https://www.vodafone.de/meinvodafone/account/login' },
        tariffs: [],
      },
    ];

    for (const { provider, tariffs } of providersWithTariffs) {
      const createdProvider = await prisma.provider.create({
        data: { ...provider, isActive: true },
      });
      // Tarife für diesen Anbieter anlegen
      for (const tariffName of tariffs) {
        await prisma.tariff.create({
          data: {
            providerId: createdProvider.id,
            name: tariffName,
            isActive: true,
          },
        });
      }
    }
    console.log('[FactoryReset] Anbieter mit Tarifen erstellt');

    // Standard App-Einstellungen (wie in seed.ts)
    const appSettings = [
      { key: 'deadlineCriticalDays', value: '14' },
      { key: 'deadlineWarningDays', value: '42' },
      { key: 'deadlineOkDays', value: '90' },
      { key: 'companyName', value: 'OpenCRM' },
      { key: 'defaultEmailDomain', value: 'stressfrei-wechseln.de' },
    ];
    for (const setting of appSettings) {
      await prisma.appSetting.create({ data: setting });
    }
    console.log('[FactoryReset] App-Einstellungen erstellt');

    return { success: true };
  } catch (error: any) {
    try {
      await prisma.$executeRawUnsafe('SET FOREIGN_KEY_CHECKS = 1');
    } catch {}
    return { success: false, error: error.message };
  }
}
