import crypto from 'crypto';

// ==================================================================================================
// App secret — master key untuk enkripsi token & signing session.
// WAJIB di-set di .env. Kalau hilang/ganti, semua token terenkripsi & session jadi invalid.
// ==================================================================================================
const APP_SECRET = process.env.APP_SECRET;
if (!APP_SECRET || APP_SECRET.length < 32) {
  console.error('❌ APP_SECRET harus di-set di .env (min 32 char acak).');
  console.error('   Generate: node -e "console.log(require(\'crypto\').randomBytes(48).toString(\'hex\'))"');
  process.exit(1);
}

// Derive a stable 32-byte key from APP_SECRET for AES-256-GCM
const ENC_KEY = crypto.createHash('sha256').update(String(APP_SECRET)).digest();

// ==================================================================================================
// Password hashing — scrypt (built-in, no dependency). Format: scrypt$<saltHex>$<hashHex>
// ==================================================================================================
export function hashPassword(password) {
  const salt = crypto.randomBytes(16);
  const hash = crypto.scryptSync(String(password), salt, 64);
  return `scrypt$${salt.toString('hex')}$${hash.toString('hex')}`;
}

export function verifyPassword(password, stored) {
  try {
    const [scheme, saltHex, hashHex] = String(stored).split('$');
    if (scheme !== 'scrypt') return false;
    const salt = Buffer.from(saltHex, 'hex');
    const expected = Buffer.from(hashHex, 'hex');
    const actual = crypto.scryptSync(String(password), salt, expected.length);
    // constant-time compare
    return expected.length === actual.length && crypto.timingSafeEqual(expected, actual);
  } catch {
    return false;
  }
}

// ==================================================================================================
// Token encryption — AES-256-GCM. Output: <ivHex>:<tagHex>:<cipherHex>
// Dipakai buat nyimpen bot token user di DB (jangan pernah simpan plaintext).
// ==================================================================================================
export function encryptSecret(plaintext) {
  if (plaintext == null || plaintext === '') return null;
  const iv = crypto.randomBytes(12);
  const cipher = crypto.createCipheriv('aes-256-gcm', ENC_KEY, iv);
  const enc = Buffer.concat([cipher.update(String(plaintext), 'utf8'), cipher.final()]);
  const tag = cipher.getAuthTag();
  return `${iv.toString('hex')}:${tag.toString('hex')}:${enc.toString('hex')}`;
}

export function decryptSecret(blob) {
  if (!blob) return null;
  try {
    const [ivHex, tagHex, dataHex] = String(blob).split(':');
    const iv = Buffer.from(ivHex, 'hex');
    const tag = Buffer.from(tagHex, 'hex');
    const data = Buffer.from(dataHex, 'hex');
    const decipher = crypto.createDecipheriv('aes-256-gcm', ENC_KEY, iv);
    decipher.setAuthTag(tag);
    return Buffer.concat([decipher.update(data), decipher.final()]).toString('utf8');
  } catch {
    return null;
  }
}

// Masked preview of a secret for UI (never return full token to client)
export function maskSecret(plaintext) {
  if (!plaintext) return '';
  const s = String(plaintext);
  if (s.length <= 12) return '••••';
  return `${s.slice(0, 6)}…${s.slice(-4)}`;
}

// ==================================================================================================
// Session tokens — random opaque token; disimpan di DB (table sessions).
// ==================================================================================================
export function newSessionToken() {
  return crypto.randomBytes(32).toString('hex');
}
