#!/usr/bin/env node

const { spawn, spawnSync } = require('child_process');
const fs = require('fs');
const path = require('path');

const ROOT = path.resolve(__dirname, '..');

function print(text = '') {
  process.stdout.write(`${text}\n`);
}

function printJson(value) {
  print(JSON.stringify(value, null, 2));
}

function fail(message, code = 1) {
  process.stderr.write(`${message}\n`);
  process.exitCode = code;
}

function parseArgs(argv) {
  const flags = {};
  const positional = [];
  for (let i = 0; i < argv.length; i += 1) {
    const value = argv[i];
    if (value.startsWith('--')) {
      const key = value.slice(2);
      const next = argv[i + 1];
      if (next && !next.startsWith('--')) {
        flags[key] = next;
        i += 1;
      } else {
        flags[key] = true;
      }
    } else {
      positional.push(value);
    }
  }
  return { flags, positional };
}

function asJson(flags) {
  return Boolean(flags.json);
}

function requireLib(relativePath) {
  return require(path.join(ROOT, relativePath));
}

function help() {
  print(`MazeXChain CLI

Usage:
  mazex help
  mazex version
  mazex node start [--port 3000] [--p2p-port 4100]
  mazex node health [--url http://localhost:3000]
  mazex chain status [--json]
  mazex chain verify [--json]
  mazex chain replay [--json]
  mazex wallet create [--show-private] [--out wallet.json]
  mazex wallet balance <mazex-address> [--json]
  mazex config show [--json]
  mazex downloads package

Principles:
  - The CLI calls existing protocol modules; it does not redefine consensus.
  - Private wallet material is hidden unless --show-private or --out is used.
  - Packaging writes only public download artifacts under public/downloads/.
`);
}

function version() {
  const pkg = require(path.join(ROOT, 'package.json'));
  print(`${pkg.name} ${pkg.version}`);
}

function startNode(flags) {
  const env = { ...process.env };
  if (flags.port) env.PORT = String(flags.port);
  if (flags['p2p-port']) env.MAZEX_P2P_PORT = String(flags['p2p-port']);
  const child = spawn(process.execPath, [path.join(ROOT, 'src', 'server.js')], {
    cwd: ROOT,
    env,
    stdio: 'inherit',
  });
  child.on('exit', (code, signal) => {
    if (signal) {
      fail(`mazex node stopped by signal ${signal}`, 1);
      return;
    }
    process.exitCode = code || 0;
  });
}

async function nodeHealth(flags) {
  const baseUrl = String(flags.url || 'http://localhost:3000').replace(/\/+$/, '');
  const response = await fetch(`${baseUrl}/api/health`);
  const body = await response.json();
  if (!response.ok || body.error) {
    fail(JSON.stringify(body, null, 2), 1);
    return;
  }
  printJson(body);
}

function chainStatus(flags) {
  const {
    getBase1ChainSummary,
    getBase1HeadProof,
    getCurrentBase1Public,
    getBase1FeeSchedule,
    getBase1StorageStatus,
  } = requireLib('src/lib/base1_state.js');
  const publicState = getCurrentBase1Public();
  const current = publicState.current || publicState;
  const output = {
    chain: getBase1ChainSummary(),
    head: getBase1HeadProof(),
    current: {
      layer_no: current.layer_no,
      step_no: current.step_no,
      progress: current.progress,
      grid_size: current.grid_size,
      score: current.score,
      step_commit: current.step_commit,
      behavior_root: current.behavior_root,
    },
    fees: getBase1FeeSchedule(),
    storage: getBase1StorageStatus(),
  };
  if (asJson(flags)) {
    printJson(output);
    return;
  }
  print(`Layer ${output.current.layer_no} / Step ${output.current.progress}`);
  print(`Height ${output.chain.height} / Work ${output.chain.cumulative_work_score}`);
  print(`Head ${output.chain.head_hash}`);
  print(`State ${output.chain.head_state_root}`);
  print(`Grid ${output.current.grid_size} / Score ${output.current.score}`);
  print(`Submit fee ${output.fees.submit_fee_base || output.fees.submit_fee || '--'} MAZEX`);
}

