/**
 * Bootstrap the ONE system administrator account (documented procedure D-6).
 * Creates an active account with a temporary password that MUST be changed on first login.
 *
 * Usage:
 *   npm run bootstrap:admin -- --username <name> --full-name "<arabic name>" [--password <temp>] [--email <email>]
 * If --password is omitted, a random one is printed once.
 * Refuses to run if a system_admin already exists (one account only — AUTHORIZATION_MATRIX §7).
 */
import readline from "node:readline/promises";
import { one, pool } from "../src/db/client.js";
import { hashPassword, randomToken } from "../src/lib/crypto.js";

interface Args {
  username?: string;
  fullName?: string;
  password?: string;
  email?: string;
  force?: boolean;
}

function parseArgs(argv: string[]): Args {
  const args: Args = {};
  for (let i = 0; i < argv.length; i++) {
    const a = argv[i];
    if (a === "--username") args.username = argv[++i];
    else if (a === "--full-name") args.fullName = argv[++i];
    else if (a === "--password") args.password = argv[++i];
    else if (a === "--email") args.email = argv[++i];
    else if (a === "--force") args.force = true;
    else {
      console.error(`Unknown argument: ${a}`);
      process.exit(1);
    }
  }
  return args;
}

const usernamePattern = /^[a-zA-Z0-9_.-]{3,64}$/;

async function main(): Promise<void> {
  const args = parseArgs(process.argv.slice(2));
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });

  const existing = await one<{ count: string }>(
    `SELECT COUNT(*)::text AS count FROM user_roles ur JOIN roles r ON r.id = ur.role_id WHERE r.code = 'system_admin' AND ur.revoked_at IS NULL`,
  );
  const alreadyBootstrapped = Number(existing?.count ?? 0) > 0;
  if (alreadyBootstrapped && !args.force) {
    console.error("A system_admin account already exists. Only ONE such account is allowed (AUTHORIZATION_MATRIX §7).");
    console.error("Override deliberately with --force only if you are rotating the account.");
    process.exit(1);
  }

  const interactive = Boolean(process.stdin.isTTY);
  const username = args.username ?? (interactive ? await rl.question("Admin username (latin letters/digits/._-): ") : undefined);
  if (!username) {
    console.error("Missing --username (required when running non-interactively).");
    process.exit(1);
  }
  if (!usernamePattern.test(username)) {
    console.error("Invalid username.");
    process.exit(1);
  }
  const taken = await one(`SELECT id FROM users WHERE lower(username) = lower($1)`, [username]);
  if (taken) {
    console.error("Username already taken.");
    process.exit(1);
  }
  const fullName = args.fullName ?? (interactive ? await rl.question("Full name (Arabic): ") : undefined);
  if (!fullName || fullName.trim().length < 3) {
    console.error("Full name is required (--full-name).");
    process.exit(1);
  }
  const email = args.email ?? (interactive ? await rl.question("Email (optional, Enter to skip): ") : "");

  let password = args.password;
  if (!password) {
    password = randomToken(12);
    console.log(`\nGenerated temporary password: ${password}\n(it is shown once; the account must change it on first login)`);
  }

  const hash = await hashPassword(password);
  const client = await pool.connect();
  try {
    await client.query("BEGIN");
    const user = await client.query<{ id: number }>(
      `INSERT INTO users (username, full_name, email, password_hash, is_active, must_change_password)
       VALUES ($1, $2, $3, $4, TRUE, TRUE) RETURNING id`,
      [username.trim(), fullName.trim(), email.trim() || null, hash],
    );
    const userId = user.rows[0]!.id;
    await client.query(
      `INSERT INTO user_roles (user_id, role_id, reason)
       SELECT $1, id, 'bootstrap: initial system admin (D-6)'
         FROM roles WHERE code = 'system_admin'`,
      [userId],
    );
    await client.query(
      `INSERT INTO audit_logs (actor_user_id, action, entity_type, entity_id, changed_fields, context)
       VALUES ($1, 'admin.bootstrap', 'user', $1, '{"account":"system_admin"}'::jsonb, '{"procedure":"D-6"}'::jsonb)`,
      [userId],
    );
    await client.query("COMMIT");
    console.log(`\nSystem admin created (id=${userId}, username=${username}). MUST change password on first login.`);
  } catch (error) {
    await client.query("ROLLBACK");
    throw error;
  } finally {
    client.release();
    rl.close();
    await pool.end();
  }
}

main().catch((error) => {
  console.error(error instanceof Error ? error.message : error);
  process.exit(1);
});
