/**
 * Seed-Script: Factory-Defaults aus backend/factory-defaults/ in die DB einspielen.
 *
 * - Liest alle JSON-Dateien aus den Unterordnern (providers/, contract-meta/, pdf-templates/)
 * - Merged mehrere Dateien pro Kategorie automatisch
 * - Nutzt Prisma upsert → idempotent, kann mehrfach aufgerufen werden
 * - Kopiert PDF-Dateien aus pdf-templates/ nach uploads/pdf-templates/
 *
 * Aufruf:
 *   npm run seed:defaults
 */
import fs from 'fs';
import path from 'path';
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

// ROOT kann via FACTORY_DEFAULTS_DIR überschrieben werden (Container-Bootstrap
// mit eingebauten Defaults aus dem Image).
const ROOT = process.env.FACTORY_DEFAULTS_DIR
  ? path.resolve(process.env.FACTORY_DEFAULTS_DIR)
  : path.join(process.cwd(), 'factory-defaults');
const UPLOADS_ROOT = path.join(process.cwd(), 'uploads');
const PDF_UPLOAD_DIR = path.join(UPLOADS_ROOT, 'pdf-templates');

interface ProviderDef {
  name: string;
  portalUrl?: string | null;
  usernameFieldName?: string | null;
  passwordFieldName?: string | null;
  isActive?: boolean;
  tariffs?: { name: string; isActive?: boolean }[];
}

interface CancellationPeriodDef {
  code: string;
  description: string;
  isActive?: boolean;
}

interface ContractDurationDef {
  code: string;
  description: string;
  isActive?: boolean;
}

interface ContractCategoryDef {
  code: string;
  name: string;
  icon?: string | null;
  color?: string | null;
  sortOrder?: number;
  isActive?: boolean;
}

interface PdfTemplateDef {
  name: string;
  description?: string | null;
  providerName?: string | null;
  originalName: string;
  fieldMapping: any;
  phoneFieldPrefix?: string | null;
  maxPhoneFields?: number | null;
  isActive?: boolean;
  pdfFilename: string; // Dateiname im pdf-templates/-Ordner
}

interface AppSettingDef {
  key: string;
  value: string;
}

// Whitelist – muss synchron zu factoryDefaults.service.ts sein.
const FACTORY_DEFAULT_APP_SETTING_KEYS = new Set([
  'privacyPolicyHtml',
  'authorizationTemplateHtml',
  'imprintHtml',
  'websitePrivacyPolicyHtml',
]);

/**
 * Liest alle *.json Dateien aus einem Ordner und gibt die zusammengeführten Arrays zurück.
 */
function readJsonArrays<T>(dir: string): T[] {
  if (!fs.existsSync(dir)) return [];
  const files = fs.readdirSync(dir).filter((f) => f.endsWith('.json'));
  const result: T[] = [];
  for (const f of files) {
    const content = fs.readFileSync(path.join(dir, f), 'utf-8');
    try {
      const data = JSON.parse(content);
      if (Array.isArray(data)) {
        result.push(...data);
      }
    } catch (e) {
      console.warn(`⚠  Konnte ${f} nicht parsen – überspringe.`);
    }
  }
  return result;
}

/**
 * Dedupliziert Einträge per unique-Key (letzter Eintrag gewinnt).
 */
function dedupe<T>(items: T[], keyFn: (item: T) => string): T[] {
  const map = new Map<string, T>();
  for (const item of items) {
    map.set(keyFn(item), item);
  }
  return Array.from(map.values());
}

