const { URLSearchParams } = require("url");
const { AuthorizationError, NotAuthorizedError } = require("./errors");
const { decodeWindows1251 } = require("./utils");
const axios = require("axios");

class PageProvider {
  constructor() {
    this.authorized = false;
    this.request = axios;
    this.cookie = null;
    this.host = "http://rutracker.org";
    this.loginUrl = `${this.host}/forum/login.php`;
    this.searchUrl = `${this.host}/forum/tracker.php`;
    this.threadUrl = `${this.host}/forum/viewtopic.php`;
    this.downloadUrl = `${this.host}/forum/dl.php`;
  }

  login(username, password) {
    const body = new URLSearchParams();

    body.append("login_username", username);
    body.append("login_password", password);
    body.append("login", "Вход");

    return this.request({
      url: this.loginUrl,
      method: "POST",
      data: body.toString(),
      maxRedirects: 0,
      validateStatus(status) {
        return status === 302;
      }
    })
      .then(response => {
        const [cookie] = response.headers["set-cookie"][0].split(";");
        this.cookie = cookie;
        this.authorized = true;

        return true;
      })
      .catch(() => {
        throw new AuthorizationError();
      });
  }

  search({ query }) {
    if (!this.authorized) {
      return Promise.reject(new NotAuthorizedError());
    }

    const url = `${this.searchUrl}?nm=${encodeURIComponent(query)}`;

    return this.request({
      url,
      method: "POST",
      responseType: "arraybuffer",
      headers: {
        Cookie: this.cookie
      }
    }).then(response => decodeWindows1251(response.data));
  }

  thread(id) {
    if (!this.authorized) {
      return Promise.reject(new NotAuthorizedError());
    }

    const url = `${this.threadUrl}?t=${encodeURIComponent(id)}`;

    return this.request({
      url,
      method: "GET",
      responseType: "arraybuffer",
      headers: {
        Cookie: this.cookie
      }
    }).then(response => decodeWindows1251(response.data));
  }

  torrentFile(id) {
    if (!this.authorized) {
      return Promise.reject(new NotAuthorizedError());
    }

    const url = `${this.downloadUrl}?t=${encodeURIComponent(id)}`;

    return this.request({
      url,
      method: "GET",
      responseType: "stream",
      headers: {
        Cookie: this.cookie
      }
    }).then(response => response.data);
  }
}

module.exports = PageProvider;

@@ -44,16 +44,18 @@
 %0Aconst %7B
+%0A 
  Authori
@@ -66,16 +66,18 @@
 onError,
+%0A 
  NotAuth
@@ -87,17 +87,36 @@
 zedError
- 
+,%0A  ValidationError%0A
 %7D = requ
@@ -589,16 +589,191 @@
 l.php%60;%0A
+    this.sortMapping = %7B%0A      registered: %221%22,%0A      title: %222%22,%0A      downloads: %224%22,%0A      size: %227%22,%0A      lastMessage: %228%22,%0A      seeds: %2210%22,%0A      leeches: %2211%22%0A    %7D;%0A
   %7D%0A%0A  l
@@ -1450,16 +1450,37 @@
 (%7B query
+, sort = %22registered%22
  %7D) %7B%0A  
@@ -1575,71 +1575,496 @@
     
-const url = %60$%7Bthis.searchUrl%7D?nm=$%7BencodeURIComponent(query)%7D%60
+if (!Object.prototype.hasOwnProperty.call(this.sortMapping, sort)) %7B%0A      const validSortFields = Object.keys(this.sortMapping);%0A      return Promise.reject(%0A        new ValidationError(%0A          %60Invalid sort property %22$%7Bsort%7D%22. Valid properties are $%7BvalidSortFields.join(%0A            %22, %22%0A          )%7D%60%0A        )%0A      );%0A    %7D%0A%0A    const url = %60$%7Bthis.searchUrl%7D?nm=$%7BencodeURIComponent(query)%7D%60;%0A    const body = new URLSearchParams();%0A%0A    body.append(%22o%22, this.sortMapping%5Bsort%5D)
 ;%0A%0A 
@@ -2091,32 +2091,61 @@
 st(%7B%0A      url,%0A
+      data: body.toString(),%0A
       method: %22P

Implement sort search query