text: 'use strict';
  
  const RandomTeams = require('../../random-teams');
  
  class RandomLetsGoTeams extends RandomTeams {
  	/**
  	 * @param {string | Species} species
  	 * @param {RandomTeamsTypes.TeamDetails} [teamDetails]
  	 * @return {RandomTeamsTypes.RandomSet}
  	 */
  	randomSet(species, teamDetails = {}) {
  		species = this.dex.getSpecies(species);
  		let forme = species.name;
  
  		if (species.battleOnly) {
  			// Only change the forme. The species has custom moves, and may have different typing and requirements.
  			forme = /** @type {string} */ (species.battleOnly);
  		}
  
  		// @ts-ignore
  		let movePool = (species.randomBattleMoves || Object.keys(this.dex.data.Learnsets[species.id].learnset)).slice();
  		/**@type {string[]} */
  		let moves = [];
  		/**@type {{[k: string]: true}} */
  		let hasType = {};
  		hasType[species.types[0]] = true;
  		if (species.types[1]) {
  			hasType[species.types[1]] = true;
  		}
  
  		/**@type {{[k: string]: boolean}} */
  		let hasMove = {};
  		let counter;
  
  		do {
  			// Keep track of all moves we have:
  			hasMove = {};
  			for (const setMoveid of moves) {
  				hasMove[setMoveid] = true;
  			}
  
  			// Choose next 4 moves from learnset/viable moves and add them to moves list:
  			while (moves.length < 4 && movePool.length) {
  				let moveid = this.sampleNoReplace(movePool);
  				hasMove[moveid] = true;
  				moves.push(moveid);
  			}
  
  			counter = this.queryMoves(moves, hasType, {}, movePool);
  
  			// Iterate through the moves again, this time to cull them:
  			for (const [i, setMoveid] of moves.entries()) {
  				let move = this.dex.getMove(setMoveid);
  				let moveid = move.id;
  				let rejected = false;
  				let isSetup = false;
  
  				switch (moveid) {
  				// Set up once and only if we have the moves for it
  				case 'bulkup': case 'swordsdance':
  					if (counter.setupType !== 'Physical' || counter['physicalsetup'] > 1) rejected = true;
  					if (counter.Physical + counter['physicalpool'] < 2) rejected = true;
  					isSetup = true;
  					break;
  				case 'calmmind': case 'nastyplot': case 'quiverdance':
  					if (counter.setupType !== 'Special' || counter['specialsetup'] > 1) rejected = true;
  					if (counter.Special + counter['specialpool'] < 2) rejected = true;
  					isSetup = true;
  					break;
  				case 'growth': case 'shellsmash':
  					if (counter.setupType !== 'Mixed') rejected = true;
  					if (counter.damagingMoves.length + counter['physicalpool'] + counter['specialpool'] < 2) rejected = true;
  					isSetup = true;
  					break;
  				case 'agility':
  					if (counter.damagingMoves.length < 2 && !counter.setupType) rejected = true;
  					if (!counter.setupType) isSetup = true;
  					break;
  
  				// Bad after setup
  				case 'dragontail':
  					if (counter.setupType || !!counter['speedsetup'] || hasMove['encore'] || hasMove['roar'] || hasMove['whirlwind']) rejected = true;
  					break;
  				case 'fakeout': case 'uturn':
  					if (counter.setupType || !!counter['speedsetup'] || hasMove['substitute']) rejected = true;
  					break;
  				case 'haze': case 'leechseed': case 'roar': case 'whirlwind':
  					if (counter.setupType || !!counter['speedsetup'] || hasMove['dragontail']) rejected = true;
  					break;
  				case 'protect':
  					if (counter.setupType || hasMove['rest'] || hasMove['lightscreen'] || hasMove['reflect']) rejected = true;
  					break;
  				case 'seismictoss':
  					if (counter.damagingMoves.length > 1 || counter.setupType) rejected = true;
  					break;
  				case 'stealthrock':
  					if (counter.setupType || !!counter['speedsetup'] || teamDetails.stealthRock) rejected = true;
  					break;
  
  				// Bit redundant to have both
  				case 'leechlife': case 'substitute':
  					if (hasMove['uturn']) rejected = true;
  					break;
  				case 'dragonclaw': case 'dragonpulse':
  					if (hasMove['dragontail'] || hasMove['outrage']) rejected = true;
  					break;
  				case 'thunderbolt':
  					if (hasMove['thunder']) rejected = true;
  					break;
  				case 'flareblitz': case 'flamethrower': case 'lavaplume':
  					if (hasMove['fireblast'] || hasMove['firepunch']) rejected = true;
  					break;
  				case 'megadrain':
  					if (hasMove['petaldance'] || hasMove['powerwhip']) rejected = true;
  					break;
  				case 'bonemerang':
  					if (hasMove['earthquake']) rejected = true;
  					break;
  				case 'icebeam':
  					if (hasMove['blizzard']) rejected = true;
  					break;
  				case 'return':
  					if (hasMove['bodyslam'] || hasMove['facade'] || hasMove['doubleedge']) rejected = true;
  					break;
  				case 'psychic':
  					if (hasMove['psyshock']) rejected = true;
  					break;
  				case 'rockslide':
  					if (hasMove['stoneedge']) rejected = true;
  					break;
  				case 'hydropump': case 'willowisp':
  					if (hasMove['scald']) rejected = true;
  					break;
  				case 'surf':
  					if (hasMove['hydropump'] || hasMove['scald']) rejected = true;
  					break;
  				}
  
  				// Increased/decreased priority moves are unneeded with moves that boost only speed
  				if (move.priority !== 0 && !!counter['speedsetup']) {
  					rejected = true;
  				}
  
  				// This move doesn't satisfy our setup requirements:
  				if ((move.category === 'Physical' && counter.setupType === 'Special') || (move.category === 'Special' && counter.setupType === 'Physical')) {
  					// Reject STABs last in case the setup type changes later on
  					if (!hasType[move.type] || counter.stab > 1 || counter[move.category] < 2) rejected = true;
  				}
  				// @ts-ignore
  				if (counter.setupType && !isSetup && counter.setupType !== 'Mixed' && move.category !== counter.setupType && counter[counter.setupType] < 2) {
  					// Mono-attacking with setup and RestTalk is allowed
  					// Reject Status moves only if there is nothing else to reject
  					// @ts-ignore
  					if (move.category !== 'Status' || counter[counter.setupType] + counter.Status > 3 && counter['physicalsetup'] + counter['specialsetup'] < 2) rejected = true;
  				}
  
  				// Pokemon should have moves that benefit their Type, as well as moves required by its forme
  				// @ts-ignore
  				if (!rejected && (counter['physicalsetup'] + counter['specialsetup'] < 2 && (!counter.setupType || counter.setupType === 'Mixed' || (move.category !== counter.setupType && move.category !== 'Status') || counter[counter.setupType] + counter.Status > 3)) &&
  					(((counter.damagingMoves.length === 0 || !counter.stab) && (counter['physicalpool'] || counter['specialpool'])) ||
  					(hasType['Dark'] && !counter['Dark']) ||
  					(hasType['Dragon'] && !counter['Dragon']) ||
  					(hasType['Electric'] && !counter['Electric']) ||
  					(hasType['Fighting'] && !counter['Fighting'] && (counter.setupType || !counter['Status'])) ||
  					(hasType['Fire'] && !counter['Fire']) ||
  					(hasType['Ghost'] && !hasType['Dark'] && !counter['Ghost']) ||
  					(hasType['Ground'] && !counter['Ground']) ||
  					(hasType['Ice'] && !counter['Ice']) ||
  					(hasType['Water'] && (!counter['Water'] || !counter.stab)))) {
  					// Reject Status or non-STAB
  					if (!isSetup && !move.damage && (move.category !== 'Status' || !move.flags.heal)) {
  						if (move.category === 'Status' || !hasType[move.type] || move.selfSwitch || move.basePower && move.basePower < 40 && !move.multihit) rejected = true;
  					}
  				}
  
  				// Remove rejected moves from the move list
  				if (rejected && movePool.length) {
  					moves.splice(i, 1);
  					break;
  				}
  			}
  		} while (moves.length < 4 && movePool.length);
  
  		let ivs = {
  			hp: 31,
  			atk: 31,
  			def: 31,
  			spa: 31,
  			spd: 31,
  			spe: 31,
  		};
  
  		// Minimize confusion damage
  		if (!counter['Physical'] && !hasMove['transform']) {
  			ivs.atk = 0;
  		}
  
  		return {
  			name: species.baseSpecies,
  			species: forme,
  			level: 100,
  			gender: species.gender,
  			happiness: 70,
  			shiny: this.randomChance(1, 1024),
  			item: (species.requiredItem || ''),
  			ability: 'No Ability',
  			moves: moves,
  			evs: {hp: 20, atk: 20, def: 20, spa: 20, spd: 20, spe: 20},
  			ivs: ivs,
  		};
  	}
  
  	randomTeam() {
  		let pokemon = [];
  
  		let pokemonPool = [];
  		for (let id in this.dex.data.FormatsData) {
  			let species = this.dex.getSpecies(id);
  			if (species.num < 1 || (species.num > 151 && ![808, 809].includes(species.num)) || species.gen > 7 || species.nfe || !species.randomBattleMoves || !species.randomBattleMoves.length) continue;
  			pokemonPool.push(id);
  		}
  
  		/**@type {{[k: string]: number}} */
  		let typeCount = {};
  		/**@type {{[k: string]: number}} */
  		let typeComboCount = {};
  		/**@type {{[k: string]: number}} */
  		let baseFormes = {};
  		/**@type {RandomTeamsTypes.TeamDetails} */
  		let teamDetails = {};
  
  		while (pokemonPool.length && pokemon.length < 6) {
  			let species = this.dex.getSpecies(this.sampleNoReplace(pokemonPool));
  			if (!species.exists) continue;
  
  			// Limit to one of each species (Species Clause)
  			if (baseFormes[species.baseSpecies]) continue;
  
  			let types = species.types;
  
  			// Limit 2 of any type
  			let skip = false;
  			for (const type of species.types) {
  				if (typeCount[type] > 1 && this.randomChance(4, 5)) {
  					skip = true;
  					break;
  				}
  			}
  			if (skip) continue;
  
  			let set = this.randomSet(species, teamDetails);
  
  			// Limit 1 of any type combination
  			let typeCombo = types.slice().sort().join();
  			if (typeComboCount[typeCombo] >= 1) continue;
  
  			// Okay, the set passes, add it to our team
  			pokemon.push(set);
  
  			// Now that our Pokemon has passed all checks, we can increment our counters
  			baseFormes[species.baseSpecies] = 1;
  
  			// Increment type counters
  			for (const type of types) {
  				if (type in typeCount) {
  					typeCount[type]++;
  				} else {
  					typeCount[type] = 1;
  				}
  			}
  			if (typeCombo in typeComboCount) {
  				typeComboCount[typeCombo]++;
  			} else {
  				typeComboCount[typeCombo] = 1;
  			}
  
  			// Team details
  			if (set.moves.includes('stealthrock')) teamDetails['stealthRock'] = 1;
  			if (set.moves.includes('rapidspin')) teamDetails['rapidSpin'] = 1;
  		}
  		return pokemon;
  	}
  }
  
  module.exports = RandomLetsGoTeams;
  