/**
 * Database migration script using PrismaClient raw SQL.
 * Does NOT require the Prisma CLI (npx prisma) — only the runtime client.
 * Safe to run multiple times (all statements are idempotent).
 *
 * SAFETY RULES:
 * - NO deleteMany / DELETE / TRUNCATE on icon_assets, icon_categories,
 *   tenant_symbols, or features. These contain user data.
 * - All operations must be idempotent (safe to re-run).
 * - In production, destructive operations are blocked.
 */
const { PrismaClient } = require('@prisma/client')

const prisma = new PrismaClient()

async function migrate() {
  console.log('🔧 Running database migrations...')

  // ─── Step 1: Ensure enum values exist ───
  console.log('  [1/7] Ensuring enum values...')
  const enumMigrations = [
    // Role enum
    `DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_enum WHERE enumlabel = 'SERVER_ADMIN' AND enumtypid = (SELECT oid FROM pg_type WHERE typname = 'Role')) THEN ALTER TYPE "Role" ADD VALUE 'SERVER_ADMIN'; END IF; END$$;`,
    `DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_enum WHERE enumlabel = 'TENANT_ADMIN' AND enumtypid = (SELECT oid FROM pg_type WHERE typname = 'Role')) THEN ALTER TYPE "Role" ADD VALUE 'TENANT_ADMIN'; END IF; END$$;`,
    `DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_enum WHERE enumlabel = 'OPERATOR' AND enumtypid = (SELECT oid FROM pg_type WHERE typname = 'Role')) THEN ALTER TYPE "Role" ADD VALUE 'OPERATOR'; END IF; END$$;`,
    `DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_enum WHERE enumlabel = 'VIEWER' AND enumtypid = (SELECT oid FROM pg_type WHERE typname = 'Role')) THEN ALTER TYPE "Role" ADD VALUE 'VIEWER'; END IF; END$$;`,
    // DictionaryScope enum
    `DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'DictionaryScope') THEN CREATE TYPE "DictionaryScope" AS ENUM ('GLOBAL', 'TENANT'); END IF; END$$;`,
  ]
  for (const sql of enumMigrations) {
    try { await prisma.$executeRawUnsafe(sql) } catch (e) { /* enum might already exist */ }
  }

  // ─── Step 2: Migrate old enum data ───
  console.log('  [2/7] Migrating old data...')
  const dataMigrations = [
    `UPDATE users SET role = 'SERVER_ADMIN' WHERE role = 'ADMIN'`,
    `UPDATE users SET role = 'OPERATOR' WHERE role = 'EDITOR'`,
    `UPDATE tenants SET "subscriptionStatus" = 'ACTIVE' WHERE "subscriptionStatus" = 'TRIAL'`,
  ]
  for (const sql of dataMigrations) {
    try { await prisma.$executeRawUnsafe(sql) } catch (e) { /* table might not exist yet */ }
  }

  // ─── Step 3: Add missing columns (idempotent) ───
  console.log('  [3/7] Adding missing columns...')
  const columnMigrations = [
    // Tenants
    `ALTER TABLE tenants ADD COLUMN IF NOT EXISTS "logoUrl" TEXT`,
    `ALTER TABLE tenants ADD COLUMN IF NOT EXISTS "logoFileKey" TEXT`,
    `ALTER TABLE tenants ADD COLUMN IF NOT EXISTS "hiddenIconIds" TEXT[] DEFAULT '{}'`,
    `ALTER TABLE tenants ADD COLUMN IF NOT EXISTS "journalSuggestions" TEXT[] DEFAULT '{}'`,
    `ALTER TABLE tenants ADD COLUMN IF NOT EXISTS "contactEmail" TEXT`,
    `ALTER TABLE tenants ADD COLUMN IF NOT EXISTS "contactPhone" TEXT`,
    `ALTER TABLE tenants ADD COLUMN IF NOT EXISTS "address" TEXT`,
    `ALTER TABLE tenants ADD COLUMN IF NOT EXISTS "notes" TEXT`,
    `ALTER TABLE tenants ADD COLUMN IF NOT EXISTS "maxUsers" INTEGER DEFAULT 5`,
    `ALTER TABLE tenants ADD COLUMN IF NOT EXISTS "maxProjects" INTEGER DEFAULT 10`,
    // Users
    `ALTER TABLE users ADD COLUMN IF NOT EXISTS "lastLoginAt" TIMESTAMP`,
    `ALTER TABLE users ADD COLUMN IF NOT EXISTS "emailVerified" BOOLEAN DEFAULT true`,
    `ALTER TABLE users ADD COLUMN IF NOT EXISTS "isActive" BOOLEAN NOT NULL DEFAULT true`,
    `ALTER TABLE users ADD COLUMN IF NOT EXISTS "emailVerificationToken" TEXT`,
    `ALTER TABLE users ADD COLUMN IF NOT EXISTS "resetToken" TEXT`,
    `ALTER TABLE users ADD COLUMN IF NOT EXISTS "resetTokenExpiry" TIMESTAMP`,
    // Icon categories
    `ALTER TABLE icon_categories ADD COLUMN IF NOT EXISTS "isGlobal" BOOLEAN DEFAULT false`,
    `ALTER TABLE icon_categories ADD COLUMN IF NOT EXISTS "tenantId" TEXT`,
    // Projects
    `ALTER TABLE projects ADD COLUMN IF NOT EXISTS "planImageKey" TEXT`,
    `ALTER TABLE projects ADD COLUMN IF NOT EXISTS "planBounds" JSONB`,
    `ALTER TABLE projects ADD COLUMN IF NOT EXISTS "einsatzleiter" TEXT`,
    `ALTER TABLE projects ADD COLUMN IF NOT EXISTS "journalfuehrer" TEXT`,
    `ALTER TABLE projects ADD COLUMN IF NOT EXISTS "mode" TEXT NOT NULL DEFAULT 'EINSATZ'`,
    `ALTER TABLE projects ADD COLUMN IF NOT EXISTS "exerciseEvaluation" TEXT`,
    `ALTER TABLE projects ADD COLUMN IF NOT EXISTS "featuresVersion" INTEGER NOT NULL DEFAULT 0`,
    `ALTER TABLE projects ADD COLUMN IF NOT EXISTS "editingById" TEXT`,
    `ALTER TABLE projects ADD COLUMN IF NOT EXISTS "editingUserName" TEXT`,
    `ALTER TABLE projects ADD COLUMN IF NOT EXISTS "editingSessionId" TEXT`,
    `ALTER TABLE projects ADD COLUMN IF NOT EXISTS "editingStartedAt" TIMESTAMP`,
    `ALTER TABLE projects ADD COLUMN IF NOT EXISTS "editingHeartbeat" TIMESTAMP`,
    // Journal
    `ALTER TABLE journal_entries ADD COLUMN IF NOT EXISTS "isCorrected" BOOLEAN DEFAULT false`,
    `ALTER TABLE journal_entries ADD COLUMN IF NOT EXISTS "correctionOfId" TEXT`,
  ]
  let added = 0
  for (const sql of columnMigrations) {
    try {
      await prisma.$executeRawUnsafe(sql)
      added++
    } catch (e) {
      // Table might not exist yet — that's OK, prisma db push or first seed will create it
    }
  }
  console.log(`  ${added}/${columnMigrations.length} column migrations executed`)

  // ─── Step 4: Create new tables ───
  console.log('  [4/7] Creating new tables...')
  const tableMigrations = [
    // Dictionary entries table
    `CREATE TABLE IF NOT EXISTS dictionary_entries (
      id TEXT PRIMARY KEY DEFAULT gen_random_uuid(),
      word TEXT NOT NULL,
      scope "DictionaryScope" NOT NULL DEFAULT 'GLOBAL',
      "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
      "tenantId" TEXT REFERENCES tenants(id) ON DELETE CASCADE,
      UNIQUE(word, "tenantId")
    )`,
    // Rapports table
    `CREATE TABLE IF NOT EXISTS rapports (
      id TEXT PRIMARY KEY DEFAULT gen_random_uuid(),
      "reportNumber" TEXT NOT NULL,
      token TEXT NOT NULL UNIQUE DEFAULT gen_random_uuid(),
      data JSONB NOT NULL DEFAULT '{}',
      "generatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
      "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
      "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
      "projectId" TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
      "tenantId" TEXT NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
      "createdById" TEXT REFERENCES users(id) ON DELETE SET NULL,
      UNIQUE("tenantId", "reportNumber")
    )`,
    // Exercise goals (Übungs-Cockpit)
    `CREATE TABLE IF NOT EXISTS exercise_goals (
      id TEXT PRIMARY KEY DEFAULT gen_random_uuid(),
      text TEXT NOT NULL,
      status TEXT NOT NULL DEFAULT 'OPEN',
      note TEXT,
      "sortOrder" INTEGER NOT NULL DEFAULT 0,
      "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
      "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
      "projectId" TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE
    )`,
  ]
  for (const sql of tableMigrations) {
    try { await prisma.$executeRawUnsafe(sql) } catch (e) { /* table might already exist */ }
  }

  // ─── Step 5: Set safe defaults ───
  console.log('  [5/7] Setting defaults...')
  try {
    await prisma.$executeRawUnsafe(`UPDATE tenants SET "subscriptionStatus" = 'ACTIVE' WHERE "subscriptionStatus" = 'TRIAL'`)
  } catch (e) { /* ignore */ }

  // ─── Step 6: Detect orphan users (log only, no deletion) ───
  console.log('  [6/7] Checking for orphan users...')
  try {
    const orphans = await prisma.user.findMany({
      where: {
        role: { not: 'SERVER_ADMIN' },
        memberships: { none: {} },
      },
      select: { id: true, email: true, name: true },
    })
    if (orphans.length > 0) {
      console.log(`  ⚠️  Found ${orphans.length} orphan user(s) (NOT deleting — manual review required):`)
      for (const o of orphans) {
        console.log(`    - ${o.email} (${o.name})`)
      }
    } else {
      console.log('  No orphan users found')
    }
  } catch (e) {
    console.log('  Orphan check skipped:', e.message)
  }

  // ─── Step 7: Backfill logoFileKey from logoUrl ───
  console.log('  [7/7] Backfilling logoFileKey...')
  try {
    // Extract fileKey from MinIO URLs: the path after the bucket name
    // e.g. http://localhost:9002/lageplan-icons/logos/tenant-xxx.png → logos/tenant-xxx.png
    const tenants = await prisma.tenant.findMany({
      where: { logoUrl: { not: null }, logoFileKey: null },
      select: { id: true, logoUrl: true },
    })
    for (const t of tenants) {
      if (!t.logoUrl) continue
      // Try to extract the fileKey from the URL
      const match = t.logoUrl.match(/logos\/[^?]+/)
      if (match) {
        await prisma.tenant.update({
          where: { id: t.id },
          data: { logoFileKey: match[0] },
        })
      }
    }
    if (tenants.length > 0) console.log(`  Backfilled ${tenants.length} logo fileKey(s)`)
  } catch (e) {
    console.log('  Logo backfill skipped:', e.message)
  }

  // ─── Step 8: Drop unique constraint on rapports(tenantId, reportNumber) ───
  console.log('  [8] Dropping rapports unique constraint on (tenantId, reportNumber)...')
  try {
    await prisma.$executeRawUnsafe(`ALTER TABLE "rapports" DROP CONSTRAINT IF EXISTS "rapports_tenantId_reportNumber_key"`)
    console.log('  Constraint dropped (or did not exist)')
  } catch (e) {
    console.log('  Constraint drop skipped:', e.message)
  }

  // ─── Step 9: Add einsatzNr column to projects ───
  console.log('  [9] Adding einsatzNr column to projects...')
  try {
    await prisma.$executeRawUnsafe(`ALTER TABLE "projects" ADD COLUMN IF NOT EXISTS "einsatzNr" TEXT`)
    console.log('  einsatzNr column added (or already exists)')
  } catch (e) {
    console.log('  einsatzNr column skipped:', e.message)
  }

  // ─── Step 10: Make rapports.tenantId nullable ───
  console.log('  [10] Making rapports.tenantId nullable...')
  try {
    await prisma.$executeRawUnsafe(`ALTER TABLE "rapports" ALTER COLUMN "tenantId" DROP NOT NULL`)
    console.log('  rapports.tenantId is now nullable')
  } catch (e) {
    console.log('  rapports.tenantId nullable skipped:', e.message)
  }

  // ─── Step 11: Add privacy consent columns to tenants ───
  console.log('  [11] Adding privacy consent columns to tenants...')
  try {
    await prisma.$executeRawUnsafe(`ALTER TABLE "tenants" ADD COLUMN IF NOT EXISTS "privacyAccepted" BOOLEAN DEFAULT false`)
    await prisma.$executeRawUnsafe(`ALTER TABLE "tenants" ADD COLUMN IF NOT EXISTS "privacyAcceptedAt" TIMESTAMP`)
    await prisma.$executeRawUnsafe(`ALTER TABLE "tenants" ADD COLUMN IF NOT EXISTS "adminAccessAccepted" BOOLEAN DEFAULT false`)
    console.log('  Privacy consent columns added')
  } catch (e) {
    console.log('  Privacy consent columns skipped:', e.message)
  }

  // ─── Step 12: Create tenant_symbols table (CRITICAL — fail-fast) ───
  console.log('  [12] Creating tenant_symbols table...')
  try {
    await prisma.$executeRawUnsafe(`
      CREATE TABLE IF NOT EXISTS tenant_symbols (
        id TEXT PRIMARY KEY DEFAULT gen_random_uuid(),
        "isActive" BOOLEAN NOT NULL DEFAULT true,
        "tenantId" TEXT NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
        "iconId" TEXT REFERENCES icon_assets(id) ON DELETE SET NULL,
        UNIQUE("tenantId", "iconId")
      )
    `)
    console.log('  tenant_symbols table created (or already exists)')
  } catch (e) {
    console.error('  ❌ CRITICAL: tenant_symbols table creation failed:', e.message)
    throw e
  }

  // ─── Step 13: Create symbol_templates table ───
  console.log('  [13] Creating symbol_templates table...')
  try {
    await prisma.$executeRawUnsafe(`
      CREATE TABLE IF NOT EXISTS symbol_templates (
        id TEXT PRIMARY KEY DEFAULT gen_random_uuid(),
        "fileKey" TEXT NOT NULL,
        "originalFilename" TEXT NOT NULL,
        "displayName" TEXT,
        "categoryName" TEXT,
        "svgPath" TEXT,
        "metadata" JSONB NOT NULL DEFAULT '{}',
        "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
        "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
        UNIQUE("fileKey")
      )
    `)
    console.log('  symbol_templates table created (or already exists)')
  } catch (e) {
    console.log('  symbol_templates table skipped:', e.message)
  }

  // ─── Step 14: Create tenant_categories table (CRITICAL — fail-fast) ───
  console.log('  [14] Creating tenant_categories table...')
  try {
    await prisma.$executeRawUnsafe(`
      CREATE TABLE IF NOT EXISTS tenant_categories (
        id TEXT PRIMARY KEY DEFAULT gen_random_uuid(),
        "name" TEXT NOT NULL,
        "description" TEXT,
        "sortOrder" INTEGER NOT NULL DEFAULT 0,
        "isActive" BOOLEAN NOT NULL DEFAULT true,
        "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
        "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
        "tenantId" TEXT NOT NULL REFERENCES tenants(id) ON DELETE CASCADE
      )
    `)
    console.log('  tenant_categories table created (or already exists)')
  } catch (e) {
    console.error('  ❌ CRITICAL: tenant_categories table creation failed:', e.message)
    throw e
  }

  // ─── Step 14b: Ensure tenant_categories has NOT NULL defaults ───
  console.log('  [14b] Ensuring tenant_categories columns...')
  const tcColumns = [
    `ALTER TABLE tenant_categories ALTER COLUMN "isActive" SET DEFAULT true`,
    `ALTER TABLE tenant_categories ALTER COLUMN "sortOrder" SET DEFAULT 0`,
    `ALTER TABLE tenant_categories ALTER COLUMN "createdAt" SET DEFAULT CURRENT_TIMESTAMP`,
    `ALTER TABLE tenant_categories ALTER COLUMN "updatedAt" SET DEFAULT CURRENT_TIMESTAMP`,
  ]
  for (const sql of tcColumns) {
    try { await prisma.$executeRawUnsafe(sql) } catch (e) { /* ignore */ }
  }

  // ─── Step 15: Extend tenant_symbols with Phase 1 columns ───
  console.log('  [15] Extending tenant_symbols with Phase 1 columns...')
  const tenantSymbolColumns = [
    `ALTER TABLE tenant_symbols ADD COLUMN IF NOT EXISTS "name" TEXT`,
    `ALTER TABLE tenant_symbols ADD COLUMN IF NOT EXISTS "svgPath" TEXT`,
    `ALTER TABLE tenant_symbols ADD COLUMN IF NOT EXISTS "isUploaded" BOOLEAN NOT NULL DEFAULT false`,
    `ALTER TABLE tenant_symbols ADD COLUMN IF NOT EXISTS "categoryId" TEXT`,
    `ALTER TABLE tenant_symbols ADD COLUMN IF NOT EXISTS "migratedFromIconId" TEXT`,
    `ALTER TABLE tenant_symbols ADD COLUMN IF NOT EXISTS "customName" TEXT`,
    `ALTER TABLE tenant_symbols ADD COLUMN IF NOT EXISTS "sortOrder" INTEGER`,
    `ALTER TABLE tenant_symbols ADD COLUMN IF NOT EXISTS "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP`,
    `ALTER TABLE tenant_symbols ADD COLUMN IF NOT EXISTS "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP`,
  ]
  let tsAdded = 0
  for (const sql of tenantSymbolColumns) {
    try { await prisma.$executeRawUnsafe(sql); tsAdded++ } catch (e) { /* ignore */ }
  }
  console.log(`  ${tsAdded}/${tenantSymbolColumns.length} tenant_symbol columns added`)

  // ─── Step 15b: Add tenant_symbols.categoryId FK separately (idempotent) ───
  console.log('  [15b] Adding tenant_symbols.categoryId FK...')
  try {
    await prisma.$executeRawUnsafe(`
      DO $$ BEGIN
        IF NOT EXISTS (
          SELECT 1 FROM information_schema.table_constraints
          WHERE constraint_name = 'tenant_symbols_categoryId_fkey'
            AND table_name = 'tenant_symbols'
        ) THEN
          ALTER TABLE tenant_symbols
            ADD CONSTRAINT "tenant_symbols_categoryId_fkey"
            FOREIGN KEY ("categoryId") REFERENCES tenant_categories(id) ON DELETE SET NULL;
        END IF;
      EXCEPTION WHEN OTHERS THEN
        RAISE NOTICE 'categoryId FK skipped: %', SQLERRM;
      END $$;
    `)
    console.log('  ✅ tenant_symbols.categoryId FK added')
  } catch (e) {
    console.log('  categoryId FK skipped:', e.message)
  }

  // ─── Step 16: Fix tenant_symbols FK (CASCADE → SET NULL) ───
  console.log('  [16] Fixing tenant_symbols.iconId FK (CASCADE → SET NULL)...')
  try {
    // Make iconId nullable
    await prisma.$executeRawUnsafe(`ALTER TABLE tenant_symbols ALTER COLUMN "iconId" DROP NOT NULL`)
    // Drop old cascade FK and recreate with SET NULL
    await prisma.$executeRawUnsafe(`
      DO $$ BEGIN
        -- Drop existing FK constraint (name varies)
        ALTER TABLE tenant_symbols DROP CONSTRAINT IF EXISTS "tenant_symbols_iconId_fkey";
        ALTER TABLE tenant_symbols DROP CONSTRAINT IF EXISTS "tenant_symbols_iconId_icon_assets_id_fk";
        -- Recreate with SET NULL
        ALTER TABLE tenant_symbols
          ADD CONSTRAINT "tenant_symbols_iconId_fkey"
          FOREIGN KEY ("iconId") REFERENCES icon_assets(id) ON DELETE SET NULL;
      EXCEPTION WHEN OTHERS THEN
        RAISE NOTICE 'FK fix skipped: %', SQLERRM;
      END $$;
    `)
    console.log('  ✅ tenant_symbols.iconId FK is now ON DELETE SET NULL')
  } catch (e) {
    console.log('  FK fix skipped:', e.message)
  }

  // ─── Step 17: Drop unique constraint on tenant_symbols(tenantId, iconId) ───
  console.log('  [17] Dropping UNIQUE(tenantId, iconId) on tenant_symbols...')
  try {
    await prisma.$executeRawUnsafe(`ALTER TABLE tenant_symbols DROP CONSTRAINT IF EXISTS "tenant_symbols_tenantId_iconId_key"`)
    console.log('  ✅ Unique constraint dropped (duplicates now allowed)')
  } catch (e) {
    console.log('  Unique constraint drop skipped:', e.message)
  }

  // ─── Step 18: Migrate legacy tenantSymbols (name, svgPath, categoryId, migratedFromIconId) ───
  console.log('  [18] Migrating legacy tenantSymbols data...')
  try {
    const tenantsWithSymbols = await prisma.$queryRawUnsafe(`
      SELECT "tenantId", COUNT(id) as cnt FROM tenant_symbols GROUP BY "tenantId"
    `)
    let catsCreated = 0
    let symsMigrated = 0
    for (const row of tenantsWithSymbols) {
      const tenantId = row.tenantId
      // Create default category if none exists for this tenant
      let defaultCat = await prisma.$queryRawUnsafe(`
        SELECT id FROM tenant_categories WHERE "tenantId" = '${tenantId}' AND name = 'Meine Symbole' LIMIT 1
      `)
      if (!defaultCat || !defaultCat.length) {
        const newCatId = crypto.randomUUID()
        await prisma.$executeRawUnsafe(`
          INSERT INTO tenant_categories (id, "tenantId", name, "sortOrder", "createdAt", "updatedAt")
          VALUES ('${newCatId}', '${tenantId}', 'Meine Symbole', 0, NOW(), NOW())
        `)
        defaultCat = [{ id: newCatId }]
        catsCreated++
      }
      const catId = defaultCat[0].id

      // Migrate symbols: set name, svgPath, categoryId, migratedFromIconId where null
      await prisma.$executeRawUnsafe(`
        UPDATE tenant_symbols ts
        SET
          name = COALESCE(ts.name, ts."customName", ia.name, 'Unbenannt'),
          "svgPath" = COALESCE(ts."svgPath", ia."fileKey"),
          "categoryId" = COALESCE(ts."categoryId", '${catId}'),
          "migratedFromIconId" = COALESCE(ts."migratedFromIconId", ts."iconId"),
          "updatedAt" = NOW()
        FROM icon_assets ia
        WHERE ts."tenantId" = '${tenantId}'
          AND ia.id = ts."iconId"
          AND (ts.name IS NULL OR ts."svgPath" IS NULL OR ts."categoryId" IS NULL OR ts."migratedFromIconId" IS NULL)
      `)
      // Also handle symbols where iconId is already null (orphaned) — at least set category & name
      await prisma.$executeRawUnsafe(`
        UPDATE tenant_symbols
        SET
          name = COALESCE(name, "customName", 'Unbenannt'),
          "categoryId" = COALESCE("categoryId", '${catId}'),
          "updatedAt" = NOW()
        WHERE "tenantId" = '${tenantId}'
          AND (name IS NULL OR "categoryId" IS NULL)
      `)
      symsMigrated++
    }
    console.log(`  ✅ ${catsCreated} default categories created, ${symsMigrated} tenants processed`)
  } catch (e) {
    console.log('  Legacy migration skipped:', e.message)
  }

  console.log('✅ Database migrations complete')
}

migrate()
  .then(async () => { await prisma.$disconnect() })
  .catch(async (e) => {
    console.error('Migration error:', e.message)
    await prisma.$disconnect()
    process.exit(1)
  })
