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

const ROOT = path.resolve(__dirname, '..', '..');
const CONFIG_DIR = path.resolve(process.env.MAZEX_CONFIG_DIR || path.join(ROOT, 'config'));

const CONFIG_FILES = {
  app: 'app.config.json',
  chain: 'chain.config.json',
  consensus: 'consensus.config.json',
  genesis: 'genesis.config.json',
  network: 'network.config.json',
  security: 'security.config.json',
  wallet: 'wallet.config.json',
};

let cachedConfig = null;

function readJsonFile(filePath, fallback = {}) {
  if (!fs.existsSync(filePath)) return fallback;
  try {
    return JSON.parse(fs.readFileSync(filePath, 'utf8'));
  } catch (error) {
    error.message = `Failed to read MazeXChain config ${filePath}: ${error.message}`;
    throw error;
  }
}

function resolveProjectPath(value, fallback = null) {
  const raw = String(value || '').trim();
  if (!raw) return fallback;
  return path.isAbsolute(raw) ? raw : path.resolve(ROOT, raw);
}

function loadConfigFile(key) {
  return readJsonFile(path.join(CONFIG_DIR, CONFIG_FILES[key]), {});
}

function loadMazexConfig({ reload = false } = {}) {
  if (cachedConfig && !reload) return cachedConfig;
  const config = {
    root: ROOT,
    config_dir: CONFIG_DIR,
    app: loadConfigFile('app'),
    chain: loadConfigFile('chain'),
    consensus: loadConfigFile('consensus'),
    genesis_config: loadConfigFile('genesis'),
    network: loadConfigFile('network'),
    security: loadConfigFile('security'),
    wallet: loadConfigFile('wallet'),
  };

  const genesis = config.genesis_config?.genesis || {};
  config.genesis = readJsonFile(resolveProjectPath(genesis.initial_state, path.join(ROOT, 'genesis', 'genesis.json')), {});
  config.initial_accounts = readJsonFile(
    resolveProjectPath(genesis.initial_accounts, path.join(ROOT, 'genesis', 'initial-accounts.json')),
    {},
  );
  config.initial_fragments = readJsonFile(
    resolveProjectPath(genesis.initial_fragment_registry, path.join(ROOT, 'genesis', 'initial-fragments.json')),
    {},
  );

  cachedConfig = config;
  return config;
}

function resetMazexConfigCache() {
  cachedConfig = null;
}

function configSection(key) {
  return loadMazexConfig()[key] || {};
}

function envValue(name) {
  return Object.prototype.hasOwnProperty.call(process.env, name) ? process.env[name] : undefined;
}

function envOrConfigString(envName, configValue, fallback = '') {
  const value = envValue(envName);
  const selected = value !== undefined ? value : configValue;
  const normalized = String(selected ?? fallback ?? '').trim();
  return normalized || String(fallback ?? '');
}

function envOrConfigBool(envName, configValue, fallback = false) {
  const value = envValue(envName);
  const selected = value !== undefined ? value : configValue;
  if (selected === undefined || selected === null || selected === '') return Boolean(fallback);
  if (typeof selected === 'boolean') return selected;
  return ['1', 'true', 'yes', 'on'].includes(String(selected).toLowerCase());
}

function envOrConfigNumber(envName, configValue, fallback = 0, { min = -Infinity, max = Infinity, integer = false } = {}) {
  const value = envValue(envName);
  const numeric = Number(value !== undefined ? value : configValue ?? fallback);
  let normalized = Number.isFinite(numeric) ? numeric : Number(fallback || 0);
  if (integer) normalized = Math.trunc(normalized);
  normalized = Math.max(min, Math.min(max, normalized));
  return normalized;
}

function envOrConfigList(envName, configValue, fallback = []) {
  const value = envValue(envName);
  const selected = value !== undefined ? value : configValue;
  if (Array.isArray(selected)) return selected.map((item) => String(item).trim()).filter(Boolean);
  if (selected === undefined || selected === null || selected === '') return fallback;
  return String(selected)
    .split(',')
    .map((item) => item.trim())
    .filter(Boolean);
}

function getRuntimeConfigSnapshot() {
  const config = loadMazexConfig();
  return {
    app: config.app?.app || {},
    runtime: config.app?.runtime || {},
    chain: config.chain?.chain || {},
    fees: config.chain?.fees || {},
    rewards: config.chain?.rewards || {},
    consensus: config.consensus?.consensus || {},
    verification: config.consensus?.verification || {},
    network: config.network?.network || {},
    network_limits: config.network?.limits || {},
    security: config.security?.security || {},
    payload_limits: config.security?.payload_limits || {},
    wallet: config.wallet?.wallet || {},
    vault: config.wallet?.vault || {},
    genesis: config.genesis_config?.genesis || {},
  };
}

module.exports = {
  CONFIG_DIR,
  CONFIG_FILES,
  ROOT,
  configSection,
  envOrConfigBool,
  envOrConfigList,
  envOrConfigNumber,
  envOrConfigString,
  getAppConfig: () => configSection('app'),
  getChainConfig: () => configSection('chain'),
  getConsensusConfig: () => configSection('consensus'),
  getGenesisConfig: () => configSection('genesis_config'),
  getInitialAccounts: () => configSection('initial_accounts'),
  getInitialFragments: () => configSection('initial_fragments'),
  getNetworkConfig: () => configSection('network'),
  getRuntimeConfigSnapshot,
  getSecurityConfig: () => configSection('security'),
  getWalletConfig: () => configSection('wallet'),
  loadMazexConfig,
  readJsonFile,
  resetMazexConfigCache,
  resolveProjectPath,
};
