"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var teams_exports = {};
__export(teams_exports, {
  TeamsHandler: () => TeamsHandler,
  commands: () => commands,
  destroy: () => destroy,
  pages: () => pages
});
module.exports = __toCommonJS(teams_exports);
var import_lib = require("../../lib");
const MAX_TEAMS = 200;
const MAX_SEARCH = 3e3;
const ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz".split("");
function refresh(context) {
  return `<button class="button" name="send" value="/j ${context.pageid}" style="float: right"> <i class="fa fa-refresh"></i> ${context.tr("Refresh")}</button>`;
}
const TeamsHandler = new class {
  constructor() {
    this.database = new import_lib.PostgresDatabase();
    this.readyPromise = Config.usepostgres ? (async () => {
      try {
        await this.database.query("SELECT * FROM teams LIMIT 1");
      } catch {
        await this.database.query((0, import_lib.FS)(`databases/schemas/teams.sql`).readSync());
      }
    })() : null;
  }
  destroy() {
    void this.database.destroy();
  }
  async search(search, user, count = 10, includePrivate = false) {
    const args = [];
    const where = [];
    if (count > 500) {
      throw new Chat.ErrorMessage("Cannot search more than 500 teams.");
    }
    if (search.format) {
      where.push(`format = $${args.length + 1}`);
      args.push(toID(search.format));
    }
    if (search.owner) {
      where.push(`ownerid = $${args.length + 1}`);
      args.push(toID(search.owner));
    }
    if (search.gen) {
      where.push(`format LIKE 'gen${search.gen}%'`);
    }
    if (!includePrivate)
      where.push("private IS NULL");
    const result = await this.query(
      `SELECT * FROM teams${where.length ? ` WHERE ${where.join(" AND ")}` : ""} ORDER BY date DESC LIMIT ${count}`,
      args
    );
    return result.filter((row) => {
      const team = Teams.unpack(row.team);
      if (row.private && row.ownerid !== user.id) {
        return false;
      }
      let match = true;
      if (search.pokemon?.length) {
        match = search.pokemon.some(
          (pokemon) => team.some((set) => toID(set.species) === toID(pokemon))
        );
      }
      if (!match)
        return false;
      if (search.moves?.length) {
        match = search.moves.some(
          (move) => team.some((set) => set.moves.some((m) => toID(m) === toID(move)))
        );
      }
      if (!match)
        return false;
      if (search.abilities?.length) {
        match = search.abilities.some(
          (ability) => team.some((set) => toID(set.ability) === toID(ability))
        );
      }
      return match;
    });
  }
  async query(statement, values = []) {
    if (this.readyPromise)
      await this.readyPromise;
    return this.database.query(statement, values);
  }
  async save(context, formatName, rawTeam, teamName = null, isPrivate, isUpdate) {
    const connection = context.connection;
    this.validateAccess(connection, true);
    if (Monitor.countPrepBattle(connection.ip, connection)) {
      return null;
    }
    const user = connection.user;
    const format = Dex.formats.get(toID(formatName));
    if (!format.exists || format.team) {
      connection.popup("Invalid format:\n\n" + formatName);
      return null;
    }
    let existing = null;
    if (isUpdate) {
      existing = await this.get(isUpdate);
      if (!existing) {
        connection.popup("You're trying to edit a team that doesn't exist.");
        return null;
      }
      if (context.user.id !== existing.ownerid) {
        connection.popup("This is not your team.");
        return null;
      }
    }
    const team = Teams.import(rawTeam, true);
    if (!team) {
      connection.popup("Invalid team:\n\n" + rawTeam);
      return null;
    }
    if (team.length > 24) {
      connection.popup("Your team has too many Pokemon.");
    }
    let unownWord = "";
    for (const set of team) {
      const namedSpecies = Dex.species.get(set.name);
      if (!namedSpecies.exists) {
        set.name = set.species;
      }
      if (!Dex.species.get(set.species).exists) {
        connection.popup(`Invalid Pokemon ${set.species} in team.`);
        return null;
      }
      const speciesid = toID(set.species);
      if (speciesid.length <= 6 && speciesid.startsWith("unown")) {
        unownWord += speciesid.charAt(5) || "a";
      }
      if (set.moves.length > 24) {
        connection.popup("Only 24 moves are allowed per set.");
        return null;
      }
      for (const m of set.moves) {
        if (!Dex.moves.get(m).exists) {
          connection.popup(`Invalid move ${m} on ${set.species}.`);
          return null;
        }
      }
      if (toID(set.ability) === "none") {
        set.ability = "No Ability";
      }
      if (set.ability && !Dex.abilities.get(set.ability).exists) {
        connection.popup(`Invalid ability ${set.ability} on ${set.species}.`);
        return null;
      }
      if (set.item && !Dex.items.get(set.item).exists) {
        connection.popup(`Invalid item ${set.item} on ${set.species}.`);
        return null;
      }
      if (set.nature && !Dex.natures.get(set.nature).exists) {
        connection.popup(`Invalid nature ${set.nature} on ${set.species}.`);
        return null;
      }
      if (set.teraType && !Dex.types.get(set.teraType).exists) {
        connection.popup(`Invalid Tera Type ${set.nature} on ${set.species}.`);
        return null;
      }
    }
    if (unownWord) {
      const filtered = Chat.nicknamefilter(unownWord, user);
      if (!filtered || filtered !== unownWord) {
        connection.popup(
          `Your team was rejected for the following reason:

- Your Unowns spell out a banned word: ${unownWord.toUpperCase()}`
        );
        return null;
      }
    }
    if (teamName) {
      if (teamName.length > 100) {
        connection.popup("Your team's name is too long.");
        return null;
      }
      const filtered = context.filter(teamName);
      if (!filtered || filtered?.trim() !== teamName.trim()) {
        connection.popup(`Your team's name has a filtered word.`);
        return null;
      }
    }
    const count = await this.count(user);
    if (count >= MAX_TEAMS) {
      connection.popup(`You have too many teams stored. If you wish to upload this team, delete some first.`);
      return null;
    }
    rawTeam = Teams.pack(team);
    if (isUpdate && existing) {
      const differenceExists = existing.team !== rawTeam || teamName && teamName !== existing.title || format.id !== existing.format || existing.private !== isPrivate;
      if (!differenceExists) {
        connection.popup("Your team was not saved as no changes were made.");
        return null;
      }
      await this.query(
        "UPDATE teams SET team = $1, title = $2, private = $3, format = $4 WHERE teamid = $5",
        [rawTeam, teamName, isPrivate, format.id, isUpdate]
      );
      return isUpdate;
    } else {
      const exists = await this.query("SELECT * FROM teams WHERE ownerid = $1 AND team = $2", [user.id, rawTeam]);
      if (exists.length) {
        connection.popup("You've already uploaded that team.");
        return null;
      }
      const loaded = await this.query(
        `INSERT INTO teams (ownerid, team, date, format, views, title, private) VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING teamid`,
        [user.id, rawTeam, new Date(), format.id, 0, teamName, isPrivate]
      );
      return loaded?.[0].teamid;
    }
  }
  generatePassword(len = 20) {
    let pw = "";
    for (let i = 0; i < len; i++)
      pw += ALPHABET[Math.floor(Math.random() * ALPHABET.length)];
    return pw;
  }
  updateViews(teamid) {
    return this.query(`UPDATE teams SET views = views + 1 WHERE teamid = $1`, [teamid]);
  }
  list(userid, count, publicOnly = false) {
    let query = `SELECT * FROM teams WHERE ownerid = $1 `;
    if (publicOnly) {
      query += `AND private IS NULL `;
    }
    query += `ORDER BY date DESC LIMIT $2`;
    return this.query(
      query,
      [userid, count]
    );
  }
  preview(teamData, user, isFull = false) {
    let buf = import_lib.Utils.html`<strong>${teamData.title || `Untitled ${teamData.teamid}`}`;
    if (teamData.private)
      buf += ` (Private)`;
    buf += `</strong><br />`;
    buf += `<small>Uploaded by: <strong>${teamData.ownerid}</strong></small><br />`;
    buf += `<small>Uploaded on: ${Chat.toTimestamp(teamData.date, { human: true })}</small><br />`;
    buf += `<small>Format: ${Dex.formats.get(teamData.format).name}</small><br />`;
    buf += `<small>Views: ${teamData.views === -1 ? 0 : teamData.views}</small>`;
    const team = Teams.unpack(teamData.team);
    let link = `view-team-${teamData.teamid}`;
    if (teamData.private) {
      link += `-${teamData.private}`;
    }
    buf += `<br /><a class="subtle" href="/${link}">`;
    buf += team.map((set) => `<psicon pokemon="${set.species}" />`).join(" ");
    buf += `</a><br />${isFull ? "View full team" : "Shareable link to team"}</a>`;
    buf += ` <small>(or copy/paste <code>&lt;&lt;${link}&gt;&gt;</code> in chat to share!)</small>`;
    if (user && (teamData.ownerid === user.id || user.can("rangeban"))) {
      buf += `<br />`;
      buf += `<details class="readmore"><summary>Manage (edit/delete/etc)</summary>`;
      buf += `<button class="button" name="send" value="/teams setprivacy ${teamData.teamid},${teamData.private ? "no" : "yes"}">`;
      buf += teamData.private ? `Make public` : `Make private`;
      buf += `</button><br />`;
      buf += `<button class="button" name="send" value="/teams delete ${teamData.teamid}">Delete team</button><br />`;
      buf += `<button class="button" name="send" value="/j view-teams-edit-${teamData.teamid}">Edit team</button>`;
      buf += `</details>`;
    }
    return buf;
  }
  renderTeam(teamData, user) {
    let buf = this.preview(teamData, user, true);
    buf += `<hr />`;
    const team = Teams.unpack(teamData.team);
    buf += team.map((set) => {
      let teamBuf = Teams.exportSet(set).replace(/\n/g, "<br />");
      if (set.name && set.name !== set.species) {
        teamBuf = teamBuf.replace(set.name, import_lib.Utils.html`<psicon pokemon="${set.species}" /> <br />${set.name}`);
      } else {
        teamBuf = teamBuf.replace(set.species, `<psicon pokemon="${set.species}" /> <br />${set.species}`);
      }
      if (set.item) {
        const tester = new RegExp(`${import_lib.Utils.escapeRegex(set.item)}\\b`);
        teamBuf = teamBuf.replace(tester, `${set.item} <psicon item="${set.item}" />`);
      }
      return teamBuf;
    }).join("<hr />");
    return buf;
  }
  validateAccess(conn, popup = false) {
    const user = conn.user;
    if (!user)
      throw new Chat.Interruption();
    const err = (message) => {
      if (popup) {
        conn.popup(message);
        throw new Chat.Interruption();
      }
      throw new Chat.ErrorMessage(message);
    };
    if (!Config.usepostgres || !Config.usepostgresteams) {
      err(`The teams database is currently disabled.`);
    }
    if (!Users.globalAuth.atLeast(user, Config.usepostgresteams)) {
      err("You cannot currently use the teams database.");
    }
    if (user.locked || user.semilocked)
      err("You cannot use the teams database while locked.");
    if (!user.autoconfirmed)
      err("You must be autoconfirmed to use the teams database.");
  }
  async count(user) {
    const id = toID(user);
    const result = await this.query(`SELECT count(*) AS count FROM teams WHERE ownerid = $1`, [id]);
    return result?.[0]?.count || 0;
  }
  async get(teamid) {
    teamid = Number(teamid);
    if (isNaN(teamid)) {
      throw new Chat.ErrorMessage(`Invalid team ID.`);
    }
    const rows = await this.query(
      `SELECT * FROM teams WHERE teamid = $1`,
      [teamid]
    );
    if (!rows.length)
      return null;
    return rows[0];
  }
  async delete(id) {
    id = Number(id);
    if (isNaN(id)) {
      throw new Chat.ErrorMessage("Invalid team ID");
    }
    await this.query(
      `DELETE FROM teams WHERE teamid = $1`,
      [id]
    );
  }
}();
const destroy = () => TeamsHandler.destroy();
const commands = {
  teams: {
    upload() {
      return this.parse("/j view-teams-upload");
    },
    update: "save",
    async save(target, room, user, connection, cmd) {
      TeamsHandler.validateAccess(connection, true);
      const targets = import_lib.Utils.splitFirst(target, ",", 5);
      const isEdit = cmd === "update";
      const rawTeamID = isEdit ? targets.shift() : void 0;
      let [teamName, formatid, rawPrivacy, rawTeam] = targets;
      const teamID = Number(rawTeamID);
      if (isEdit && (!rawTeamID?.length || isNaN(teamID))) {
        connection.popup("Invalid team ID provided.");
        return null;
      }
      if (rawTeam.includes("\n")) {
        rawTeam = Teams.pack(Teams.import(rawTeam, true));
      }
      if (!rawTeam) {
        connection.popup("Invalid team.");
        return null;
      }
      formatid = toID(formatid);
      teamName = toID(teamName) ? teamName : null;
      const privacy = toID(rawPrivacy) === "1" ? TeamsHandler.generatePassword() : null;
      const id = await TeamsHandler.save(
        this,
        formatid,
        rawTeam,
        teamName,
        privacy,
        isEdit ? teamID : void 0
      );
      const page = isEdit ? "edit" : "upload";
      if (id) {
        connection.send(`|queryresponse|teamupload|` + JSON.stringify({ teamid: id, teamName }));
        connection.send(`>view-teams-${page}
|deinit`);
        this.parse(`/join view-teams-view-${id}-${id}`);
      } else {
        this.parse(`/join view-teams-${page}`);
        return;
      }
    },
    ""(target) {
      return this.parse("/teams user " + toID(target) || this.user.id);
    },
    latest() {
      return this.parse(`/j view-teams-filtered-latest`);
    },
    views: "mostviews",
    mostviews() {
      return this.parse(`/j view-teams-filtered-views`);
    },
    user: "view",
    for: "view",
    view(target) {
      const [name, rawNum] = target.split(",").map(toID);
      const num = parseInt(rawNum);
      if (rawNum && isNaN(num)) {
        return this.popupReply(`Invalid count.`);
      }
      let page = "view";
      switch (this.cmd) {
        case "for":
        case "user":
          page = "all";
          break;
      }
      return this.parse(`/j view-teams-${page}-${toID(name)}${num ? `-${num}` : ""}`);
    },
    async delete(target, room, user, connection) {
      TeamsHandler.validateAccess(connection, true);
      const teamid = Number(toID(target));
      if (isNaN(teamid))
        return this.popupReply(`Invalid team ID.`);
      const teamData = await TeamsHandler.get(teamid);
      if (!teamData)
        return this.popupReply(`Team not found.`);
      if (teamData.ownerid !== user.id && !user.can("rangeban")) {
        return this.errorReply("You cannot delete teams you do not own.");
      }
      await TeamsHandler.delete(teamid);
      this.popupReply(`Team ${teamid} deleted.`);
      for (const page of connection.openPages || /* @__PURE__ */ new Set()) {
        if (page.startsWith("teams-"))
          this.refreshPage(page);
      }
    },
    async setprivacy(target, room, user, connection) {
      TeamsHandler.validateAccess(connection, true);
      const [teamId, rawPrivacy] = target.split(",").map(toID);
      let privacy;
      if (!teamId.length) {
        return this.popupReply("Invalid team ID.");
      }
      if (this.meansYes(rawPrivacy)) {
        privacy = TeamsHandler.generatePassword();
      } else if (this.meansNo(rawPrivacy)) {
        privacy = null;
      } else {
        return this.popupReply(`Invalid privacy setting.`);
      }
      const team = await TeamsHandler.get(teamId);
      if (!team)
        return this.popupReply(`Team not found.`);
      if (team.ownerid !== user.id && !user.can("rangeban")) {
        return this.popupReply(`You cannot change privacy for a team you don't own.`);
      }
      await TeamsHandler.query(`UPDATE teams SET private = $1 WHERE teamid = $2`, [privacy, teamId]);
      for (const pageid of this.connection.openPages || /* @__PURE__ */ new Set()) {
        if (pageid.startsWith("teams-")) {
          this.refreshPage(pageid);
        }
      }
      return this.popupReply(privacy ? `Team set to private. Password: ${privacy}` : `Team set to public.`);
    },
    search(target, room, user) {
      return this.parse(`/j view-teams-searchpersonal`);
    },
    browse(target, room, user) {
      return this.parse(`/j view-teams-browse${target ? `-${target}` : ""}`);
    },
    help() {
      return this.parse("/help teams");
    }
  },
  teamshelp: [
    `/teams OR /teams for [user]- View the (public) teams of the given [user].`,
    `/teams upload - Open the page to upload a team.`,
    `/teams setprivacy [team id], [privacy] - Set the privacy of the team matching the [teamid].`,
    `/teams delete [team id] - Delete the team matching the [teamid].`,
    `/teams search - Opens the page to search your teams`,
    `/teams mostviews - Views public teams, sorted by most views.`,
    `/teams view [team ID] - View the team matching the given [team ID]`,
    `/teams browse - Opens a list of public teams uploaded by other users.`
  ]
};
const pages = {
  // support view-team-${teamid}
  team(query, user, connection) {
    return pages.teams.view.call(this, query, user, connection);
  },
  teams: {
    async all(query, user, connection) {
      if (!user.named)
        return Rooms.RETRY_AFTER_LOGIN;
      TeamsHandler.validateAccess(connection);
      const targetUserid = toID(query.shift()) || user.id;
      let count = Number(query.shift()) || 10;
      if (count > MAX_TEAMS)
        count = MAX_TEAMS;
      this.title = `[Teams] ${targetUserid}`;
      const teams = await TeamsHandler.list(targetUserid, count, user.id !== targetUserid);
      let buf = `<div class="ladder pad"><h2>${targetUserid}'s last ${Chat.count(count, "teams")}</h2>`;
      buf += refresh(this);
      buf += `<br /><a class="button" href="/view-teams-searchpersonal">Search your teams</a> `;
      buf += `<a class="button" href="/view-teams-searchpublic">Browse public teams</a><br />`;
      if (targetUserid === user.id) {
        buf += `<a class="button" href="/view-teams-upload">Upload new</a>`;
      }
      buf += `<hr />`;
      for (const team of teams) {
        buf += TeamsHandler.preview(team, user);
        buf += `<hr />`;
      }
      const total = await TeamsHandler.count(user.id);
      if (total > count) {
        buf += `<button class="button" name="send" value="/j view-teams-all-${targetUserid}-${count + 20}">View more</button>`;
      }
      return buf;
    },
    async filtered(query, user, connection) {
      if (!user.named)
        return Rooms.RETRY_AFTER_LOGIN;
      const type = query.shift() || "";
      TeamsHandler.validateAccess(connection);
      let count = Number(query.shift()) || 50;
      if (count > MAX_TEAMS)
        count = MAX_TEAMS;
      let teams = [], title = "";
      const buttons = {
        views: `<button class="button" name="send" value="/teams mostviews">Sort by most views</button>`,
        latest: `<button class="button" name="send" value="/teams latest">Sort by most recent upload</button>`
      };
      switch (type) {
        case "views":
          this.title = `[Most Viewed Teams]`;
          teams = await TeamsHandler.query(
            `SELECT * FROM teams WHERE private IS NULL ORDER BY views DESC LIMIT $1`,
            [count]
          );
          title = `Most viewed teams:`;
          delete buttons.views;
          break;
        default:
          this.title = `[Latest Teams]`;
          teams = await TeamsHandler.query(
            `SELECT * FROM teams WHERE private IS NULL ORDER BY date DESC LIMIT $1`,
            [count]
          );
          title = `Recently uploaded teams:`;
          delete buttons.latest;
          break;
      }
      let buf = `<div class="ladder pad"><h2>${title}</h2>${refresh(this)}`;
      buf += Object.values(buttons).join("<br />");
      buf += `<hr />`;
      buf += teams.map((team) => TeamsHandler.preview(team, user)).join("<hr />");
      buf += `</div>`;
      return buf;
    },
    async view(query, user, connection) {
      if (!user.named)
        return Rooms.RETRY_AFTER_LOGIN;
      TeamsHandler.validateAccess(connection);
      const rawTeamid = toID(query.shift() || "");
      const password = toID(query.shift());
      this.title = `[View Team]`;
      const teamid = Number(rawTeamid);
      if (isNaN(teamid)) {
        throw new Chat.ErrorMessage(`Invalid team ID.`);
      }
      const team = await TeamsHandler.get(teamid);
      if (!team) {
        this.title = `[Invalid Team]`;
        return this.errorReply(`No team with the ID ${teamid} was found.`);
      }
      if (team?.private && user.id !== team.ownerid && password !== team.private) {
        this.title = `[Private Team]`;
        return this.errorReply(`That team is private.`);
      }
      this.title = `[Team] ${team.teamid}`;
      if (user.id !== team.ownerid && team.views >= 0) {
        void TeamsHandler.updateViews(team.teamid);
      }
      return `<div class="ladder pad">` + TeamsHandler.renderTeam(team, user) + "</div>";
    },
    upload(query, user, connection) {
      if (!user.named)
        return Rooms.RETRY_AFTER_LOGIN;
      TeamsHandler.validateAccess(connection);
      this.title = `[Upload Team]`;
      let buf = `<div class="ladder pad"><h2>Upload a team</h2>${refresh(this)}<hr />`;
      buf += `<form data-submitsend="/teams save {name},{format},{privacy},{team}">`;
      buf += `<strong>What's the name of the team?</strong><br />`;
      buf += `<input name="name" /><br />`;
      buf += `<strong>What's the team's format?</strong><br />`;
      buf += `<formatselect name="format" format="gen${Dex.gen}ou">[Gen ${Dex.gen} OU]</formatselect><br />`;
      buf += `<strong>Should the team be private? (yes/no)</strong><br />`;
      buf += `<select name="privacy" /><option value="1">Yes</option><option value="0">No</option></select><br />`;
      buf += `<strong>Provide the team:</strong><br />`;
      buf += `<textarea style="width: 100%; height: 400px" name="team"></textarea><br />`;
      buf += `<button class="button notifying" type="submit">Upload team</button>`;
      buf += `</form></div>`;
      return buf;
    },
    async edit(query, user, connection) {
      if (!user.named)
        return Rooms.RETRY_AFTER_LOGIN;
      TeamsHandler.validateAccess(connection);
      const teamID = toID(query.shift() || "");
      if (!teamID.length) {
        return this.errorReply(`Invalid team ID.`);
      }
      this.title = `[Edit Team] ${teamID}`;
      const data = await TeamsHandler.get(teamID);
      if (!data) {
        return this.errorReply(`Team ${teamID} not found.`);
      }
      let buf = `<div class="ladder pad"><h2>Edit team ${teamID}</h2>${refresh(this)}<hr />`;
      buf += `<form data-submitsend="/teams update ${teamID},{name},{format},{privacy},{team}">`;
      buf += `<strong>Team name</strong><br />`;
      buf += `<input name="name" value="${data.title || `Untitled ${teamID}`}" /><br />`;
      buf += `<strong>Team format</strong><br />`;
      buf += `<formatselect name="format" format="${data.format}">`;
      buf += `${Dex.formats.get(data.format).name}</formatselect><br />`;
      buf += `<strong>Team privacy</strong><br />`;
      const privacy = ["1", "0"];
      if (!data.private) {
        privacy.reverse();
      }
      buf += `<select name="privacy" />`;
      buf += `${privacy.map((v) => `<option value="${v}">${Number(v) ? "Yes" : "No"}</option>`)}`;
      buf += `</select><br />`;
      buf += `<strong>Team:</strong><br />`;
      const teamStr = Teams.export(Teams.import(data.team)).replace(/\n/g, "&#13;");
      buf += `<textarea style="width: 100%; height: 400px" name="team">${teamStr}</textarea><br />`;
      buf += `<button class="button notifying" type="submit">Upload team</button>`;
      buf += `</form></div>`;
      return buf;
    },
    async searchpublic(query, user, connection) {
      if (!user.named)
        return Rooms.RETRY_AFTER_LOGIN;
      TeamsHandler.validateAccess(connection, true);
      this.title = "[Teams] Search";
      let buf = '<div class="pad">';
      buf += refresh(this);
      buf += "<h2 />Search all teams</h2>";
      const type = this.pageid.split("-")[2];
      const isPersonal = type === "searchpersonal";
      query = query.join("-").split("--");
      if (!query.map(toID).filter(Boolean).length || isPersonal && query.length === 1) {
        buf += `<hr />`;
        buf += `<form data-submitsend="/join view-teams-${type}-{owner}--{tier}--{pokemon}--{moves}--{ability}--{gen}">`;
        buf += `<strong>Search metadata:</strong><br />`;
        buf += `<span style="display: ${isPersonal ? "none" : ""}">`;
        buf += `<Team owner: <input name="owner" /></span><br />`;
        buf += `Team format: <formatselect name="tier" format="gen${Dex.gen}ou">[Gen ${Dex.gen}] OU</formatselect><br /><br />`;
        buf += `<strong>Search in team:</strong> (separate different searches with commas)<br />`;
        buf += `Generation: <input name="gen" /><br />`;
        buf += `Pokemon: <input name="pokemon" /><br />`;
        buf += `Abilities: <input name="ability" /><br />`;
        buf += `Moves: <input name="moves" /><br /><br />`;
        buf += `<button class="button notifying" type="submit">Search!</button>`;
        return buf;
      }
      const [rawOwner, rawFormat, rawPokemon, rawMoves, rawAbilities, rawGen] = query;
      const owner = toID(rawOwner);
      if (owner.length > 18) {
        return this.errorReply(`Invalid owner name. Names must be under 18 characters long.`);
      }
      const format = toID(rawFormat);
      if (format && !Dex.formats.get(format).exists) {
        return this.errorReply(`Format ${format} not found.`);
      }
      const gen = Number(rawGen);
      if (rawGen && (isNaN(gen) || (gen < 1 || gen > Dex.gen))) {
        return this.errorReply(`Invalid generation: '${rawGen}'`);
      }
      const pokemon = rawPokemon?.split(",").map(toID).filter(Boolean);
      const moves = rawMoves?.split(",").map(toID).filter(Boolean);
      const abilities = rawAbilities?.split(",").map(toID).filter(Boolean);
      const search = {
        pokemon,
        moves,
        format,
        owner,
        abilities,
        gen: gen || void 0
      };
      const results = await TeamsHandler.search(search, user, 50, isPersonal);
      buf += `Search: ` + Object.entries(search).filter(([, v]) => !!v?.toString()).map(([k, v]) => `${k.charAt(0).toUpperCase() + k.slice(1)}: ${v}`).join(", ");
      buf += `<hr />`;
      if (!results.length) {
        buf += `<div class="message-error">No results found.</div>`;
        return buf;
      }
      buf += results.map((t) => TeamsHandler.preview(t, user)).join("<hr />");
      return buf;
    },
    async searchpersonal(query, user, connection) {
      if (!user.named)
        return Rooms.RETRY_AFTER_LOGIN;
      this.pageid = "view-teams-searchpersonal";
      return pages.teams.searchpublic.call(
        this,
        `${user.id}${query.join("-")}`.split("-"),
        user,
        connection
      );
    },
    async browse(query, user, connection) {
      if (!user.named)
        return Rooms.RETRY_AFTER_LOGIN;
      TeamsHandler.validateAccess(connection, true);
      const sorter = toID(query.shift()) || "latest";
      let count = Number(toID(query.shift())) || 50;
      if (count > MAX_SEARCH) {
        count = MAX_SEARCH;
      }
      let queryStr = "SELECT * FROM teams WHERE private IS NULL";
      let name = sorter;
      switch (sorter) {
        case "views":
          queryStr += ` ORDER BY views DESC `;
          name = "most viewed";
          break;
        case "latest":
          queryStr += ` ORDER BY date DESC`;
          break;
        default:
          return this.errorReply(`Invalid sort term '${sorter}'. Must be either 'views' or 'latest'.`);
      }
      queryStr += ` LIMIT ${count}`;
      let buf = `<div class="pad"><h2>Browse ${name} teams</h2>`;
      buf += refresh(this);
      buf += `<br /><a class="button" href="/view-teams-searchpublic">Search</a>`;
      const opposite = sorter === "views" ? "latest" : "views";
      buf += `<button class="button" name="send" value="/j view-teams-browse-${opposite}-${count}">Sort by ${opposite}</button>`;
      buf += `<hr />`;
      const results = await TeamsHandler.query(queryStr, []);
      if (!results.length) {
        buf += `<div class="message-error">None found.</div>`;
        return buf;
      }
      for (const team of results) {
        buf += TeamsHandler.preview(team, user);
        buf += `<hr />`;
      }
      if (count < MAX_SEARCH) {
        buf += `<button class="button" name="send" value="/j view-teams-browse-${sorter}-${count + 20}">View more</button>`;
      }
      return buf;
    }
  }
};
process.nextTick(() => {
  Chat.multiLinePattern.register("/teams save ", "/teams update ");
});
//# sourceMappingURL=teams.js.map
