const fs = require('fs');
const path = require('path');
const { getAddress, isAddress } = require('ethers');

const PROJECT_ROOT = path.join(__dirname, '..');
const NETWORK_PATH = path.join(PROJECT_ROOT, 'runtime', 'plais-qbft', 'network.json');
const DEFAULT_VAULT_PATH = path.join(PROJECT_ROOT, 'data', 'operator-custody', 'operator-key.dpapi.json');
const EXPECTED_PROTECTION = 'windows-dpapi-current-user';

function normalizeAddress(value) {
  const text = String(value || '').trim();
  return isAddress(text) ? getAddress(text) : '';
}

function readJson(filePath) {
  return JSON.parse(fs.readFileSync(filePath, 'utf8'));
}

function resolveVaultPath() {
  const configured = String(process.env.PLAIS_EVM_OPERATOR_VAULT_PATH || '').trim();
  return configured ? path.resolve(PROJECT_ROOT, configured) : DEFAULT_VAULT_PATH;
}

function getConfiguredOperatorAddress() {
  const configured = normalizeAddress(process.env.PLAIS_EVM_OPERATOR_ADDRESS);
  if (configured) return configured;
  try {
    return normalizeAddress(readJson(NETWORK_PATH).operatorAddress);
  } catch (error) {
    return '';
  }
}

function readNetworkIdentity() {
  try {
    const network = readJson(NETWORK_PATH);
    const operatorAddress = normalizeAddress(network.operatorAddress);
    const genesisPath = path.resolve(String(network.genesisFile || ''));
    const genesis = readJson(genesisPath);
    const allocated = Boolean(operatorAddress && genesis.alloc && genesis.alloc[operatorAddress.slice(2).toLowerCase()]);
    return { operatorAddress, allocated, error: '' };
  } catch (error) {
    return { operatorAddress: '', allocated: false, error: error.code || 'NETWORK_IDENTITY_UNAVAILABLE' };
  }
}

function readVaultMetadata() {
  const vaultPath = resolveVaultPath();
  try {
    const metadata = readJson(vaultPath);
    return {
      exists: true,
      path: vaultPath,
      version: Number(metadata.version || 0),
      protection: String(metadata.protection || ''),
      operatorAddress: normalizeAddress(metadata.operatorAddress),
      ciphertextPresent: typeof metadata.ciphertext === 'string' && metadata.ciphertext.length >= 32,
      aclProtected: metadata.aclProtected === true,
      createdAt: metadata.createdAt || null,
      error: ''
    };
  } catch (error) {
    return {
      exists: false,
      path: vaultPath,
      version: 0,
      protection: '',
      operatorAddress: '',
      ciphertextPresent: false,
      aclProtected: false,
      createdAt: null,
      error: error.code || 'OPERATOR_VAULT_UNAVAILABLE'
    };
  }
}

function getStatus() {
  const configuredAddress = normalizeAddress(process.env.PLAIS_EVM_OPERATOR_ADDRESS);
  const network = readNetworkIdentity();
  const vault = readVaultMetadata();
  const plaintextKeyPresent = /^0x[0-9a-fA-F]{64}$/.test(String(process.env.PLAIS_EVM_OPERATOR_PRIVATE_KEY || '').trim());
  const identityMatches = Boolean(
    configuredAddress
    && network.operatorAddress
    && configuredAddress === network.operatorAddress
    && network.allocated
  );
  const vaultReady = Boolean(
    vault.exists
    && vault.version === 1
    && vault.protection === EXPECTED_PROTECTION
    && vault.operatorAddress === configuredAddress
    && vault.ciphertextPresent
    && vault.aclProtected
  );
  const checks = [
    {
      key: 'operator_plaintext_key_absent',
      ok: !plaintextKeyPresent,
      detail: plaintextKeyPresent ? 'A plaintext operator private key is still loaded.' : 'No plaintext operator private key is loaded by the web process.'
    },
    {
      key: 'operator_address_identity',
      ok: identityMatches,
      detail: identityMatches ? 'Configured operator address matches the canonical network and genesis allocation.' : (network.error || 'Operator address does not match canonical network identity.')
    },
    {
      key: 'operator_offline_vault',
      ok: vaultReady,
      detail: vaultReady ? 'Encrypted DPAPI custody metadata is present and ACL protection was verified during migration.' : (vault.error || 'Offline operator vault metadata is incomplete.')
    },
    {
      key: 'operator_signing_lock',
      ok: String(process.env.PLAIS_EVM_OPERATOR_CUSTODY_MODE || '') === 'offline-dpapi'
        && process.env.PLAIS_EVM_OPERATOR_SIGNING_ENABLED === '0',
      detail: 'Web signing must remain disabled while custody mode is offline-dpapi.'
    },
    {
      key: 'multichain_transfer_lock',
      ok: process.env.PLAIS_MULTICHAIN_TRANSFERS_ENABLED === '0',
      detail: process.env.PLAIS_MULTICHAIN_TRANSFERS_ENABLED === '0'
        ? 'BNB, TRX, and token transfer signing is disabled.'
        : 'Multichain transfer signing is not explicitly disabled.'
    }
  ];
  return {
    ok: checks.every((check) => check.ok),
    mode: String(process.env.PLAIS_EVM_OPERATOR_CUSTODY_MODE || ''),
    operatorAddress: configuredAddress || network.operatorAddress,
    signerOnline: false,
    signingEnabled: false,
    vault: {
      exists: vault.exists,
      version: vault.version,
      protection: vault.protection,
      aclProtected: vault.aclProtected,
      createdAt: vault.createdAt
    },
    checks
  };
}

function signerOfflineError() {
  const error = new Error('Operator signing is offline. Use an approved offline signing ceremony.');
  error.code = 'OPERATOR_SIGNER_OFFLINE';
  error.status = 503;
  return error;
}

module.exports = {
  EXPECTED_PROTECTION,
  getConfiguredOperatorAddress,
  getStatus,
  resolveVaultPath,
  signerOfflineError
};
