const express = require('express');
const fs = require('fs');
const path = require('path');
const testnetService = require('../services/testnetService');
const walletService = require('../services/walletService');
const rewardLedgerService = require('../services/rewardLedgerService');
const nodeParticipantService = require('../services/nodeParticipantService');
const { COIN_CONFIG } = require('../config/coinConfig');

const router = express.Router();
const ONLINE_NODE_WINDOW_MS = Number(process.env.PLAIS_NODE_ONLINE_WINDOW_MS || 10 * 60 * 1000);

function isOnlineNode(node, now = Date.now()) {
  const status = String(node?.status || '').toLowerCase();
  if (!['active', 'warning'].includes(status) || !node?.lastHeartbeatAt) return false;

  const lastHeartbeatMs = new Date(node.lastHeartbeatAt).getTime();
  return Number.isFinite(lastHeartbeatMs)
    && now >= lastHeartbeatMs
    && now - lastHeartbeatMs <= ONLINE_NODE_WINDOW_MS;
}

function formatNumber(value, fractionDigits = 2) {
  const number = Number(value || 0);
  if (!Number.isFinite(number)) return '0';
  return new Intl.NumberFormat('ko-KR', {
    maximumFractionDigits: fractionDigits
  }).format(number);
}

function getNetworkMode(status) {
  const explicitMode = String(process.env.AISFOR_CHAIN_PUBLIC_MODE || process.env.AISFOR_CHAIN_MODE || '').toLowerCase();
  if (explicitMode === 'mainnet' || explicitMode === 'testnet') return explicitMode;
  if (isTechnicalMainnetOperational(status)) return 'mainnet';
  const networkId = String(status?.config?.networkId || '').toLowerCase();
  return networkId.includes('mainnet') ? 'mainnet' : 'testnet';
}

function isTechnicalMainnetOperational(status) {
  const canonical = status?.canonical || {};
  const expectedValidatorCount = Number(canonical.configuredValidatorCount || status?.config?.validatorCount || 4);
  return canonical.online === true
    && canonical.id === 'besu-qbft'
    && Number(canonical.chainId || 0) === Number(status?.config?.chainId || 0)
    && String(canonical.consensus || '').toUpperCase() === 'QBFT'
    && Number(canonical.validatorCount || 0) >= expectedValidatorCount;
}

function buildRewardSummary() {
  const entries = rewardLedgerService.listEntries();
  const confirmed = entries.filter((entry) => {
    const blockHeight = Number(entry.ledgerBlockHeight || entry.blockHeight || 0);
    return blockHeight > 0;
  });
  const rows = confirmed.length ? confirmed : entries;
  const net = rows.reduce((sum, entry) => sum + Number(entry.rewardAmount || 0), 0);
  const positive = rows.reduce((sum, entry) => {
    const amount = Number(entry.rewardAmount || 0);
    return amount > 0 ? sum + amount : sum;
  }, 0);
  const negative = rows.reduce((sum, entry) => {
    const amount = Number(entry.rewardAmount || 0);
    return amount < 0 ? sum + amount : sum;
  }, 0);

  return {
    count: rows.length,
    net,
    positive,
    negative
  };
}

