import { PrismaClient } from '@prisma/client';
import bcrypt from 'bcryptjs';
import crypto from 'crypto';

const prisma = new PrismaClient();

async function main() {
  console.log('Seeding database...');

  // ==================== PERMISSIONS ====================
  // Ressourcen mit ihren erlaubten Aktionen
  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'],
    // Konfiguration (CRUD)
    'cancellation-periods': ['create', 'read', 'update', 'delete'],
    'contract-durations': ['create', 'read', 'update', 'delete'],
    'contract-categories': ['create', 'read', 'update', 'delete'],
    'email-providers': ['create', 'read', 'update', 'delete'],
    // Einstellungen (nur lesen/ändern)
    settings: ['read', 'update'],
    // Spezial-Permissions
    developer: ['access'],
    emails: ['delete'],
    // DSGVO & Audit
    audit: ['read', 'export', 'admin'],
    gdpr: ['export', 'delete', 'admin'],
  };

  const permissions: { resource: string; action: string }[] = [];
  for (const [resource, actions] of Object.entries(resourcePermissions)) {
    for (const action of actions) {
      permissions.push({ resource, action });
    }
  }

  for (const perm of permissions) {
    await prisma.permission.upsert({
      where: { resource_action: perm },
      update: {},
      create: perm,
    });
  }

  console.log(`Permissions created (${permissions.length} total)`);

  // Get all permissions
  const allPermissions = await prisma.permission.findMany();
  const customerReadPerm = allPermissions.find(
    (p) => p.resource === 'customers' && p.action === 'read'
  );
  const contractReadPerm = allPermissions.find(
    (p) => p.resource === 'contracts' && p.action === 'read'
  );
  const platformReadPerm = allPermissions.find(
    (p) => p.resource === 'platforms' && p.action === 'read'
  );
  const providerReadPerm = allPermissions.find(
    (p) => p.resource === 'providers' && p.action === 'read'
  );

  // Helper: Sync permissions for a role (adds missing, removes excess)
  async function syncRolePermissions(roleId: number, permissionIds: number[]) {
    const existing = await prisma.rolePermission.findMany({
      where: { roleId },
      select: { permissionId: true },
    });
    const existingIds = new Set(existing.map((e) => e.permissionId));
    const targetIds = new Set(permissionIds);

    // Add missing permissions
    const missing = permissionIds.filter((id) => !existingIds.has(id));
    if (missing.length > 0) {
      await prisma.rolePermission.createMany({
        data: missing.map((permissionId) => ({ roleId, permissionId })),
        skipDuplicates: true,
      });
      console.log(`  → ${missing.length} Permissions hinzugefügt für Rolle #${roleId}`);
    }

    // Remove excess permissions
    const excess = existing.filter((e) => !targetIds.has(e.permissionId)).map((e) => e.permissionId);
    if (excess.length > 0) {
      await prisma.rolePermission.deleteMany({
        where: { roleId, permissionId: { in: excess } },
      });
      console.log(`  → ${excess.length} Permissions entfernt für Rolle #${roleId}`);
    }
  }

  // Create roles
  // Admin - all permissions EXCEPT developer:access and audit/gdpr (controlled separately via checkboxes)
  const adminPermissions = allPermissions.filter(
    (p) =>
      !(p.resource === 'developer' && p.action === 'access') &&
      p.resource !== 'audit' &&
      p.resource !== 'gdpr'
  );
  const adminRole = await prisma.role.upsert({
    where: { name: 'Admin' },
    update: {},
    create: {
      name: 'Admin',
      description: 'Voller Zugriff auf alle Funktionen',
      permissions: {
        create: adminPermissions.map((p) => ({ permissionId: p.id })),
      },
    },
  });
  await syncRolePermissions(adminRole.id, adminPermissions.map((p) => p.id));

  // Developer - ALL permissions (developer:access + alles andere)
  const developerPermissions = allPermissions;
  const developerRole = await prisma.role.upsert({
    where: { name: 'Developer' },
    update: {},
    create: {
      name: 'Developer',
      description: 'Voller Zugriff inkl. Entwickler-Tools',
      permissions: {
        create: developerPermissions.map((p) => ({ permissionId: p.id })),
      },
    },
  });
  await syncRolePermissions(developerRole.id, developerPermissions.map((p) => p.id));

  // DSGVO - audit and gdpr permissions (hidden role, controlled via hasGdprAccess)
  const gdprPermissions = allPermissions.filter(
    (p) => p.resource === 'audit' || p.resource === 'gdpr'
  );
  const gdprRole = await prisma.role.upsert({
    where: { name: 'DSGVO' },
    update: {},
    create: {
      name: 'DSGVO',
      description: 'DSGVO-Zugriff: Audit-Logs und Datenschutz-Verwaltung',
      permissions: {
        create: gdprPermissions.map((p) => ({ permissionId: p.id })),
      },
    },
  });
  await syncRolePermissions(gdprRole.id, gdprPermissions.map((p) => p.id));

  // Employee - full access to customers, contracts, read access to lookup tables
  const employeePermIds = allPermissions
    .filter(
      (p) =>
        p.resource === 'customers' ||
        p.resource === 'contracts' ||
        // Read-only Zugriff auf Stammdaten und Konfiguration
        (p.action === 'read' && [
          'platforms',
          'providers',
          'tariffs',
          'cancellation-periods',
          'contract-durations',
          'contract-categories',
        ].includes(p.resource))
    )
    .map((p) => p.id);

  const employeeRole = await prisma.role.upsert({
    where: { name: 'Mitarbeiter' },
    update: {},
    create: {
      name: 'Mitarbeiter',
      description: 'Kann Kunden und Verträge verwalten',
      permissions: {
        create: employeePermIds.map((id) => ({ permissionId: id })),
      },
    },
  });
  await syncRolePermissions(employeeRole.id, employeePermIds);

  // Read-only employee - read access to main entities and lookup tables
  const readOnlyResources = [
    'customers',
    'contracts',
    'platforms',
    'providers',
    'tariffs',
    'cancellation-periods',
    'contract-durations',
    'contract-categories',
  ];
  const readOnlyPermIds = allPermissions
    .filter((p) => p.action === 'read' && readOnlyResources.includes(p.resource))
    .map((p) => p.id);

  const readOnlyRole = await prisma.role.upsert({
    where: { name: 'Mitarbeiter (Nur-Lesen)' },
    update: {},
    create: {
      name: 'Mitarbeiter (Nur-Lesen)',
      description: 'Kann nur lesen, keine Änderungen',
      permissions: {
        create: readOnlyPermIds.map((id) => ({ permissionId: id })),
      },
    },
  });
  await syncRolePermissions(readOnlyRole.id, readOnlyPermIds);

  // Customer role - read own data only (handled in middleware)
  const customerRole = await prisma.role.upsert({
    where: { name: 'Kunde' },
    update: {},
    create: {
      name: 'Kunde',
      description: 'Kann nur eigene Daten lesen',
      permissions: {
        create: readOnlyPermIds.map((id) => ({ permissionId: id })),
      },
    },
  });
  await syncRolePermissions(customerRole.id, readOnlyPermIds);

  console.log('Roles created');

  // Admin-User anlegen. Standard-Passwort darf NIEMALS in der Source-Repo
  // landen (Pentest Runde 12: "admin" verletzt die eigene 12-Zeichen-
  // Komplexitätspolicy). Stattdessen:
  //  - SEED_ADMIN_PASSWORD-ENV → wird verwendet (z.B. via docker-compose env)
  //  - sonst → zufälliges 16-Zeichen-Passwort, wird ein einziges Mal beim
  //    Seed in stdout ausgegeben. Wer das Log nicht sieht, muss
  //    Passwort-vergessen-Flow nutzen.
  // Hash-Cost: 12 (OWASP 2026), nicht mehr 10.
  function generateInitialPassword(): string {
    const upper = 'ABCDEFGHJKLMNPQRSTUVWXYZ';
    const lower = 'abcdefghijkmnopqrstuvwxyz';
    const digits = '23456789';
    const special = '!@#$%&*+=?';
    const all = upper + lower + digits + special;
    // Kryptografisch sichere Auswahl – Math.random() ist vorhersagbar
    // und reicht für ein Initial-Admin-Passwort nicht (Pentest 2026-05-20).
    const pick = (s: string) => s[crypto.randomInt(0, s.length)];
    // mind. einen aus jeder Klasse + Rest zufällig
    const chars = [pick(upper), pick(lower), pick(digits), pick(special)];
    // 28 Zeichen → Komplexität + komfortable Marge über dem 25-Zeichen-
    // Mitarbeiter-Schwellwert (Pentest Runde 13).
    for (let i = chars.length; i < 28; i++) chars.push(pick(all));
    // Fisher-Yates Shuffle mit kryptografisch starkem Random.
    for (let i = chars.length - 1; i > 0; i--) {
      const j = crypto.randomInt(0, i + 1);
      [chars[i], chars[j]] = [chars[j], chars[i]];
    }
    return chars.join('');
  }

  const envPassword = process.env.SEED_ADMIN_PASSWORD;
  const adminPlainPassword = envPassword && envPassword.length >= 25
    ? envPassword
    : generateInitialPassword();
  const hashedPassword = await bcrypt.hash(adminPlainPassword, 12);

  const adminUser = await prisma.user.upsert({
    where: { email: 'admin@admin.com' },
    update: {},
    create: {
      email: 'admin@admin.com',
      password: hashedPassword,
      firstName: 'Admin',
      lastName: 'User',
      roles: {
        create: [{ roleId: adminRole.id }],
      },
    },
  });

  console.log('========================================================');
  console.log('  Admin-User: admin@admin.com');
  if (envPassword && envPassword.length >= 25) {
    console.log('  Passwort: aus SEED_ADMIN_PASSWORD');
  } else {
    if (envPassword && envPassword.length < 25) {
      console.log('  ⚠️  SEED_ADMIN_PASSWORD < 25 Zeichen, wird ignoriert!');
    }
    console.log(`  Initial-Passwort: ${adminPlainPassword}`);
    console.log('  ⚠️  Dieses Passwort wird hier EINMAL ausgegeben!');
    console.log('  Bitte sofort nach dem ersten Login ändern.');
  }
  console.log('========================================================');

  // Create some sales platforms
  const platforms = ['Moon Fachhandel', 'Verivox', 'Check24', 'Eigenvermittlung'];
  for (const name of platforms) {
    await prisma.salesPlatform.upsert({
      where: { name },
      update: {},
      create: { name, isActive: true },
    });
  }

  console.log('Sales platforms created');

  // ==================== STANDARD PROVIDERS ====================
  const providers = [
    {
      name: 'Vodafone',
      portalUrl: 'https://www.vodafone.de/meinvodafone/account/login',
      usernameFieldName: 'username',
      passwordFieldName: 'password',
    },
    {
      name: 'Klarmobil',
      portalUrl: 'https://www.klarmobil.de/login',
      usernameFieldName: 'username',
      passwordFieldName: 'password',
    },
    {
      name: 'Otelo',
      portalUrl: 'https://www.otelo.de/mein-otelo/login',
      usernameFieldName: 'username',
      passwordFieldName: 'password',
    },
    {
      name: 'Congstar',
      portalUrl: 'https://www.congstar.de/login/',
      usernameFieldName: 'username',
      passwordFieldName: 'password',
    },
    {
      name: 'Telekom',
      portalUrl: 'https://www.telekom.de/kundencenter/startseite',
      usernameFieldName: 'username',
      passwordFieldName: 'password',
    },
    {
      name: 'O2',
      portalUrl: 'https://www.o2online.de/ecare/selfcare',
      usernameFieldName: 'username',
      passwordFieldName: 'password',
    },
    {
      name: '1&1',
      portalUrl: 'https://control-center.1und1.de/',
      usernameFieldName: 'username',
      passwordFieldName: 'password',
    },
  ];

  for (const provider of providers) {
    await prisma.provider.upsert({
      where: { name: provider.name },
      update: {
        portalUrl: provider.portalUrl,
        usernameFieldName: provider.usernameFieldName,
        passwordFieldName: provider.passwordFieldName,
      },
      create: { ...provider, isActive: true },
    });
  }

  console.log('Providers created');

  // Create contract categories (matching existing enum values)
  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 category of contractCategories) {
    await prisma.contractCategory.upsert({
      where: { code: category.code },
      update: { name: category.name, icon: category.icon, color: category.color, sortOrder: category.sortOrder },
      create: category,
    });
  }

  console.log('Contract categories created');

  // ==================== CANCELLATION PERIODS ====================
  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 period of cancellationPeriods) {
    await prisma.cancellationPeriod.upsert({
      where: { code: period.code },
      update: { description: period.description },
      create: period,
    });
  }

  console.log('Cancellation periods created');

  // ==================== CONTRACT DURATIONS ====================
  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 duration of contractDurations) {
    await prisma.contractDuration.upsert({
      where: { code: duration.code },
      update: { description: duration.description },
      create: duration,
    });
  }

  console.log('Contract durations created');

  // ==================== APP SETTINGS ====================
  const appSettings = [
    // Cockpit-Einstellungen (Fristen-Ampel)
    { key: 'deadlineCriticalDays', value: '14' },   // Rot: <= 14 Tage
    { key: 'deadlineWarningDays', value: '42' },    // Gelb: <= 42 Tage
    { key: 'deadlineOkDays', value: '90' },         // Grün: <= 90 Tage
    // Allgemeine Einstellungen
    { key: 'companyName', value: 'OpenCRM' },
    { key: 'defaultEmailDomain', value: 'stressfrei-wechseln.de' },
  ];

  for (const setting of appSettings) {
    await prisma.appSetting.upsert({
      where: { key: setting.key },
      update: {},  // Bestehende Werte nicht überschreiben
      create: setting,
    });
  }

  console.log('App settings created');

  // ==================== AUDIT RETENTION POLICIES (DSGVO) ====================
  // Standard-Policy (ohne Sensitivity)
  const existingDefault = await prisma.auditRetentionPolicy.findFirst({
    where: { resourceType: '*', sensitivity: null },
  });
  if (!existingDefault) {
    await prisma.auditRetentionPolicy.create({
      data: {
        resourceType: '*',
        sensitivity: null,
        retentionDays: 3650, // 10 Jahre
        description: 'Standard-Aufbewahrungsfrist',
        legalBasis: 'AO §147, HGB §257',
      },
    });
  }

  // Spezifische Policies mit Sensitivity
  const specificPolicies = [
    {
      resourceType: 'Authentication',
      sensitivity: 'CRITICAL' as const,
      retentionDays: 730, // 2 Jahre
      description: 'Login-Versuche und Authentifizierung',
      legalBasis: 'Sicherheitsanforderungen',
    },
    {
      resourceType: 'Customer',
      sensitivity: 'HIGH' as const,
      retentionDays: 3650, // 10 Jahre
      description: 'Kundendaten-Zugriffe',
      legalBasis: 'Steuerrecht (AO §147)',
    },
    {
      resourceType: 'Contract',
      sensitivity: 'MEDIUM' as const,
      retentionDays: 3650, // 10 Jahre
      description: 'Vertragsdaten-Zugriffe',
      legalBasis: 'Steuerrecht (AO §147)',
    },
    {
      resourceType: 'AppSetting',
      sensitivity: 'LOW' as const,
      retentionDays: 1095, // 3 Jahre
      description: 'Allgemeine Einstellungen',
      legalBasis: 'Verjährungsfrist (BGB §195)',
    },
    {
      // Stiller Token-Refresh (Routine). Ohne eigene Regel fiele diese
      // Kombination in die Auffangregel `*` mit 3650 Tagen – das Rauschen
      // wäre dann länger aufbewahrt als echte Logins (730 Tage).
      resourceType: 'Authentication',
      sensitivity: 'LOW' as const,
      retentionDays: 90,
      description: 'Routine-Auth (stiller Token-Refresh)',
      legalBasis: 'Betriebsnotwendigkeit / Datenminimierung (DSGVO Art. 5)',
    },
  ];

  for (const policy of specificPolicies) {
    await prisma.auditRetentionPolicy.upsert({
      where: {
        resourceType_sensitivity: {
          resourceType: policy.resourceType,
          sensitivity: policy.sensitivity,
        },
      },
      update: {
        retentionDays: policy.retentionDays,
        description: policy.description,
        legalBasis: policy.legalBasis,
      },
      create: policy,
    });
  }

  console.log('Audit retention policies created');

  // ==================== CONSENT HASH FÜR BESTEHENDE KUNDEN ====================
  const customersWithoutHash = await prisma.customer.findMany({
    where: { consentHash: null },
    select: { id: true },
  });

  for (const c of customersWithoutHash) {
    await prisma.customer.update({
      where: { id: c.id },
      data: { consentHash: crypto.randomUUID() },
    });
  }

  if (customersWithoutHash.length > 0) {
    console.log(`ConsentHash für ${customersWithoutHash.length} Kunden generiert`);
  }

  console.log('Seeding completed!');
}

main()
  .catch((e) => {
    console.error(e);
    process.exit(1);
  })
  .finally(async () => {
    await prisma.$disconnect();
  });
