util/HTMLParser.js

const { TypeError, Error } = require("../errors");
const { URLs } = require("./Constants");
const cheerio = require("cheerio");

/**
 * Parses raw HTML to readable objects
 */
class HTMLParser {
  constructor(html, type) {
    /**
     * The HTML to be parsed
     * @type {string}
     */
    this.html = html;

    /**
     * The type to parse
     * @type {string}
     */
    this.type = type;
  }

  /**
   * Parses the HTML to a readable object
   * @return {PlayerData|SquadronData|NewsInfo[]|Wiki[]}
   */
  parse() {
    switch (this.type.toLowerCase()) {
      case "user":
        return this.parseUser();
      case "clan":
        return this.parseClan();
      case "news":
        return this.parseNews();
      case "wiki":
        return this.parseWiki();
    }
    throw new TypeError("INVALID_TYPE");
  }
  parseUser() { // eslint-disable-line complexity
    const $ = cheerio.load(this.html);

    if (!$(".user-profile__data-nick").length) throw new TypeError("USER_UNAVAILABLE");

    // General profile information
    const info = $(".user-profile__data-item"),
      planes = $(".profile-score__list-col:nth-child(2) > .profile-score__list-item:not(:first-child)"),
      elite = $(".profile-score__list-col:nth-child(3) > .profile-score__list-item:not(:first-child)"),
      medals = $(".profile-score__list-col:nth-child(4) > .profile-score__list-item:not(:first-child)"),
      avab = $(".profile-stat__list-row:nth-child(1) > .profile-stat__list-ab:nth-child(3) > li:not(:first-child)"),
      avrb = $(".profile-stat__list-row:nth-child(1) > .profile-stat__list-rb:nth-child(4) > li:not(:first-child)"),
      avsb = $(".profile-stat__list-row:nth-child(1) > .profile-stat__list-sb:nth-child(5) > li:not(:first-child)"),
      grab = $(".profile-stat__list-row:nth-child(2) > .profile-stat__list-ab:nth-child(3) > li:not(:first-child)"),
      grrb = $(".profile-stat__list-row:nth-child(2) > .profile-stat__list-rb:nth-child(4) > li:not(:first-child)"),
      grsb = $(".profile-stat__list-row:nth-child(2) > .profile-stat__list-sb:nth-child(5) > li:not(:first-child)"),
      flab = $(".profile-stat__list-row:nth-child(3) > .profile-stat__list-ab:nth-child(3) > li:not(:first-child)"),
      flrb = $(".profile-stat__list-row:nth-child(3) > .profile-stat__list-rb:nth-child(4) > li:not(:first-child)"),
      flsb = $(".profile-stat__list-row:nth-child(3) > .profile-stat__list-sb:nth-child(5) > li:not(:first-child)"),
      abdata = $(".profile-stat__list-ab > .profile-stat__list-item"),
      rbdata = $(".profile-stat__list-rb > .profile-stat__list-item"),
      sbdata = $(".profile-stat__list-sb > .profile-stat__list-item");

    const profile = {
      name: $(".user-profile__data-nick").text().trim(),
      title: info.eq(0).text().trim().length ? info.eq(0).text().trim() : null,
      clan: info.eq(2).length ? { tag: info.eq(1).text().trim(), url: $(".user-profile__data-link").attr("href") } : null,
      level: info.eq(2).length ? parseInt(info.eq(2).text().trim()
        .split(" ")[1])
        : parseInt(info.eq(1).text().trim()
          .split(" ")[1]),
      avatar: "http:".concat($(".user-profile__ava-img").attr("src")),
      registered: $(".user-profile__data-regdate")
        .text()
        .trim()
        .replace(/Registration date\s/, ""),
      banned: Boolean($(".user-profile__data-nick--banned").length),
      // Country information
      usa: {
        vehicles: parseInt(planes.eq(0).text().trim()),
        elite: parseInt(elite.eq(0).text().trim()),
        medals: parseInt(medals.eq(0).text().trim())
      },
      ussr: {
        vehicles: parseInt(planes.eq(1).text().trim()),
        elite: parseInt(elite.eq(1).text().trim()),
        medals: parseInt(medals.eq(1).text().trim())
      },
      britain: {
        vehicles: parseInt(planes.eq(2).text().trim()),
        elite: parseInt(elite.eq(2).text().trim()),
        medals: parseInt(medals.eq(2).text().trim())
      },
      germany: {
        vehicles: parseInt(planes.eq(3).text().trim()),
        elite: parseInt(elite.eq(3).text().trim()),
        medals: parseInt(medals.eq(3).text().trim())
      },
      japan: {
        vehicles: parseInt(planes.eq(4).text().trim()),
        elite: parseInt(elite.eq(4).text().trim()),
        medals: parseInt(medals.eq(4).text().trim())
      },
      italy: {
        vehicles: parseInt(planes.eq(5).text().trim()),
        elite: parseInt(elite.eq(5).text().trim()),
        medals: parseInt(medals.eq(5).text().trim())
      },
      france: {
        vehicles: parseInt(planes.eq(6).text().trim()),
        elite: parseInt(elite.eq(6).text().trim()),
        medals: parseInt(medals.eq(6).text().trim())
      }
    };

    // Stat cards
    const stats = {
      arcade: {
        victories: !isNaN(parseInt(abdata.eq(1).text().trim())) ? parseInt(abdata.eq(1).text().trim()) : null,
        completed: !isNaN(
          parseInt(abdata.eq(2)
            .text()
            .trim()
            .replace(/,/g, "")
          )
        ) ? parseInt(abdata.eq(2)
            .text()
            .trim()
            .replace(/,/g, "")
          ) : null,
        ratio: abdata.eq(3).text().trim(),
        deaths: !isNaN(
          parseInt(abdata.eq(4)
            .text()
            .trim()
            .replace(/,/g, "")
          )
        ) ? parseInt(abdata.eq(4)
            .text()
            .trim()
            .replace(/,/g, "")
          ) : null,
        lionsEarned: !isNaN(
          parseInt(abdata.eq(5)
            .text()
            .trim()
            .replace(/,/g, "")
          )
        ) ? parseInt(abdata.eq(5)
            .text()
            .trim()
            .replace(/,/g, "")
          ) : null,
        playTime: abdata.eq(6).text().trim(),
        airTargetsDestroyed: !isNaN(parseInt(abdata.eq(7).text().trim())) ? parseInt(abdata.eq(7).text().trim()) : null,
        groundTargetsDestroyed: !isNaN(parseInt(abdata.eq(8).text().trim())) ? parseInt(abdata.eq(8).text().trim()) : null,
        navalTargetsDestroyed: !isNaN(parseInt(abdata.eq(9).text().trim())) ? parseInt(abdata.eq(9).text().trim()) : null,
        aviation: {
          battles: {
            total: !isNaN(avab.eq(0).text().trim()) ? parseInt(avab.eq(0).text().trim()) : null,
            fighter: !isNaN(avab.eq(1).text().trim()) ? parseInt(avab.eq(1).text().trim()) : null,
            bomber: !isNaN(avab.eq(2).text().trim()) ? parseInt(avab.eq(2).text().trim()) : null,
            attacker: !isNaN(avab.eq(3).text().trim()) ? parseInt(avab.eq(3).text().trim()) : null
          },
          playTime: {
            total: avab.eq(4).text().trim() !== "N/A" ? avab.eq(4).text().trim() : null,
            fighter: avab.eq(5).text().trim() !== "N/A" ? avab.eq(5).text().trim() : null,
            bomber: avab.eq(6).text().trim() !== "N/A" ? avab.eq(6).text().trim() : null,
            attacker: avab.eq(7).text().trim() !== "N/A" ? avab.eq(7).text().trim() : null
          },
          targetsDestroyed: {
            total: !isNaN(avab.eq(8).text().trim()) ? parseInt(avab.eq(8).text().trim()) : null,
            air: !isNaN(avab.eq(9).text().trim()) ? parseInt(avab.eq(9).text().trim()) : null,
            ground: !isNaN(avab.eq(10).text().trim()) ? parseInt(avab.eq(10).text().trim()) : null,
            naval: !isNaN(avab.eq(11).text().trim()) ? parseInt(avab.eq(11).text().trim()) : null
          }
        },
        ground: {
          battles: {
            total: !isNaN(grab.eq(0).text().trim()) ? parseInt(grab.eq(0).text().trim()) : null,
            tank: !isNaN(grab.eq(1).text().trim()) ? parseInt(grab.eq(1).text().trim()) : null,
            spg: !isNaN(grab.eq(2).text().trim()) ? parseInt(grab.eq(2).text().trim()) : null,
            heavy: !isNaN(grab.eq(3).text().trim()) ? parseInt(grab.eq(3).text().trim()) : null,
            spaa: !isNaN(grab.eq(4).text().trim()) ? parseInt(grab.eq(4).text().trim()) : null
          },
          playTime: {
            total: grab.eq(5).text().trim() !== "N/A" ? grab.eq(5).text().trim() : null,
            tank: grab.eq(6).text().trim() !== "N/A" ? grab.eq(5).text().trim() : null,
            spg: grab.eq(7).text().trim() !== "N/A" ? grab.eq(5).text().trim() : null,
            heavy: grab.eq(8).text().trim() !== "N/A" ? grab.eq(5).text().trim() : null,
            spaa: grab.eq(9).text().trim() !== "N/A" ? grab.eq(5).text().trim() : null
          },
          targetsDestroyed: {
            total: !isNaN(grab.eq(10).text().trim()) ? parseInt(grab.eq(10).text().trim()) : null,
            air: !isNaN(grab.eq(11).text().trim()) ? parseInt(grab.eq(11).text().trim()) : null,
            ground: !isNaN(grab.eq(12).text().trim()) ? parseInt(grab.eq(12).text().trim()) : null,
            naval: !isNaN(grab.eq(13).text().trim()) ? parseInt(grab.eq(13).text().trim()) : null
          }
        },
        fleet: {
          battles: {
            total: !isNaN(flab.eq(0).text().trim()) ? parseInt(flab.eq(0).text().trim()) : null,
            ship: !isNaN(flab.eq(1).text().trim()) ? parseInt(flab.eq(1).text().trim()) : null,
            torpedoBoat: !isNaN(flab.eq(2).text().trim()) ? parseInt(flab.eq(2).text().trim()) : null,
            gunBoat: !isNaN(flab.eq(3).text().trim()) ? parseInt(flab.eq(3).text().trim()) : null,
            torpedoGunBoat: !isNaN(flab.eq(4).text().trim()) ? parseInt(flab.eq(4).text().trim()) : null,
            subChaser: !isNaN(flab.eq(5).text().trim()) ? parseInt(flab.eq(5).text().trim()) : null,
            destroyer: !isNaN(flab.eq(6).text().trim()) ? parseInt(flab.eq(6).text().trim()) : null,
            ferryBarge: !isNaN(flab.eq(7).text().trim()) ? parseInt(flab.eq(7).text().trim()) : null
          },
          playTime: {
            total: flab.eq(8).text().trim() !== "N/A" ? flab.eq(8).text().trim() : null,
            ship: flab.eq(9).text().trim() !== "N/A" ? flab.eq(9).text().trim() : null,
            torpedoBoat: flab.eq(10).text().trim() !== "N/A" ? flab.eq(10).text().trim() : null,
            gunBoat: flab.eq(11).text().trim() !== "N/A" ? flab.eq(11).text().trim() : null,
            torpedoGunBoat: flab.eq(12).text().trim() !== "N/A" ? flab.eq(12).text().trim() : null,
            subChaser: flab.eq(13).text().trim() !== "N/A" ? flab.eq(13).text().trim() : null,
            destroyer: flab.eq(14).text().trim() !== "N/A" ? flab.eq(14).text().trim() : null,
            ferryBarge: flab.eq(15).text().trim() !== "N/A" ? flab.eq(15).text().trim() : null
          },
          targetsDestroyed: {
            total: !isNaN(flab.eq(16).text().trim()) ? parseInt(flab.eq(16).text().trim()) : null,
            air: !isNaN(flab.eq(17).text().trim()) ? parseInt(flab.eq(17).text().trim()) : null,
            ground: !isNaN(flab.eq(18).text().trim()) ? parseInt(flab.eq(18).text().trim()) : null,
            naval: !isNaN(flab.eq(19).text().trim()) ? parseInt(flab.eq(19).text().trim()) : null
          }
        }
      },
      realistic: {
        victories: !isNaN(parseInt(rbdata.eq(1).text().trim())) ? parseInt(rbdata.eq(1).text().trim()) : null,
        completed: !isNaN(
          parseInt(rbdata.eq(2)
            .text()
            .trim()
            .replace(/,/g, "")
          )
        ) ? parseInt(rbdata.eq(2)
            .text()
            .trim()
            .replace(/,/g, "")
          ) : null,
        ratio: rbdata.eq(3).text().trim(),
        deaths: !isNaN(
          parseInt(rbdata.eq(4)
            .text()
            .trim()
            .replace(/,/g, "")
          )
        ) ? parseInt(rbdata.eq(4)
            .text()
            .trim()
            .replace(/,/g, "")
          ) : null,
        lionsEarned: !isNaN(
          parseInt(rbdata.eq(5)
            .text()
            .trim()
            .replace(/,/g, "")
          )
        ) ? parseInt(rbdata.eq(5)
            .text()
            .trim()
            .replace(/,/g, "")
          ) : null,
        playTime: rbdata.eq(6).text().trim(),
        airTargetsDestroyed: !isNaN(parseInt(rbdata.eq(7).text().trim())) ? parseInt(rbdata.eq(7).text().trim()) : null,
        groundTargetsDestroyed: !isNaN(parseInt(rbdata.eq(8).text().trim())) ? parseInt(rbdata.eq(8).text().trim()) : null,
        navalTargetsDestroyed: !isNaN(parseInt(rbdata.eq(9).text().trim())) ? parseInt(rbdata.eq(9).text().trim()) : null,
        aviation: {
          battles: {
            total: !isNaN(avrb.eq(0).text().trim()) ? parseInt(avrb.eq(0).text().trim()) : null,
            fighter: !isNaN(avrb.eq(1).text().trim()) ? parseInt(avrb.eq(1).text().trim()) : null,
            bomber: !isNaN(avrb.eq(2).text().trim()) ? parseInt(avrb.eq(2).text().trim()) : null,
            attacker: !isNaN(avrb.eq(3).text().trim()) ? parseInt(avrb.eq(3).text().trim()) : null
          },
          playTime: {
            total: avrb.eq(4).text().trim() !== "N/A" ? avrb.eq(4).text().trim() : null,
            fighter: avrb.eq(5).text().trim() !== "N/A" ? avrb.eq(5).text().trim() : null,
            bomber: avrb.eq(6).text().trim() !== "N/A" ? avrb.eq(6).text().trim() : null,
            attacker: avrb.eq(7).text().trim() !== "N/A" ? avrb.eq(7).text().trim() : null
          },
          targetsDestroyed: {
            total: !isNaN(avrb.eq(8).text().trim()) ? parseInt(avrb.eq(8).text().trim()) : null,
            air: !isNaN(avrb.eq(9).text().trim()) ? parseInt(avrb.eq(9).text().trim()) : null,
            ground: !isNaN(avrb.eq(10).text().trim()) ? parseInt(avrb.eq(10).text().trim()) : null,
            naval: !isNaN(avrb.eq(11).text().trim()) ? parseInt(avrb.eq(11).text().trim()) : null
          }
        },
        ground: {
          battles: {
            total: !isNaN(grrb.eq(0).text().trim()) ? parseInt(grrb.eq(0).text().trim()) : null,
            tank: !isNaN(grrb.eq(1).text().trim()) ? parseInt(grrb.eq(1).text().trim()) : null,
            spg: !isNaN(grrb.eq(2).text().trim()) ? parseInt(grrb.eq(2).text().trim()) : null,
            heavy: !isNaN(grrb.eq(3).text().trim()) ? parseInt(grrb.eq(3).text().trim()) : null,
            spaa: !isNaN(grrb.eq(4).text().trim()) ? parseInt(grrb.eq(4).text().trim()) : null
          },
          playTime: {
            total: grrb.eq(5).text().trim() !== "N/A" ? grrb.eq(5).text().trim() : null,
            tank: grrb.eq(6).text().trim() !== "N/A" ? grrb.eq(6).text().trim() : null,
            spg: grrb.eq(7).text().trim() !== "N/A" ? grrb.eq(7).text().trim() : null,
            heavy: grrb.eq(8).text().trim() !== "N/A" ? grrb.eq(8).text().trim() : null,
            spaa: grrb.eq(9).text().trim() !== "N/A" ? grrb.eq(9).text().trim() : null
          },
          targetsDestroyed: {
            total: !isNaN(grrb.eq(10).text().trim()) ? parseInt(grrb.eq(10).text().trim()) : null,
            air: !isNaN(grrb.eq(11).text().trim()) ? parseInt(grrb.eq(11).text().trim()) : null,
            ground: !isNaN(grrb.eq(12).text().trim()) ? parseInt(grrb.eq(12).text().trim()) : null,
            naval: !isNaN(grrb.eq(13).text().trim()) ? parseInt(grrb.eq(13).text().trim()) : null
          }
        },
        fleet: {
          battles: {
            total: !isNaN(flrb.eq(0).text().trim()) ? parseInt(flrb.eq(0).text().trim()) : null,
            ship: !isNaN(flrb.eq(1).text().trim()) ? parseInt(flrb.eq(1).text().trim()) : null,
            torpedoBoat: !isNaN(flrb.eq(2).text().trim()) ? parseInt(flrb.eq(2).text().trim()) : null,
            gunBoat: !isNaN(flrb.eq(3).text().trim()) ? parseInt(flrb.eq(3).text().trim()) : null,
            torpedoGunBoat: !isNaN(flrb.eq(4).text().trim()) ? parseInt(flrb.eq(4).text().trim()) : null,
            subChaser: !isNaN(flrb.eq(5).text().trim()) ? parseInt(flrb.eq(5).text().trim()) : null,
            destroyer: !isNaN(flrb.eq(6).text().trim()) ? parseInt(flrb.eq(6).text().trim()) : null,
            ferryBarge: !isNaN(flrb.eq(7).text().trim()) ? parseInt(flrb.eq(7).text().trim()) : null
          },
          playTime: {
            total: flrb.eq(8).text().trim() !== "N/A" ? flrb.eq(8).text().trim() : null,
            ship: flrb.eq(9).text().trim() !== "N/A" ? flrb.eq(9).text().trim() : null,
            torpedoBoat: flrb.eq(10).text().trim() !== "N/A" ? flrb.eq(10).text().trim() : null,
            gunBoat: flrb.eq(11).text().trim() !== "N/A" ? flrb.eq(11).text().trim() : null,
            torpedoGunBoat: flrb.eq(12).text().trim() !== "N/A" ? flrb.eq(12).text().trim() : null,
            subChaser: flrb.eq(13).text().trim() !== "N/A" ? flrb.eq(13).text().trim() : null,
            destroyer: flrb.eq(14).text().trim() !== "N/A" ? flrb.eq(14).text().trim() : null,
            ferryBarge: flrb.eq(15).text().trim() !== "N/A" ? flrb.eq(15).text().trim() : null
          },
          targetsDestroyed: {
            total: !isNaN(flrb.eq(16).text().trim()) ? parseInt(flrb.eq(16).text().trim()) : null,
            air: !isNaN(flrb.eq(17).text().trim()) ? parseInt(flrb.eq(17).text().trim()) : null,
            ground: !isNaN(flrb.eq(18).text().trim()) ? parseInt(flrb.eq(18).text().trim()) : null,
            naval: !isNaN(flrb.eq(19).text().trim()) ? parseInt(flrb.eq(19).text().trim()) : null
          }
        }
      },
      simulator: {
        victories: !isNaN(parseInt(sbdata.eq(1).text().trim())) ? parseInt(sbdata.eq(1).text().trim()) : null,
        completed: !isNaN(
          parseInt(sbdata.eq(2)
            .text()
            .trim()
            .replace(/,/g, "")
          )
        ) ? parseInt(sbdata.eq(2)
            .text()
            .trim()
            .replace(/,/g, "")
          ) : null,
        ratio: sbdata.eq(3).text().trim(),
        deaths: !isNaN(
          parseInt(sbdata.eq(4)
            .text()
            .trim()
            .replace(/,/g, "")
          )
        ) ? parseInt(sbdata.eq(4)
            .text()
            .trim()
            .replace(/,/g, "")
          ) : null,
        lionsEarned: !isNaN(
          parseInt(sbdata.eq(5)
            .text()
            .trim()
            .replace(/,/g, "")
          )
        ) ? parseInt(sbdata.eq(5)
            .text()
            .trim()
            .replace(/,/g, "")
          ) : null,
        playTime: sbdata.eq(6).text().trim(),
        airTargetsDestroyed: !isNaN(parseInt(sbdata.eq(7).text().trim())) ? parseInt(sbdata.eq(7).text().trim()) : null,
        groundTargetsDestroyed: !isNaN(parseInt(sbdata.eq(8).text().trim())) ? parseInt(sbdata.eq(8).text().trim()) : null,
        navalTargetsDestroyed: !isNaN(parseInt(sbdata.eq(9).text().trim())) ? parseInt(sbdata.eq(9).text().trim()) : null,
        aviation: {
          battles: {
            total: !isNaN(avsb.eq(0).text().trim()) ? parseInt(avsb.eq(0).text().trim()) : null,
            fighter: !isNaN(avsb.eq(1).text().trim()) ? parseInt(avsb.eq(1).text().trim()) : null,
            bomber: !isNaN(avsb.eq(2).text().trim()) ? parseInt(avsb.eq(2).text().trim()) : null,
            attacker: !isNaN(avsb.eq(3).text().trim()) ? parseInt(avsb.eq(3).text().trim()) : null
          },
          playTime: {
            total: avsb.eq(4).text().trim() !== "N/A" ? avsb.eq(4).text().trim() : null,
            fighter: avsb.eq(5).text().trim() !== "N/A" ? avsb.eq(5).text().trim() : null,
            bomber: avsb.eq(6).text().trim() !== "N/A" ? avsb.eq(6).text().trim() : null,
            attacker: avsb.eq(7).text().trim() !== "N/A" ? avsb.eq(7).text().trim() : null
          },
          targetsDestroyed: {
            total: !isNaN(avsb.eq(8).text().trim()) ? parseInt(avsb.eq(8).text().trim()) : null,
            air: !isNaN(avsb.eq(9).text().trim()) ? parseInt(avsb.eq(9).text().trim()) : null,
            ground: !isNaN(avsb.eq(10).text().trim()) ? parseInt(avsb.eq(10).text().trim()) : null,
            naval: !isNaN(avsb.eq(11).text().trim()) ? parseInt(avsb.eq(11).text().trim()) : null
          }
        },
        ground: {
          battles: {
            total: !isNaN(grsb.eq(0).text().trim()) ? parseInt(grsb.eq(0).text().trim()) : null,
            tank: !isNaN(grsb.eq(1).text().trim()) ? parseInt(grsb.eq(1).text().trim()) : null,
            spg: !isNaN(grsb.eq(2).text().trim()) ? parseInt(grsb.eq(2).text().trim()) : null,
            heavy: !isNaN(grsb.eq(3).text().trim()) ? parseInt(grsb.eq(3).text().trim()) : null,
            spaa: !isNaN(grsb.eq(4).text().trim()) ? parseInt(grsb.eq(4).text().trim()) : null
          },
          playTime: {
            total: grsb.eq(5).text().trim() !== "N/A" ? grsb.eq(5).text().trim() : null,
            tank: grsb.eq(6).text().trim() !== "N/A" ? grsb.eq(6).text().trim() : null,
            spg: grsb.eq(7).text().trim() !== "N/A" ? grsb.eq(7).text().trim() : null,
            heavy: grsb.eq(8).text().trim() !== "N/A" ? grsb.eq(8).text().trim() : null,
            spaa: grsb.eq(9).text().trim() !== "N/A" ? grsb.eq(9).text().trim() : null
          },
          targetsDestroyed: {
            total: !isNaN(grsb.eq(10).text().trim()) ? parseInt(grsb.eq(10).text().trim()) : null,
            air: !isNaN(grsb.eq(11).text().trim()) ? parseInt(grsb.eq(11).text().trim()) : null,
            ground: !isNaN(grsb.eq(12).text().trim()) ? parseInt(grsb.eq(12).text().trim()) : null,
            naval: !isNaN(grsb.eq(13).text().trim()) ? parseInt(grsb.eq(13).text().trim()) : null
          }
        },
        fleet: {
          battles: {
            total: !isNaN(flsb.eq(0).text().trim()) ? parseInt(flsb.eq(0).text().trim()) : null,
            ship: !isNaN(flsb.eq(1).text().trim()) ? parseInt(flsb.eq(1).text().trim()) : null,
            torpedoBoat: !isNaN(flsb.eq(2).text().trim()) ? parseInt(flsb.eq(2).text().trim()) : null,
            gunBoat: !isNaN(flsb.eq(3).text().trim()) ? parseInt(flsb.eq(3).text().trim()) : null,
            torpedoGunBoat: !isNaN(flsb.eq(4).text().trim()) ? parseInt(flsb.eq(4).text().trim()) : null,
            subChaser: !isNaN(flsb.eq(5).text().trim()) ? parseInt(flsb.eq(5).text().trim()) : null,
            destroyer: !isNaN(flsb.eq(6).text().trim()) ? parseInt(flsb.eq(6).text().trim()) : null,
            ferryBarge: !isNaN(flsb.eq(7).text().trim()) ? parseInt(flsb.eq(7).text().trim()) : null
          },
          playTime: {
            total: flsb.eq(8).text().trim() !== "N/A" ? flsb.eq(8).text().trim() : null,
            ship: flsb.eq(9).text().trim() !== "N/A" ? flsb.eq(9).text().trim() : null,
            torpedoBoat: flsb.eq(10).text().trim() !== "N/A" ? flsb.eq(10).text().trim() : null,
            gunBoat: flsb.eq(11).text().trim() !== "N/A" ? flsb.eq(11).text().trim() : null,
            torpedoGunBoat: flsb.eq(12).text().trim() !== "N/A" ? flsb.eq(12).text().trim() : null,
            subChaser: flsb.eq(13).text().trim() !== "N/A" ? flsb.eq(13).text().trim() : null,
            destroyer: flsb.eq(14).text().trim() !== "N/A" ? flsb.eq(14).text().trim() : null,
            ferryBarge: flsb.eq(15).text().trim() !== "N/A" ? flsb.eq(15).text().trim() : null
          },
          targetsDestroyed: {
            total: !isNaN(flsb.eq(16).text().trim()) ? parseInt(flsb.eq(16).text().trim()) : null,
            air: !isNaN(flsb.eq(17).text().trim()) ? parseInt(flsb.eq(17).text().trim()) : null,
            ground: !isNaN(flsb.eq(18).text().trim()) ? parseInt(flsb.eq(18).text().trim()) : null,
            naval: !isNaN(flsb.eq(19).text().trim()) ? parseInt(flsb.eq(19).text().trim()) : null
          }
        }
      }
    };

    return {
      profile,
      stats
    };
  }

