#!/usr/bin/env node
'use strict';
// no-var is disabled since this is supposed to support old versions of node
// block scoped var is just because idk what the fuck is going on there since it wasn't
// failing before
/* eslint-disable block-scoped-var, no-var, no-redeclare*/

// Before running, ensure that the Typescript files have been compiled.
//
// Simply doing `require('../build')` here doesn't work because the `replace`
// step of the build is asynchronous and we can't wait for it to finish easily
// unless we shell out and use `execSync`. However, the heuristic of simply
// checking for the presence of './dist/sim/dex' to determine whether a build
// is required is somewhat risky as it only is valid from a clean repository -
// if you make edits or have previous build artifacts lying around, this
// script could potentially be missing additional sources or the compiled output
// may be out of date.
//
// We're OK with risking being stale here unless we're starting the server
// because we want to keep this script as fast as possible to be usable as a way
// of interfacing with the simulator from non-JS languages. Otherwise we error
// on the side of caution and run `node build` to ensure we're always running
// with the latest code.
var fs = require('fs');
var util = require('util');
var cli = null;

function build() {
	if (cli.values['skip-build']) return;
	require('child_process').execSync('node build', {stdio: 'inherit', cwd: __dirname});
}

function readTeam(stream) {
	return stream.readLine().then(line => {
		if (line.startsWith('[') || line.includes('|')) return line;
		return stream.readAll().then(all => (line + '\n' + all));
	});
}

function ensureBuilt() {
	if (!fs.existsSync('./dist')) {
		build();
	}
}

function showHelp() {
	console.log('pokemon-showdown start [--skip-build] [PORT]');
	console.log('');
	console.log('  Starts a PS server on the specified port');
	console.log('  (Defaults to the port setting in config/config.js)');
	console.log('  (The port setting in config/config.js defaults to 8000)');
	console.log('');
	console.log('pokemon-showdown generate-team [FORMAT-ID] [RANDOM-SEED]');
	console.log('');
	console.log('  Generates a random team, and writes it to stdout in packed team format');
	console.log('  (Format defaults to the latest Random Battles format)');
	console.log('');
	console.log('pokemon-showdown validate-team [FORMAT-ID]');
	console.log('');
	console.log('  Reads a team from stdin, and validates it');
	console.log('  If valid: exits with code 0');
	console.log('  If invalid: writes errors to stderr, exits with code 1');
	console.log('');
	console.log('pokemon-showdown simulate-battle');
	console.log('');
	console.log('  Simulates a battle, taking input to stdin and writing output to stdout');
	console.log('  Protocol is documented in ./dist/sim/README.md');
	console.log('');
	console.log('pokemon-showdown json-team');
	console.log('');
	console.log('  Reads a team in any format from stdin, writes the unpacked JSON to stdout');
	console.log('');
	console.log('pokemon-showdown pack-team');
	console.log('');
	console.log('  Reads a team in any format from stdin, writes the packed team to stdout');
	console.log('');
	console.log('pokemon-showdown export-team');
	console.log('');
	console.log('  Reads a team in any format from stdin, writes the exported (human-readable) team to stdout');
	console.log('');
	console.log('pokemon-showdown help');
	console.log('');
	console.log('  Displays this reference');
}

try {
	fetch;
} catch (e) {
	// fetch was introduced in Node 18, which is EOL,
	// so we'll ask for the most recent "Active LTS" with it to be safe
	// https://nodejs.org/en/about/previous-releases
	console.error("We require Node.js version 22 or later; you're using " + process.version);
	process.exit(1);
}

var cliOpts = {
	help: {
		type: 'boolean',
		short: 'h',
		default: false
	},
	'skip-build': {
		type: 'boolean',
		default: false
	},
	debug: {
		type: 'boolean',
		short: 'D',
		default: false
	},
	replay: {
		type: 'boolean',
		short: 'R',
		default: false
	},
	spectate: {
		type: 'boolean',
		short: 'S',
		default: false
	}
};

var parserOpts = {
	options: cliOpts,
	strict: false,
	allowPositionals: true,
	allowNegative: false
};

cli = util.parseArgs(parserOpts);

