const fs = require('fs');
const net = require('net');
const path = require('path');
const { JsonRpcProvider } = require('ethers');
const { EVM_CANDIDATE_CONFIG } = require('../config/evmCandidateConfig');

const activeNetworkRoot = path.join(__dirname, '..', 'runtime', 'plais-qbft');
const activeNetworkPath = path.join(activeNetworkRoot, 'network.json');

function normalizeNodeId(value) {
  return String(value || '').trim().toLowerCase().replace(/^0x/, '').replace(/^04/, '');
}

function readExpectedValidatorNodeIds() {
  try {
    const network = JSON.parse(fs.readFileSync(activeNetworkPath, 'utf8'));
    return new Set((network.nodes || [])
      .filter((node) => node.role === 'validator')
      .map((node) => path.join(activeNetworkRoot, node.name, 'data', 'key.pub'))
      .map((keyPath) => normalizeNodeId(fs.readFileSync(keyPath, 'utf8')))
      .filter(Boolean));
  } catch (error) {
    return new Set();
  }
}

function extractRemoteHost(remoteAddress) {
  const value = String(remoteAddress || '').trim().replace(/^\//, '');
  if (!value) return '';
  if (value.startsWith('[')) {
    const closing = value.indexOf(']');
    return closing > 1 && net.isIP(value.slice(1, closing)) ? value.slice(1, closing).toLowerCase() : '';
  }
  if (net.isIP(value)) return value.toLowerCase();
  const colon = value.lastIndexOf(':');
  if (colon <= 0) return '';
  const host = value.slice(0, colon);
  return net.isIP(host) ? host.toLowerCase() : '';
}

function getHostDistributionEvidence(peers) {
  const expectedIds = readExpectedValidatorNodeIds();
  const validatorPeers = Array.isArray(peers)
    ? peers.filter((peer) => expectedIds.has(normalizeNodeId(peer && peer.id)))
    : [];
  const hosts = validatorPeers
    .map((peer) => extractRemoteHost(peer && peer.network && peer.network.remoteAddress))
    .filter(Boolean);
  return {
    validatorPeerIdentityCount: validatorPeers.length,
    independentValidatorHostCount: new Set(hosts).size,
    hostEvidenceComplete: expectedIds.size === EVM_CANDIDATE_CONFIG.validatorCount
      && validatorPeers.length === EVM_CANDIDATE_CONFIG.validatorCount
      && hosts.length === EVM_CANDIDATE_CONFIG.validatorCount
  };
}

async function getStatus() {
  const base = {
    enabled: EVM_CANDIDATE_CONFIG.enabled,
    networkName: EVM_CANDIDATE_CONFIG.networkName,
    configuredChainId: EVM_CANDIDATE_CONFIG.chainId,
    consensus: EVM_CANDIDATE_CONFIG.consensus,
    configuredValidatorCount: EVM_CANDIDATE_CONFIG.validatorCount,
    rpcUrl: EVM_CANDIDATE_CONFIG.rpcUrl.replace(/\/\/.*@/, '//***@'),
    externalTransfersEnabled: EVM_CANDIDATE_CONFIG.externalTransfersEnabled,
    online: false,
    chainId: null,
    blockNumber: null,
    latestBlockHash: '',
    latestBlockTimestamp: null,
    latestBlockAgeSec: null,
    genesisHash: '',
    peerCount: null,
    validatorPeerIdentityCount: 0,
    independentValidatorHostCount: 0,
    hostEvidenceComplete: false,
    validatorCount: null,
    validators: [],
    clientVersion: '',
    error: ''
  };
  if (!base.enabled) return base;
  try {
    const provider = new JsonRpcProvider(EVM_CANDIDATE_CONFIG.rpcUrl, undefined, { staticNetwork: false });
    const [network, blockNumber, peerCountHex, validators, clientVersion, latestBlock, genesisBlock, peers] = await Promise.all([
      provider.getNetwork(),
      provider.getBlockNumber(),
      provider.send('net_peerCount', []),
      provider.send('qbft_getValidatorsByBlockNumber', ['latest']),
      provider.send('web3_clientVersion', []),
      provider.getBlock('latest'),
      provider.getBlock(0),
      provider.send('admin_peers', [])
    ]);
    const chainId = Number(network.chainId);
    const peerCount = Number(peerCountHex);
    const validatorCount = Array.isArray(validators) ? validators.length : 0;
    const hostEvidence = getHostDistributionEvidence(peers);
    const healthy = chainId === EVM_CANDIDATE_CONFIG.chainId
      && validatorCount >= EVM_CANDIDATE_CONFIG.validatorCount;
    return {
      ...base,
      online: healthy,
      chainId,
      blockNumber,
      latestBlockHash: latestBlock?.hash || '',
      latestBlockTimestamp: latestBlock?.timestamp ? new Date(Number(latestBlock.timestamp) * 1000).toISOString() : null,
      latestBlockAgeSec: latestBlock?.timestamp ? Math.max(0, Math.floor(Date.now() / 1000) - Number(latestBlock.timestamp)) : null,
      genesisHash: genesisBlock?.hash || '',
      peerCount,
      ...hostEvidence,
      validatorCount,
      validators: Array.isArray(validators) ? validators.map((address) => String(address).toLowerCase()) : [],
      clientVersion,
      error: chainId !== EVM_CANDIDATE_CONFIG.chainId
        ? 'CHAIN_ID_MISMATCH'
        : (healthy ? '' : 'VALIDATOR_QUORUM_INCOMPLETE')
    };
  } catch (error) {
    return { ...base, error: error.code || 'RPC_UNAVAILABLE' };
  }
}

async function getBlock(blockId = 'latest') {
  const provider = new JsonRpcProvider(EVM_CANDIDATE_CONFIG.rpcUrl, undefined, { staticNetwork: false });
  const normalized = String(blockId || 'latest').trim();
  const tag = normalized === 'latest' || /^0x[0-9a-fA-F]{64}$/.test(normalized)
    ? normalized
    : Number(normalized);
  if (typeof tag === 'number' && !Number.isSafeInteger(tag)) return null;
  const block = await provider.getBlock(tag);
  if (!block) return null;
  return {
    number: block.number,
    hash: block.hash,
    parentHash: block.parentHash,
    timestamp: Number(block.timestamp),
    createdAt: new Date(Number(block.timestamp) * 1000).toISOString(),
    miner: block.miner,
    transactionCount: Array.isArray(block.transactions) ? block.transactions.length : 0,
    transactions: Array.isArray(block.transactions) ? block.transactions : [],
    gasUsed: block.gasUsed?.toString() || '0',
    gasLimit: block.gasLimit?.toString() || '0',
    baseFeePerGas: block.baseFeePerGas?.toString() || '0'
  };
}

async function getTransaction(txHash) {
  const hash = String(txHash || '').trim();
  if (!/^0x[0-9a-fA-F]{64}$/.test(hash)) return null;
  const provider = new JsonRpcProvider(EVM_CANDIDATE_CONFIG.rpcUrl, undefined, { staticNetwork: false });
  const [transaction, receipt] = await Promise.all([
    provider.getTransaction(hash),
    provider.getTransactionReceipt(hash)
  ]);
  if (!transaction) return null;
  return {
    hash: transaction.hash,
    from: transaction.from,
    to: transaction.to,
    nonce: transaction.nonce,
    value: transaction.value?.toString() || '0',
    gasLimit: transaction.gasLimit?.toString() || '0',
    gasPrice: transaction.gasPrice?.toString() || '0',
    data: transaction.data || '0x',
    blockNumber: transaction.blockNumber,
    blockHash: transaction.blockHash,
    status: receipt ? Number(receipt.status) : null,
    gasUsed: receipt?.gasUsed?.toString() || null,
    contractAddress: receipt?.contractAddress || null
  };
}

module.exports = { getBlock, getStatus, getTransaction };
