import { JSDOM } from 'jsdom';
import { RequestInit, Response } from 'node-fetch';
import { Errors, RenaiError } from '../../types/error';
import { fetch } from './web-crawler';

const domain = 'nhentai.net';
const url = `https://${domain}/`;

const paths = {
  books: 'g/',
  login: 'login/',
  favorites: 'favorites/',
};

const usernameInput = 'username_or_email';
const passwordInput = 'password';

interface ILoginMeta {
  [key: string]: string;
}

interface ILoginAuth {
  [usernameInput]: string;
  [passwordInput]: string;
}

interface ILoginParams extends ILoginMeta, ILoginAuth {}

function getNHentai(path: string): Promise<Document> {
  return fetch(`${url}${path}`)
    .then((res: Response) => {
      return res.text();
    })
    .then((text: string) => {
      const { document } = new JSDOM(text).window;
      return document;
    });
}

function postNHentai(path: string, requestInit: RequestInit = {}): Promise<Response> {
  const postUrl = `${url}${path}`;
  return fetch(postUrl, {
    ...requestInit,
    ...{
      headers: {
        ...requestInit.headers,
        ...{
          Host: domain,
          Referer: postUrl,
        },
      },
    },
    method: 'post',
  });
}

function getLoginMeta(): Promise<ILoginMeta> {
  return getNHentai(paths.login).then((document: Document) => {
    // tslint:disable-next-line: prefer-for-of
    for (let i = 0; i < document.forms.length; i++) {
      const form: HTMLFormElement = document.forms[i];
      const valueStore: ILoginMeta = {};
      let isLoginForm = false;
      // tslint:disable-next-line: prefer-for-of
      for (let j = 0; j < form.elements.length; j++) {
        const input = form.elements[j];
        const name = input.getAttribute('name');

        if (name === usernameInput || name === passwordInput) {
          isLoginForm = true;
        } else if (name) {
          valueStore[name] = input.getAttribute('value');
        }
      }
      if (isLoginForm) {
        return valueStore;
      }
    }
    return Promise.reject(new RenaiError(Errors.ENOLOGIN));
  });
}

export function isLoggedIn(): Promise<boolean> {
  return fetch(`${url}${paths.favorites}`, { redirect: 'manual' }).then((res: Response) => {
    return res.status === HttpCode.OK;
  });
}

export function login(name: string, password: string): Promise<void> {
  return getLoginMeta()
    .then((meta: ILoginMeta) => {
      const loginParams: ILoginParams = {
        ...meta,
        ...{
          // tslint:disable-next-line: object-literal-sort-keys
          username_or_email: name,
          password,
        },
      };

      return postNHentai(paths.login, {
        body: encodeURI(
          Object.keys(loginParams)
            .map((key: keyof ILoginParams) => `${key}=${loginParams[key]}`)
            .join('&')
        ),
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
        },
        redirect: 'manual',
      });
    })
    .then(() => {})
    .catch(() => Promise.reject(new RenaiError(Errors.ELOGINFAIL)));
}