if (cli.values['help']) {
	showHelp();
	process.exit(0);
}

if (!cli.positionals.length || /^[0-9]+$/.test(cli.positionals[0])) {
	// Start the server.
	//
	// The port the server should host on can be passed as an argument, e.g. to
	// host on port 9000:
	// $ ./pokemon-showdown 9000
	build();
	require('module')._load('./dist/server/index.js', module, true);
} else {
	switch (cli.positionals[0]) {
	case 'help':
	case 'h':
	case '?':
	{
		showHelp();
		break;
	}
	case 'start':
	{
		build();
		require('module')._load('./dist/server/index.js', module, true);
		break;
	}
	case 'generate-team':
		{
			ensureBuilt();
			var Teams = require('./dist/sim/teams.js').Teams;
			var seed = cli.positionals[2] || undefined;
			console.log(Teams.pack(Teams.generate(cli.positionals[1], {seed})));
		}
		break;
	case 'generate-teams':
			{
				ensureBuilt();
				var Teams = require('./dist/sim/teams.js').Teams;
				for (var i = 0; i < (process.argv[4] || 1000); i++)
					console.log(Teams.pack(Teams.generate(process.argv[3], { seed })));
			}
			break;
	case 'validate-team':
		{
			ensureBuilt();
			require('source-map-support/register');
			var Teams = require('./dist/sim/teams.js').Teams;
			var TeamValidator = require('./dist/sim/team-validator.js').TeamValidator;
			var validator = TeamValidator.get(cli.positionals[1]);
			var Streams = require('./dist/lib/streams.js');
			var stdin = Streams.stdin();

			readTeam(stdin).then(function (textTeam) {
				try {
					var team = Teams.import(textTeam);
					var result = validator.validateTeam(team);
					if (result) {
						console.error(result.join('\n'));
						process.exit(1);
					}
					process.exit(0);
				} catch (e) {
					console.error(e);
					process.exit(1);
				}
			});
		}
		break;
	case 'simulate-battle':
		{
			build();
			var BattleTextStream = require('./dist/sim/battle-stream.js').BattleTextStream;
			var Streams = require('./dist/lib/streams');
			var stdin = Streams.stdin();
			var stdout = Streams.stdout();

			var battleStream = new BattleTextStream({
				noCatch: true,
				debug: cli.values['debug'],
				replay: cli.values['spectate'] ? 'spectator' : cli.values['replay']
			});
			stdin.pipeTo(battleStream);
			battleStream.pipeTo(stdout);
		}
		break;
	case 'unpack-team':
	case 'json-team':
		{
			build();
			var Teams = require('./dist/sim/teams').Teams;
			var Streams = require('./dist/lib/streams');
			var stdin = Streams.stdin();

			readTeam(stdin).then(function (team) {
				try {
					var unpackedTeam = Teams.unpack(Teams.import(team));
					console.log(JSON.stringify(unpackedTeam));
					process.exit(0);
				} catch (e) {
					console.error(e);
					process.exit(1);
				}
			});
		}
		break;
	case 'pack-team':
		{
			build();
			var Teams = require('./dist/sim/teams').Teams;
			var Streams = require('./dist/lib/streams');
			var stdin = Streams.stdin();

			readTeam(stdin).then(function (team) {
				try {
					var packedTeam = Teams.pack(Teams.import(team));
					console.log(packedTeam);
					process.exit(0);
				} catch (e) {
					console.error(e);
					process.exit(1);
				}
			});
		}
		break;
	case 'export-team':
		{
			build();
			var Teams = require('./dist/sim/teams').Teams;
			var Streams = require('./dist/lib/streams');
			var stdin = Streams.stdin();

			readTeam(stdin).then(function (team) {
				try {
					var exportedTeam = Teams.export(Teams.import(team));
					console.log(exportedTeam);
					process.exit(0);
				} catch (e) {
					console.error(e);
					process.exit(1);
				}
			});
		}
		break;
	default:
		console.error('Unrecognized command: ' + cli.positionals[0]);
		console.error('Use `pokemon-showdown help` for help');
		process.exit(1);
	}
}
