#!/usr/bin/env node
import { randomBytes, scryptSync } from "node:crypto";
import { Pool } from "pg";
import "dotenv/config";

const args = process.argv.slice(2);
const validRoles = new Set(["admin", "finance", "ops", "support", "viewer"]);

function getArg(name) {
  const index = args.indexOf(name);
  return index >= 0 ? args[index + 1] : undefined;
}

function hasFlag(name) {
  return args.includes(name);
}

function usage() {
  console.log(`Usage:
  npm run admin:create-user -- --email <email> --name <name> --role <admin|finance|ops|support|viewer> --password <password> [--inactive] [--rotate-password]

Examples:
  npm run admin:create-user -- --email finance@example.com --name "Finance Ops" --role finance --password "change-this"
  npm run admin:create-user -- --email admin@example.com --name "Main Admin" --role admin --password "change-this" --rotate-password

Environment:
  DATABASE_URL or PGHOST/PGPORT/PGUSER/PGPASSWORD/PGDATABASE
`);
}

function validatePasswordPolicy(value) {
  const failures = [];
  if (value.length < 14) failures.push("at least 14 characters");
  if (!/[a-z]/.test(value)) failures.push("a lowercase letter");
  if (!/[A-Z]/.test(value)) failures.push("an uppercase letter");
  if (!/[0-9]/.test(value)) failures.push("a number");
  if (!/[^A-Za-z0-9]/.test(value)) failures.push("a symbol");
  if (/password|admin|qris|soundbox|change-this/i.test(value)) failures.push("no obvious product/default words");
  return failures;
}

const email = (getArg("--email") || "").trim().toLowerCase();
const name = (getArg("--name") || "").trim();
const roleName = (getArg("--role") || "").trim().toLowerCase();
const password = getArg("--password") || "";
const status = hasFlag("--inactive") ? "inactive" : "active";
const rotatePassword = hasFlag("--rotate-password");

if (hasFlag("--help") || hasFlag("-h")) {
  usage();
  process.exit(0);
}

const errors = [];
if (!email || !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email)) {
  errors.push("--email must be a valid email address");
}
if (!name) {
  errors.push("--name is required");
}
if (!validRoles.has(roleName)) {
  errors.push("--role must be one of admin, finance, ops, support, viewer");
}
const passwordFailures = validatePasswordPolicy(password);
if (passwordFailures.length) {
  errors.push(`--password must include ${passwordFailures.join(", ")}`);
}
if (errors.length) {
  usage();
  for (const error of errors) {
    console.error(`ERROR ${error}`);
  }
  process.exit(1);
}

function makePool() {
  if (process.env.DATABASE_URL) {
    return new Pool({ connectionString: process.env.DATABASE_URL });
  }
  return new Pool({
    host: process.env.PGHOST || "127.0.0.1",
    port: Number(process.env.PGPORT || 5432),
    user: process.env.PGUSER || "postgres",
    password: process.env.PGPASSWORD || "postgres",
    database: process.env.PGDATABASE || "qris_soundbox_platform"
  });
}

function hashPassword(value) {
  const salt = randomBytes(16).toString("hex");
  const keyLength = 64;
  const derived = scryptSync(value, salt, keyLength).toString("hex");
  return `scrypt$${salt}$${keyLength}$${derived}`;
}

function userIdFromEmail(value) {
  return `user_${value.replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 64)}`;
}

const pool = makePool();

try {
  const role = await pool.query("SELECT * FROM roles WHERE name = $1", [roleName]);
  if (!role.rows.length) {
    throw new Error(`role '${roleName}' not found. Run npm run db:migrate so schema/seed roles are initialized.`);
  }

  const existing = await pool.query("SELECT id, email FROM users WHERE LOWER(email) = LOWER($1)", [email]);
  const passwordHash = hashPassword(password);
  const now = new Date().toISOString();

  if (existing.rows.length) {
    const updatePasswordSql = rotatePassword ? ", password_hash = $6" : "";
    const params = rotatePassword
      ? [name, role.rows[0].id, status, now, email, passwordHash]
      : [name, role.rows[0].id, status, now, email];
    await pool.query(
      `UPDATE users
          SET name = $1,
              role_id = $2,
              status = $3,
              created_at = COALESCE(created_at, $4)
              ${updatePasswordSql}
        WHERE LOWER(email) = LOWER($5)`,
      params
    );
    console.log(`Updated admin user ${email} role=${roleName} status=${status} password_rotated=${rotatePassword}`);
  } else {
    await pool.query(
      `INSERT INTO users (id, name, email, password_hash, role_id, status, created_at)
       VALUES ($1,$2,$3,$4,$5,$6,$7)`,
      [userIdFromEmail(email), name, email, passwordHash, role.rows[0].id, status, now]
    );
    console.log(`Created admin user ${email} role=${roleName} status=${status}`);
  }
} finally {
  await pool.end();
}
