import fs from "fs/promises";
import path from "path";
import argon2 from "argon2";
import pkg from "pg";

const { Pool } = pkg;

const MIGRATIONS_DIR = path.resolve(process.cwd(), "src/db/migrations");
const BOOTSTRAP_SQL = path.resolve(process.cwd(), "src/db/000_migration_table.sql");

function isSqlFile(name) {
  return name.endsWith(".sql");
}

async function ensureMigrationTable(client) {
  const sql = await fs.readFile(BOOTSTRAP_SQL, "utf8");
  await client.query(sql);
}

async function getAppliedMigrations(client) {
  const { rows } = await client.query(
    "SELECT file_name FROM schema_migration ORDER BY file_name"
  );
  return new Set(rows.map((r) => r.file_name));
}

async function applyMigration(client, fileName, sql) {
  await client.query("BEGIN");
  try {
    await client.query(sql);
    await client.query(
      "INSERT INTO schema_migration (file_name) VALUES ($1) ON CONFLICT (file_name) DO NOTHING",
      [fileName]
    );
    await client.query("COMMIT");
    console.log(`[db] migration applied: ${fileName}`);
  } catch (error) {
    await client.query("ROLLBACK");
    throw new Error(`Migration failed: ${fileName}\n${error.message}`);
  }
}

async function ensureDefaultCommittee(client) {
  await client.query(
    `
    INSERT INTO committee (name, committee_type)
    SELECT 'Gesamtbetriebsrat', 'general'
    WHERE NOT EXISTS (
      SELECT 1 FROM committee WHERE name = 'Gesamtbetriebsrat'
    )
    `
  );
}

async function ensureDefaultAdmin(client) {
  const adminEmail = process.env.BOOTSTRAP_ADMIN_EMAIL || "admin@br-office.local";
  const adminPassword = process.env.BOOTSTRAP_ADMIN_PASSWORD || "adminadmin";

  const existing = await client.query(
    `
    SELECT id, email, role
    FROM app_user
    WHERE LOWER(email) = LOWER($1)
    LIMIT 1
    `,
    [adminEmail]
  );

  if (existing.rowCount > 0) {
    console.log(`[db] default admin already exists: ${adminEmail}`);
    return;
  }

  const committee = await client.query(
    `
    SELECT id
    FROM committee
    ORDER BY id
    LIMIT 1
    `
  );

  const committeeId = committee.rows[0]?.id ?? null;
  const passwordHash = await argon2.hash(adminPassword);
  await client.query(
    `
    INSERT INTO app_user (
      full_name,
      email,
      password_hash,
      role,
      committee_id,
      is_active,
      security_classification,
      force_password_reset,
      two_factor_enabled
    )
    VALUES ($1, $2, $3, 'admin', $4, TRUE, 'restricted', TRUE, FALSE)
    `,
    ["Systemadministrator", adminEmail, passwordHash, committeeId]
  );

  console.log(`[db] default admin created: ${adminEmail}`);
}

export async function runBootstrapMigrations() {
  const pool = new Pool({
    connectionString: process.env.DATABASE_URL,
  });

  const client = await pool.connect();

  try {
    console.log("[db] bootstrap started");
    await ensureMigrationTable(client);

    const files = (await fs.readdir(MIGRATIONS_DIR))
      .filter(isSqlFile)
      .sort((a, b) => a.localeCompare(b));

    const applied = await getAppliedMigrations(client);

    for (const fileName of files) {
      if (applied.has(fileName)) {
        continue;
      }

      const fullPath = path.join(MIGRATIONS_DIR, fileName);
      const sql = await fs.readFile(fullPath, "utf8");
      await applyMigration(client, fileName, sql);
    }

    // Nur ausführen, wenn Kernschema vorhanden ist
    const tableCheck = await client.query(`
      SELECT EXISTS (
        SELECT 1
        FROM information_schema.tables
        WHERE table_schema = 'public'
          AND table_name = 'app_user'
      ) AS exists
    `);

    if (tableCheck.rows[0]?.exists) {
      await ensureDefaultCommittee(client);
      await ensureDefaultAdmin(client);
    } else {
      console.warn("[db] app_user table missing; admin bootstrap skipped");
    }

    console.log("[db] bootstrap finished");
  } finally {
    client.release();
    await pool.end();
  }
}
