const readline = require('readline');
const bcrypt = require('bcryptjs');
const { logAudit } = require('../services/auditService.cts');

function line(char = '─', len = 56) {
  return char.repeat(len);
}

function prompt(question) {
  return new Promise((resolve) => {
    const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
    rl.question(question, (answer) => {
      rl.close();
      resolve(answer.trim());
    });
  });
}

function promptPassword(label) {
  if (!process.stdin.isTTY) {
    return prompt(label);
  }

  return new Promise((resolve) => {
    process.stdout.write(label);
    process.stdin.setRawMode(true);
    process.stdin.resume();
    process.stdin.setEncoding('utf8');
    let pw = '';

    const handler = (ch) => {
      if (ch === '\n' || ch === '\r' || ch === '') {
        process.stdout.write('\n');
        process.stdin.setRawMode(false);
        process.stdin.pause();
        process.stdin.removeListener('data', handler);
        resolve(pw);
      } else if (ch === '') {
        process.exit(0);
      } else if (ch === '') {
        if (pw.length > 0) {
          pw = pw.slice(0, -1);
          process.stdout.write('\b \b');
        }
      } else {
        pw += ch;
        process.stdout.write('*');
      }
    };

    process.stdin.on('data', handler);
  });
}

async function createUser(db, username, password, role) {
  const hash = await bcrypt.hash(password, 12);
  db.prepare(
    `
    INSERT INTO users (username, password_hash, role, first_login, must_change_password, is_default_admin)
    VALUES (?, ?, ?, 0, 0, ?)
  `,
  ).run(username, hash, role, role === 'admin' ? 1 : 0);
}

async function runFromEnv(db) {
  const adminUser = process.env.INIT_ADMIN_USER;
  const adminPass = process.env.INIT_ADMIN_PASS;

  const regularUser = process.env.INIT_REGULAR_USER;
  const regularPass = process.env.INIT_REGULAR_PASS;

  const errors = [];
  if (!adminUser || adminUser.length < 3)
    errors.push('INIT_ADMIN_USER must be at least 3 characters');
  if (!adminPass || adminPass.length < 8)
    errors.push('INIT_ADMIN_PASS must be at least 8 characters');

  if (regularUser && !regularPass)
    errors.push('INIT_REGULAR_PASS required when INIT_REGULAR_USER is set');
  if (regularPass && !regularUser)
    errors.push('INIT_REGULAR_USER required when INIT_REGULAR_PASS is set');
  if (regularUser && regularUser.length < 3)
    errors.push('INIT_REGULAR_USER must be at least 3 characters');
  if (regularPass && regularPass.length < 8)
    errors.push('INIT_REGULAR_PASS must be at least 8 characters');

  if (errors.length) {
    console.error('\n[first-run] Environment variable setup failed:');
    errors.forEach((e) => console.error('  ✗ ' + e));
    console.error('\nSet both vars: INIT_ADMIN_USER and INIT_ADMIN_PASS');
    console.error(
      'Optionally set: INIT_REGULAR_USER and INIT_REGULAR_PASS for a non-admin test user',
    );
    console.error('Then open the web UI to create your first user account.\n');
    process.exit(1);
  }

  const existingAdmin = db
    .prepare('SELECT id FROM users WHERE role = ? AND username = ?')
    .get('admin', adminUser);
  const adminHash = await bcrypt.hash(adminPass, 12);

  if (existingAdmin) {
    // Update existing admin's password
    db.prepare(
      'UPDATE users SET password_hash = ?, first_login = 0, must_change_password = 0 WHERE id = ?',
    ).run(adminHash, existingAdmin.id);
    logAudit({
      user_id: existingAdmin.id,
      action: 'seed.flag_reset',
      entity_type: 'user',
      details: {
        username: adminUser,
        flags: ['first_login', 'must_change_password'],
        source: 'first-run-env',
      },
    });
    console.log(`[first-run] Admin password updated for "${adminUser}".`);
  } else {
    // Create new admin user
    db.prepare(
      `
      INSERT INTO users (username, password_hash, role, first_login, must_change_password, is_default_admin)
      VALUES (?, ?, ?, 0, 0, 1)
    `,
    ).run(adminUser, adminHash, 'admin');
    console.log(`[first-run] Admin "${adminUser}" created.`);
  }

  // Handle regular user creation if specified
  if (regularUser && regularPass) {
    const existingRegular = db
      .prepare('SELECT id FROM users WHERE role = ? AND username = ?')
      .get('user', regularUser);
    const regularHash = await bcrypt.hash(regularPass, 12);

    if (existingRegular) {
      // Update existing regular user's password
      db.prepare(
        'UPDATE users SET password_hash = ?, first_login = 0, must_change_password = 0 WHERE id = ?',
      ).run(regularHash, existingRegular.id);
      logAudit({
        user_id: existingRegular.id,
        action: 'seed.flag_reset',
        entity_type: 'user',
        details: {
          username: regularUser,
          flags: ['first_login', 'must_change_password'],
          source: 'first-run-env',
        },
      });
      console.log(`[first-run] Regular user password updated for "${regularUser}".`);
    } else {
      // Create new regular user
      db.prepare(
        `
        INSERT INTO users (username, password_hash, role, first_login, must_change_password, is_default_admin)
        VALUES (?, ?, ?, 0, 0, 0)
      `,
      ).run(regularUser, regularHash, 'user');
      console.log(`[first-run] Regular user "${regularUser}" created.`);
    }
  }

  console.log('[first-run] You can now log in with these credentials.');
}

async function run(db) {
  // Non-interactive Docker path: env vars provided
  if (process.env.INIT_ADMIN_USER && process.env.INIT_ADMIN_PASS) {
    return runFromEnv(db);
  }

  // No TTY and no env vars
  if (!process.stdin.isTTY) {
    console.error(
      [
        '',
        '[first-run] No admin account found and no TTY available for interactive setup.',
        'Set these environment variables to create the admin automatically:',
        '  INIT_ADMIN_USER=admin',
        '  INIT_ADMIN_PASS=<min 8 chars>',
        '',
        'Or run interactively: docker run -it ...',
        '',
      ].join('\n'),
    );
    process.exit(1);
  }

  // Interactive terminal wizard (admin only)
  console.log('\n' + line('═'));
  console.log('  Bill Tracker — First Run Setup');
  console.log(line('═'));
  console.log(`
No accounts found. Create an admin account to get started.

About the admin account:
  ✓  Can create user accounts via the web interface
  ✓  Can reset user passwords
  ✗  Cannot view, edit, or access any bills, payments,
     or financial data — ever
`);

  console.log(line());
  console.log('  Create Admin Account');
  console.log(line());
  console.log('');

  let username, password, confirm;

  while (true) {
    username = await prompt('  Username: ');
    if (username.length >= 3) break;
    console.log('  Username must be at least 3 characters.\n');
  }

  while (true) {
    password = await promptPassword('  Password: ');
    if (password.length < 8) {
      console.log('  Password must be at least 8 characters.\n');
      continue;
    }
    confirm = await promptPassword('  Confirm:  ');
    if (password !== confirm) {
      console.log('  Passwords do not match.\n');
      continue;
    }
    break;
  }

  await createUser(db, username, password, 'admin');

  console.log(`\n  ✓  Admin account "${username}" created.\n`);
  console.log(line('═'));
  console.log('  Setup complete!');
  console.log('');
  console.log('  Open the app in your browser and log in as admin');
  console.log('  to create your first user account.');
  console.log(line('═'));
  console.log('');
}

module.exports = { run };