async function seedProviders() {
  const items = dedupe(readJsonArrays<ProviderDef>(path.join(ROOT, 'providers')), (p) => p.name);
  if (items.length === 0) {
    console.log('  providers/ – keine Einträge gefunden');
    return;
  }

  let providerCount = 0;
  let tariffCount = 0;

  for (const p of items) {
    const provider = await prisma.provider.upsert({
      where: { name: p.name },
      update: {
        portalUrl: p.portalUrl ?? null,
        usernameFieldName: p.usernameFieldName ?? null,
        passwordFieldName: p.passwordFieldName ?? null,
        isActive: p.isActive ?? true,
      },
      create: {
        name: p.name,
        portalUrl: p.portalUrl ?? null,
        usernameFieldName: p.usernameFieldName ?? null,
        passwordFieldName: p.passwordFieldName ?? null,
        isActive: p.isActive ?? true,
      },
    });
    providerCount++;

    if (p.tariffs && p.tariffs.length > 0) {
      for (const t of p.tariffs) {
        await prisma.tariff.upsert({
          where: { providerId_name: { providerId: provider.id, name: t.name } },
          update: { isActive: t.isActive ?? true },
          create: {
            providerId: provider.id,
            name: t.name,
            isActive: t.isActive ?? true,
          },
        });
        tariffCount++;
      }
    }
  }
  console.log(`  ✓ Anbieter: ${providerCount}, Tarife: ${tariffCount}`);
}

async function seedCancellationPeriods() {
  const items = dedupe(
    readJsonArrays<CancellationPeriodDef>(path.join(ROOT, 'contract-meta')).filter((i) => i.code && i.description),
    (i) => i.code,
  );
  // Nur die relevanten Objekte (CancellationPeriod hat code+description, keine 'name')
  const relevant = items.filter((i) => 'code' in i && 'description' in i && !('name' in i) && !('icon' in i));
  if (relevant.length === 0) {
    console.log('  cancellation-periods – keine Einträge');
    return;
  }

  for (const c of relevant) {
    await prisma.cancellationPeriod.upsert({
      where: { code: c.code },
      update: { description: c.description, isActive: c.isActive ?? true },
      create: { code: c.code, description: c.description, isActive: c.isActive ?? true },
    });
  }
  console.log(`  ✓ Kündigungsfristen: ${relevant.length}`);
}

async function seedContractDurations() {
  const items = dedupe(
    readJsonArrays<ContractDurationDef>(path.join(ROOT, 'contract-meta')).filter((i) => i.code && i.description),
    (i) => i.code,
  );
  const relevant = items.filter((i) => !('name' in i) && !('icon' in i));
  if (relevant.length === 0) {
    console.log('  contract-durations – keine Einträge');
    return;
  }

  for (const d of relevant) {
    await prisma.contractDuration.upsert({
      where: { code: d.code },
      update: { description: d.description, isActive: d.isActive ?? true },
      create: { code: d.code, description: d.description, isActive: d.isActive ?? true },
    });
  }
  console.log(`  ✓ Laufzeiten: ${relevant.length}`);
}

async function seedContractCategories() {
  const items = dedupe(
    readJsonArrays<ContractCategoryDef>(path.join(ROOT, 'contract-meta')).filter((i) => i.code && (i as any).name),
    (i) => i.code,
  );
  if (items.length === 0) {
    console.log('  contract-categories – keine Einträge');
    return;
  }

  for (const c of items) {
    await prisma.contractCategory.upsert({
      where: { code: c.code },
      update: {
        name: c.name,
        icon: c.icon ?? null,
        color: c.color ?? null,
        sortOrder: c.sortOrder ?? 0,
        isActive: c.isActive ?? true,
      },
      create: {
        code: c.code,
        name: c.name,
        icon: c.icon ?? null,
        color: c.color ?? null,
        sortOrder: c.sortOrder ?? 0,
        isActive: c.isActive ?? true,
      },
    });
  }
  console.log(`  ✓ Vertragskategorien: ${items.length}`);
}