function buildHomeModel() {
  const status = testnetService.getStatus();
  const mode = getNetworkMode(status);
  const walletStats = walletService.getStats();
  const rewardSummary = buildRewardSummary();
  const nodes = nodeParticipantService.listNodes();
  // Public network counts represent nodes that are actually reporting now.
  // Historical, duplicate, seeded, and disconnected registrations stay in the
  // admin ledger but must not inflate the public network status.
  const onlineNodes = nodes.filter((node) => isOnlineNode(node));
  const readiness = status.readiness || {};
  const checks = Array.isArray(readiness.checks) ? readiness.checks : [];
  const latestBlock = status.latestBlock || {};
  const blockStats = status.blockStats || {};
  const chainOk = !!status.chainVerify?.ok;
  const canonicalAsset = status.assetPolicy?.canonical || {};
  const legacyAsset = status.assetPolicy?.legacy || {};
  const supplyDisplay = canonicalAsset.genesisSupply || 0;
  const mainnetOperational = isTechnicalMainnetOperational(status);
  const expectedValidatorCount = Number(status.canonical?.configuredValidatorCount || status.config?.validatorCount || 4);

  return {
    mode,
    isMainnet: mode === 'mainnet',
    chainName: mode === 'mainnet' ? COIN_CONFIG.chainName : `${COIN_CONFIG.chainName} Candidate`,
    networkId: status.config?.networkId || '',
    chainId: Number(status.config?.chainId || 0),
    consensus: String(status.config?.consensus || '').toUpperCase(),
    blockTimeTargetSec: Number(status.config?.blockTimeTargetSec || 0),
    launchControlStatus: 'AWAITING_EXPLICIT_LAUNCH_APPROVAL',
    externalTransfersLocked: status.canonical?.externalTransfersEnabled !== true,
    symbol: canonicalAsset.symbol || 'PLAIS',
    rewardUnit: legacyAsset.displaySymbol || 'plais Point',
    addressPrefix: '0x',
    totalSupply: formatNumber(supplyDisplay, 0),
    readinessScore: Number(readiness.score || 0),
    readinessLabel: readiness.score === 100 ? 'READY' : 'PREPARING',
    mainnetOperational,
    expectedValidatorCount,
    chainStatus: chainOk ? 'OK' : 'CHECK',
    latestHeight: blockStats.latestHeight || latestBlock.height || 0,
    finalizedBlocks: blockStats.finalizedBlocks || 0,
    walletCount: walletStats.totalWallets || walletStats.walletCount || 0,
    circulating: Number(canonicalAsset.circulatingSupply || 0),
    validatorCount: status.validators?.length || 0,
    nodeCount: onlineNodes.length,
    onlineNodeCount: onlineNodes.length,
    rewardSummary,
    checks: checks.slice(0, 7),
    validators: (status.validators || []).slice(0, 6),
    latestBlockHash: latestBlock.blockHash || latestBlock.hash || '',
    genesisHash: status.genesis?.genesisHash || '',
    updatedAt: new Date().toISOString(),
    formatNumber
  };
}

function getSourceManifest() {
  const manifestPath = path.join(__dirname, '..', 'public', 'source-release', 'manifest.json');
  try {
    const raw = fs.readFileSync(manifestPath, 'utf8').replace(/^\uFEFF/, '');
    return JSON.parse(raw);
  } catch (err) {
    return {
      ok: false,
      project: 'plais Chain',
      package: '',
      latest: 'plai-chain-source-latest.zip',
      generatedAt: '',
      sizeBytes: 0,
      sha256: '',
      excluded: {}
    };
  }
}

router.get('/', async (req, res, next) => {
  try {
    await testnetService.refreshStatus();
    res.render('home', buildHomeModel());
  } catch (error) {
    next(error);
  }
});

router.get('/status', async (req, res, next) => {
  try {
    await testnetService.refreshStatus();
    res.render('status', buildHomeModel());
  } catch (error) {
    next(error);
  }
});

router.get('/source', async (req, res, next) => {
  try {
    await testnetService.refreshStatus();
  res.render('source', {
    ...buildHomeModel(),
    sourceManifest: getSourceManifest()
  });
  } catch (error) {
    next(error);
  }
});

router.get('/listing', async (req, res, next) => {
  try {
    await testnetService.refreshStatus();
  res.render('listing', {
    ...buildHomeModel(),
    sourceManifest: getSourceManifest()
  });
  } catch (error) {
    next(error);
  }
});

router.get('/transparency', (req, res) => {
  res.redirect('/listing');
});

router.get('/listing-materials', (req, res) => {
  res.redirect('/listing');
});

router.get('/whitepaper', (req, res) => {
  res.sendFile(path.join(__dirname, '..', 'docs', 'WHITEPAPER.html'));
});

router.get('/whitepaper/ko', (req, res) => {
  res.sendFile(path.join(__dirname, '..', 'docs', 'WHITEPAPER_KO.html'));
});

router.get('/kr/whitepaper', (req, res) => {
  res.sendFile(path.join(__dirname, '..', 'docs', 'WHITEPAPER_KO.html'));
});

router.get('/en/whitepaper', (req, res) => {
  res.sendFile(path.join(__dirname, '..', 'docs', 'WHITEPAPER.html'));
});

router.get('/WHITEPAPER.html', (req, res) => {
  res.redirect('/whitepaper');
});

router.get('/WHITEPAPER_KO.html', (req, res) => {
  res.redirect('/whitepaper/ko');
});

router.get('/whitepaper.md', (req, res) => {
  res.type('text/markdown').sendFile(path.join(__dirname, '..', 'docs', 'WHITEPAPER.md'));
});

router.get('/whitepaper/ko.md', (req, res) => {
  res.type('text/markdown').sendFile(path.join(__dirname, '..', 'docs', 'WHITEPAPER_KO.md'));
});

router.get('/WHITEPAPER.md', (req, res) => {
  res.redirect('/whitepaper.md');
});

module.exports = router;