  parseClan() {
    const $ = cheerio.load(this.html);

    const nameArr = $(".username").text().trim().split(" "),
      tag = nameArr.shift().replace("[", "").replace("]", "").trim(),
      name = nameArr.join(" ");
    if (!name.length) throw new TypeError("NOT_FOUND", "clan");
    const _info = $(".clan-info > tbody > tr:nth-child(1) > td > div").children();
    const stats = $(".clan-info > tbody > tr:not(:first-child)").slice(2);
    const members = $(".clan-members > tbody:nth-child(1) > tr").slice(2);

    const arr = [];
    members.each((index, element) => {
      /**
       * Represents info about a squadron member
       * @typedef {Object} ClanMember
       * @property {string} name The in-game name of the squadron member
       * @property {ClanRating} rating The rating for the squadron member, per difficulty
       * @property {string} role The squadron member's role
       * @property {string} entry The date of entry for the squadron member
       */
      const obj = {
        name: $(element).children().eq(1).text().trim(),
        /**
         * Represents information about the member's personal squadron rating
         * @typedef {Object} ClanRating
         * @property {number} arcade The member's rating for Squadron Arcade Events
         * @property {number} realistic The member's rating for Squadron Realistic Events
         * @property {number} simulator The member's rating for Squadron Simulator Events
         */
        rating: {
          arcade: parseInt($(element)
            .children()
            .eq(2)
            .text()
            .trim()),
          realistic: parseInt($(element)
            .children()
            .eq(3)
            .text()
            .trim()),
          simulator: parseInt($(element)
            .children()
            .eq(4)
            .text()
            .trim())
        },
        role: $(element).children().eq(5).text().trim(),
        entry: $(element).children().eq(6).text().trim()
      };
      arr.push(obj);
    });

    /**
     * Represents raw squadron data
     * @typedef {Object} ClanData
     * @property {string} name The squadron name
     * @property {string} image The URL to the squadron's in-game picture
     * @property {number} players The amount of players in the squadron
     * @property {string} description The squadron's description
     * @property {string} created The date of creation for the squadron
     * @property {ClanStats} stats The statistics for the squadron per difficulty for SQB
     * @property {ClanMember[]} members Info for each squadron member
     */
    const obj = {
      name,
      tag,
      image: `http:${_info.eq(0).attr("src")}`,
      players: parseInt(_info.eq(2).text().replace("Number of players:", "").trim()),
      description: _info.eq(3).text().trim(),
      created: _info.eq(4).text().replace("date of creation:", "").trim(),
      /**
       * Represents the statistics for the squadron per difficulty for SRE
       * @typedef {Object} ClanStats
       * @property {SQBStats} arcade The info for the Arcade gamemode
       * @property {SQBStats} realistic The info for the Realistic gamemode
       * @property {SQBStats} simulator The info for the Simulator gamemode
       */
      stats: {
        /**
         * Represents statistics for a Squadron Event per difficulty
         * @typedef {Object} SQBStats
         * @property {number} airTargetsDestroyed The total amount of air targets destroyed
         * @property {number} groundTargetsDestroyed The total amount of ground targets destroyed
         * @property {number} deaths The total amount of deaths
         * @property {string} battleTime The total amount of battle time
         */
        arcade: {
          airTargetsDestroyed: parseInt(stats.eq(0).children().eq(1).text().trim()),
          groundTargetsDestroyed: parseInt(stats.eq(0).children().eq(2).text().trim()),
          deaths: parseInt(stats.eq(0).children().eq(3).text().trim()),
          battleTime: stats.eq(0).children().eq(4).text()
        },
        realistic: {
          airTargetsDestroyed: parseInt(stats.eq(2).children().eq(1).text().trim()),
          groundTargetsDestroyed: parseInt(stats.eq(2).children().eq(2).text().trim()),
          deaths: parseInt(stats.eq(2).children().eq(3).text().trim()),
          battleTime: stats.eq(2).children().eq(4).text()
        },
        simulator: {
          airTargetsDestroyed: parseInt(stats.eq(4).children().eq(1).text().trim()),
          groundTargetsDestroyed: parseInt(stats.eq(4).children().eq(2).text().trim()),
          deaths: parseInt(stats.eq(4).children().eq(3).text().trim()),
          battleTime: stats.eq(4).children().eq(4).text()
        }
      },
      members: arr
    };
    return obj;
  }

