/**
 * Create a dashboard user, or reset the password of an existing one.
 *
 *   npm run user:create -- <username>                 Super Admin (can change everything)
 *   npm run user:create -- <username> --role viewer   View only (no add / edit / delete / settings)
 *
 * For an existing user, --role also changes their role; without it the role is kept.
 * The password is prompted for (hidden). For non-interactive use, set
 * BTS_USER_PASSWORD in the environment instead.
 */
import "dotenv/config";
import { createInterface } from "node:readline";
import { Writable } from "node:stream";
import { createPrismaClient } from "../src/server/db-client";
import { ROLE_LABELS, parseRole, type UserRole } from "../src/lib/auth/roles";
import { PASSWORD_MAX_LENGTH, PASSWORD_MIN_LENGTH, hashPassword } from "../src/server/auth/password";

function promptHidden(question: string): Promise<string> {
  return new Promise((resolve) => {
    let muted = false;
    const output = new Writable({
      write(chunk, _encoding, callback) {
        if (!muted) process.stdout.write(chunk);
        callback();
      },
    });
    const rl = createInterface({ input: process.stdin, output, terminal: true });
    rl.question(question, (answer) => {
      rl.close();
      process.stdout.write("\n");
      resolve(answer);
    });
    muted = true;
  });
}

async function readPassword(): Promise<string> {
  if (process.env.BTS_USER_PASSWORD) return process.env.BTS_USER_PASSWORD;

  const password = await promptHidden("Password: ");
  const confirm = await promptHidden("Confirm password: ");
  if (password !== confirm) throw new Error("Passwords do not match.");
  return password;
}

const USAGE = "Usage: npm run user:create -- <username> [--role admin|viewer]   (username: 3–50 chars: a-z, 0-9, dot, dash, underscore)";

function parseArgs(argv: string[]): { username: string; role: UserRole | null } {
  const args = [...argv];
  let role: UserRole | null = null;
  const at = args.findIndex((arg) => arg === "--role" || arg.startsWith("--role="));
  if (at !== -1) {
    const value = args[at].includes("=") ? args[at].split("=")[1] : args[at + 1];
    role = parseRole(value);
    if (!role) throw new Error(`Unknown role "${value ?? ""}". Use --role admin or --role viewer.`);
    args.splice(at, args[at].includes("=") ? 1 : 2);
  }
  const username = args[0]?.trim().toLowerCase() ?? "";
  if (!/^[a-z0-9._-]{3,50}$/.test(username) || args.length > 1) throw new Error(USAGE);
  return { username, role };
}

async function main() {
  const { username, role } = parseArgs(process.argv.slice(2));
  if (!process.env.DATABASE_URL) throw new Error("DATABASE_URL is not set.");

  const password = await readPassword();
  if (password.length < PASSWORD_MIN_LENGTH || password.length > PASSWORD_MAX_LENGTH) {
    throw new Error(`Password must be ${PASSWORD_MIN_LENGTH}–${PASSWORD_MAX_LENGTH} characters.`);
  }

  const db = createPrismaClient(process.env.DATABASE_URL);
  try {
    const passwordHash = await hashPassword(password);
    const existing = await db.user.findUnique({ where: { username } });

    if (existing) {
      // A password reset (or role change) also signs the user out everywhere.
      const updated = await db.$transaction(async (tx) => {
        const user = await tx.user.update({ where: { username }, data: { passwordHash, ...(role ? { role } : {}) } });
        await tx.session.deleteMany({ where: { userId: existing.id } });
        return user;
      });
      console.log(`Password updated for "${username}" (${ROLE_LABELS[updated.role]}). Existing sessions were signed out.`);
    } else {
      const created = await db.user.create({ data: { username, passwordHash, role: role ?? "ADMIN" } });
      console.log(`User "${username}" created (${ROLE_LABELS[created.role]}).`);
    }
  } finally {
    await db.$disconnect();
  }
}

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