'use strict';

const fs = require('fs');
const path = require('path');

// better-sqlite3's better_sqlite3.node is architecture-specific (it links
// against libnode.so, which is the real Android binary already loaded into
// this process by nodejs-mobile-cordova). The copy under node_modules/ is
// whichever architecture it happened to be built for last; replace it with
// the prebuild matching this device's actual arch before anything requires
// better-sqlite3. See scripts/build-better-sqlite3-arm.sh.
(function installBetterSqlite3Prebuild() {
  const archDirs = {
    arm64: 'arm64-v8a',
    arm: 'armeabi-v7a',
    x64: 'x86_64',
    ia32: 'x86',
  };
  const archDir = archDirs[process.arch];
  if (!archDir) return; // Unknown arch — fall back to whatever's bundled.

  const prebuilt = path.join(__dirname, 'prebuilds', archDir, 'better_sqlite3.node');
  const target = path.join(__dirname, 'node_modules', 'better-sqlite3', 'build', 'Release', 'better_sqlite3.node');
  if (!fs.existsSync(prebuilt)) return;

  try {
    fs.mkdirSync(path.dirname(target), { recursive: true });
    fs.copyFileSync(prebuilt, target);
  } catch (err) {
    console.error('[main.js] Failed to install better-sqlite3 prebuild for', process.arch, err);
  }
})();

// Node 12.19 predates crypto.randomUUID (added in Node 14.17), used by
// services/authService.js for session token IDs.
const nodeCrypto = require('crypto');
if (typeof nodeCrypto.randomUUID !== 'function') {
  nodeCrypto.randomUUID = function () {
    const b = nodeCrypto.randomBytes(16);
    b[6] = (b[6] & 0x0f) | 0x40;
    b[8] = (b[8] & 0x3f) | 0x80;
    const hex = b.toString('hex');
    return [
      hex.slice(0, 8),
      hex.slice(8, 12),
      hex.slice(12, 16),
      hex.slice(16, 20),
      hex.slice(20),
    ].join('-');
  };
}

// Node 12.19 predates crypto.hkdfSync (added in Node 15.0), used by
// services/encryptionService.js's deriveKey to derive the token-encryption
// key from TOKEN_ENCRYPTION_KEY. Implements HKDF-Extract/Expand (RFC 5869)
// via createHmac.
if (typeof nodeCrypto.hkdfSync !== 'function') {
  nodeCrypto.hkdfSync = function (digest, ikm, salt, info, keylen) {
    const hashLen = nodeCrypto.createHash(digest).digest().length;
    const saltBuf = salt && salt.length ? Buffer.from(salt) : Buffer.alloc(hashLen, 0);
    const ikmBuf = Buffer.isBuffer(ikm) ? ikm : Buffer.from(ikm);
    const infoBuf = Buffer.isBuffer(info) ? info : Buffer.from(info || '');

    const prk = nodeCrypto.createHmac(digest, saltBuf).update(ikmBuf).digest();

    let t = Buffer.alloc(0);
    let okm = Buffer.alloc(0);
    let counter = 1;
    while (okm.length < keylen) {
      t = nodeCrypto
        .createHmac(digest, prk)
        .update(t)
        .update(infoBuf)
        .update(Buffer.from([counter]))
        .digest();
      okm = Buffer.concat([okm, t]);
      counter++;
    }
    return okm.slice(0, keylen);
  };
}

// nodejs-mobile-cordova 0.4.3 embeds Node 12.19 built without ICU, so
// `Intl` is entirely undefined. Polyfill just the bits the server uses
// (utils/money.js, routes/dataSources.js, routes/status.js) — en-US number
// formatting with thousands separators, and a no-op DateTimeFormat whose
// resolvedOptions().timeZone the callers already treat as optional.
if (typeof global.Intl === 'undefined') {
  global.Intl = {
    NumberFormat: function (_locale, options) {
      const opts = options || {};
      const minFrac = opts.minimumFractionDigits || 0;
      const maxFrac = Math.max(opts.maximumFractionDigits || 0, minFrac);
      this.format = function (value) {
        const fixed = Number(value).toFixed(maxFrac);
        let [intPart, fracPart] = fixed.split('.');
        const negative = intPart.startsWith('-');
        if (negative) intPart = intPart.slice(1);
        intPart = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
        let out = (negative ? '-' : '') + intPart;
        if (fracPart) out += '.' + fracPart;
        return out;
      };
    },
    DateTimeFormat: function () {
      this.resolvedOptions = function () {
        return { timeZone: undefined };
      };
    },
  };
}

// Server-side paths, relative to this directory (the app's writable storage
// on-device — nodejs-mobile-cordova copies nodejs-project here at runtime).
process.env.DB_PATH = path.join(__dirname, 'data', 'bills.db');
process.env.BACKUP_PATH = path.join(__dirname, 'backups');
process.env.PORT = process.env.PORT || '3000';
process.env.BIND_HOST = '127.0.0.1';
// The Capacitor WebView's origin (androidScheme: 'https') is cross-origin
// from this server's http://localhost:3000, so the LoadingScreen's
// /api/health poll and the app's API calls need CORS allowed for it.
process.env.CORS_ORIGIN = 'https://localhost';

const cordova = require('cordova-bridge');

// Wait for the WebView to hand over the device-bound encryption key (stored
// in Android Keystore / iOS Keychain — see src/crypto.ts) before starting the
// server, so encryptionService.js picks up TOKEN_ENCRYPTION_KEY on first use.
cordova.channel.on('message', function (msg) {
  if (msg && msg.type === 'encryptionKey' && typeof msg.key === 'string') {
    process.env.TOKEN_ENCRYPTION_KEY = msg.key;
    require('./server/server.js');
  }
});

cordova.channel.post('message', { type: 'ready' });