function chainVerify(flags) {
  const { verifyBase1Chain } = requireLib('src/lib/base1_state.js');
  const result = verifyBase1Chain();
  if (asJson(flags)) {
    printJson(result);
  } else {
    print(result.ok ? 'Chain verification passed.' : 'Chain verification failed.');
    print(`Height: ${result.height ?? '--'}`);
    print(`Head: ${result.head_hash || '--'}`);
    if (result.error || result.message) print(result.error || result.message);
  }
  if (!result.ok) process.exitCode = 1;
}

function chainReplay(flags) {
  const { replayBase1Chain } = requireLib('src/lib/base1_state.js');
  const result = replayBase1Chain();
  if (asJson(flags)) {
    printJson(result);
  } else {
    print(result.ok ? 'Chain replay passed.' : 'Chain replay failed.');
    print(`Checked blocks: ${result.checked_blocks ?? '--'}`);
    print(`Layer ${result.current_layer ?? '--'} / Step ${result.current_step ?? '--'}`);
    if (result.error || result.message) print(result.error || result.message);
  }
  if (!result.ok) process.exitCode = 1;
}

function sanitizeWallet(wallet, showPrivate) {
  if (showPrivate) return wallet;
  return {
    version: wallet.version,
    address: wallet.address,
    public_key: wallet.public_key,
    algorithm: wallet.algorithm,
    private_key: 'hidden: rerun with --show-private or use --out for a local backup file',
  };
}

function walletCreate(flags) {
  const { createWallet } = requireLib('src/lib/wallet.js');
  const wallet = createWallet();
  if (flags.out) {
    const outPath = path.resolve(process.cwd(), String(flags.out));
    fs.writeFileSync(outPath, `${JSON.stringify(wallet, null, 2)}\n`);
    print(`Wallet written to ${outPath}`);
    print(`Address ${wallet.address}`);
    return;
  }
  printJson(sanitizeWallet(wallet, Boolean(flags['show-private'])));
}

function walletBalance(address, flags) {
  if (!address) {
    fail('Usage: mazex wallet balance <mazex-address>');
    return;
  }
  const { getBase1Account } = requireLib('src/lib/base1_state.js');
  const account = getBase1Account(address);
  if (asJson(flags)) {
    printJson(account);
    return;
  }
  print(`Address ${account.address}`);
  print(`Confirmed ${account.confirmed_balance}`);
  print(`Pending ${account.pending_balance}`);
  print(`Nonce ${account.nonce}`);
}

function configShow(flags) {
  const { getRuntimeConfigSnapshot } = requireLib('src/lib/config.js');
  const snapshot = getRuntimeConfigSnapshot();
  if (asJson(flags)) {
    printJson(snapshot);
    return;
  }
  print(`App: ${snapshot.app.name || 'mazexchain'}`);
  print(`Chain ID: ${snapshot.chain.chain_id || '--'}`);
  print(`Address prefix: ${snapshot.wallet.address_prefix || '--'}`);
  print(`HTTP port: ${snapshot.app.port || '--'}`);
  print(`P2P port: ${snapshot.network.p2p_port || '--'}`);
}

function packageDownloads() {
  const script = path.join(ROOT, 'tools', 'package_downloads.ps1');
  const result = spawnSync(
    'powershell',
    ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', script],
    { cwd: ROOT, stdio: 'inherit' },
  );
  process.exitCode = result.status || 0;
}

async function main() {
  const { flags, positional } = parseArgs(process.argv.slice(2));
  const [area, action, value] = positional;
  if (!area || area === 'help' || flags.help) return help();
  if (area === 'version' || flags.version) return version();

  if (area === 'node' && action === 'start') return startNode(flags);
  if (area === 'node' && action === 'health') return nodeHealth(flags);

  if (area === 'chain' && action === 'status') return chainStatus(flags);
  if (area === 'chain' && action === 'verify') return chainVerify(flags);
  if (area === 'chain' && action === 'replay') return chainReplay(flags);

  if (area === 'wallet' && action === 'create') return walletCreate(flags);
  if (area === 'wallet' && action === 'balance') return walletBalance(value, flags);

  if (area === 'config' && action === 'show') return configShow(flags);
  if (area === 'downloads' && action === 'package') return packageDownloads();

  fail(`Unknown command: ${process.argv.slice(2).join(' ')}\nRun "mazex help" for usage.`);
}

main().catch((error) => {
  fail(error.stack || error.message || String(error), 1);
});
