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

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 };