async function seedPdfTemplates() {
  const items = dedupe(
    readJsonArrays<PdfTemplateDef>(path.join(ROOT, 'pdf-templates')),
    (t) => t.name,
  );
  if (items.length === 0) {
    console.log('  pdf-templates – keine Einträge');
    return;
  }

  // Upload-Verzeichnis sicherstellen
  if (!fs.existsSync(PDF_UPLOAD_DIR)) {
    fs.mkdirSync(PDF_UPLOAD_DIR, { recursive: true });
  }

  let count = 0;
  let skipped = 0;

  for (const t of items) {
    const srcPdf = path.join(ROOT, 'pdf-templates', t.pdfFilename);
    if (!fs.existsSync(srcPdf)) {
      console.warn(`  ⚠  PDF fehlt: ${t.pdfFilename} – Template "${t.name}" übersprungen`);
      skipped++;
      continue;
    }

    // PDF nach uploads/pdf-templates/ kopieren (mit eindeutigem Namen)
    const ext = path.extname(t.originalName || t.pdfFilename) || '.pdf';
    const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9);
    const destFilename = `seed-${t.name.replace(/[^a-zA-Z0-9]/g, '-')}-${uniqueSuffix}${ext}`;
    const destPdf = path.join(PDF_UPLOAD_DIR, destFilename);
    const relativePath = `/uploads/pdf-templates/${destFilename}`;

    fs.copyFileSync(srcPdf, destPdf);

    const fieldMappingJson = JSON.stringify(t.fieldMapping || {});

    // Bei existierendem Template: alten Pfad löschen, wenn Neuimport
    const existing = await prisma.pdfTemplate.findUnique({ where: { name: t.name } });
    if (existing?.templatePath) {
      const oldRel = existing.templatePath.startsWith('/uploads/')
        ? existing.templatePath.substring('/uploads/'.length)
        : existing.templatePath;
      const oldAbs = path.join(UPLOADS_ROOT, oldRel);
      if (fs.existsSync(oldAbs)) {
        try {
          fs.unlinkSync(oldAbs);
        } catch {
          // ignore
        }
      }
    }

    await prisma.pdfTemplate.upsert({
      where: { name: t.name },
      update: {
        description: t.description ?? null,
        providerName: t.providerName ?? null,
        templatePath: relativePath,
        originalName: t.originalName,
        fieldMapping: fieldMappingJson,
        phoneFieldPrefix: t.phoneFieldPrefix ?? null,
        maxPhoneFields: t.maxPhoneFields ?? 8,
        isActive: t.isActive ?? true,
      },
      create: {
        name: t.name,
        description: t.description ?? null,
        providerName: t.providerName ?? null,
        templatePath: relativePath,
        originalName: t.originalName,
        fieldMapping: fieldMappingJson,
        phoneFieldPrefix: t.phoneFieldPrefix ?? null,
        maxPhoneFields: t.maxPhoneFields ?? 8,
        isActive: t.isActive ?? true,
      },
    });
    count++;
  }

  console.log(`  ✓ PDF-Vorlagen: ${count}${skipped > 0 ? ` (${skipped} übersprungen)` : ''}`);
}

async function seedAppSettings() {
  const items = readJsonArrays<AppSettingDef>(path.join(ROOT, 'app-settings'));
  if (items.length === 0) {
    console.log('  app-settings – keine Einträge');
    return;
  }
  let count = 0;
  let skipped = 0;
  for (const s of items) {
    if (!s.key || typeof s.value !== 'string') continue;
    if (!FACTORY_DEFAULT_APP_SETTING_KEYS.has(s.key)) {
      console.warn(`  ⚠  AppSetting-Key '${s.key}' nicht auf Whitelist – übersprungen`);
      skipped++;
      continue;
    }
    await prisma.appSetting.upsert({
      where: { key: s.key },
      update: { value: s.value },
      create: { key: s.key, value: s.value },
    });
    count++;
  }
  console.log(`  ✓ HTML-Templates: ${count}${skipped > 0 ? ` (${skipped} übersprungen)` : ''}`);
}

async function main() {
  console.log('\n📦 Factory-Defaults werden eingespielt...\n');

  if (!fs.existsSync(ROOT)) {
    console.error(`❌ Ordner nicht gefunden: ${ROOT}`);
    console.error('   Lege Export-Dateien unter backend/factory-defaults/ ab.');
    process.exit(1);
  }

  await seedProviders();
  await seedCancellationPeriods();
  await seedContractDurations();
  await seedContractCategories();
  await seedPdfTemplates();
  await seedAppSettings();

  console.log('\n✅ Factory-Defaults erfolgreich eingespielt.\n');
}

main()
  .catch((e) => {
    console.error('\n❌ Fehler beim Einspielen:', e);
    process.exit(1);
  })
  .finally(() => prisma.$disconnect());