  parseNews() {
    const $ = cheerio.load(this.html);

    const elements = $(".news_item");

    const data = [];
    elements.each((index, element) => {
      const item_ = $(element);
      /**
       * Represents a War Thunder news item
       * @typedef {Object} NewsInfo
       * @property {string} url The URL to the news/changelog page
       * @property {string} title The title of the news/update
       * @property {string} date The date the news was announced/the update was released
       */
      const item = {
        url: item_.children().eq(1).children().eq(0).attr("href"),
        title: item_.children().eq(1).children().eq(0).text()
          .split("  ")
          .join(" "),
        // eslint-disable-next-line max-len
        date: item_.children().eq(0).text()
      };
      data.push(item);
    });

    return data;
  }

  parseWiki() {
    const $ = cheerio.load(this.html);

    if ($(".mw-search-nonefound").length) throw new Error("NOT_FOUND", "wiki");

    const results = $(".mw-search-results").eq(0);

    const data = [];
    results.children().each((index, element) => { // eslint-disable-line consistent-return
      const result = $(element);
      const child = result.children().eq(0);
      /**
       * Represents a War Thunder Wiki search result
       * @typedef {Object} wiki
       * @property {string} title The title of the search result's page
       * @property {string} url The URL to the search result's page
       * @property {Boolean} main If there is a page named as the search query on the Wiki
       */
      const obj = {
        title: child.text().trim(),
        url: `${URLs.wiki}${child.children().eq(0).attr("href")}`,
        main: false
      };
      if (data[0] && data[0].title === obj.title) return true;
      data.push(obj);
    });
    return data;
  }
}

module.exports = HTMLParser;