import phantom from 'phantom';

import ScraperNotifier from '../helpers/notifier';
import { waitForUrls, NAVIGATION_ERRORS } from '../helpers/navigation';
import { waitUntilElementFound, fillInput, clickButton } from '../helpers/elements-interactions';

const LOGIN_RESULT = {
  SUCCESS: 'success',
  INVALID_PASSWORD: 'invalidPassword',
  CHANGE_PASSWORD: 'changePassword',
};

const GENERAL_ERROR = 'generalError';

function handleLoginResult(loginResult, notifyAction) {
  switch (loginResult) {
    case LOGIN_RESULT.SUCCESS:
      notifyAction('login successful');
      return { success: true };
    case LOGIN_RESULT.INVALID_PASSWORD:
      notifyAction('invalid password');
      return {
        success: false,
        errorType: loginResult,
      };
    case LOGIN_RESULT.CHANGE_PASSWORD:
      notifyAction('need to change password');
      return {
        success: false,
        errorType: loginResult,
      };
    case NAVIGATION_ERRORS.TIMEOUT:
      notifyAction('timeout during login');
      return {
        success: false,
        errorType: loginResult,
      };
    case NAVIGATION_ERRORS.GENERIC:
      notifyAction('generic error during login');
      return {
        success: false,
        errorType: loginResult,
      };
    default:
      throw new Error(`unexpected login result "${loginResult}"`);
  }
}

async function analyzeLogin(page, possibleResults) {
  let loginResult;
  try {
    loginResult = await waitForUrls(page, possibleResults);
  } catch (e) {
    loginResult = e.timeout ? NAVIGATION_ERRORS.TIMEOUT : NAVIGATION_ERRORS.GENERIC;
  }

  return loginResult;
}

function createGeneralError() {
  return {
    success: false,
    errorType: GENERAL_ERROR,
  };
}

class BaseScraper {
  constructor(scraperName) {
    this.scraperName = scraperName || 'base';
  }

  async initialize(options) {
    this.options = options;
    this.notifier = new ScraperNotifier(this.scraperName);
    this.instance = await phantom.create();
    this.page = await this.instance.createPage();

    this.notify('start scraping');
  }

  async scrape(credentials, options = {}) {
    await this.initialize(options);

    const loginOptions = this.getLoginOptions(credentials);
    const loginResult = await this.login(loginOptions);

    let scrapeResult;
    if (loginResult.success) {
      scrapeResult = await this.fetchData();
    } else {
      scrapeResult = loginResult;
    }

    await this.instance.exit();

    return scrapeResult;
  }

  getLoginOptions() {
    this.notify('you must override getLoginOptions()');
  }

  async login(options) {
    if (!options) {
      return createGeneralError();
    }

    await this.page.open(options.loginUrl);
    await waitUntilElementFound(this.page, options.submitButtonId);

    await Promise.all(options.fields.map((field) => {
      return fillInput(this.page, field.id, field.value);
    }));

    await clickButton(this.page, options.submitButtonId);
    this.notify('logging in');

    if (options.postAction) {
      await options.postAction();
    }

    const loginResult = await analyzeLogin(this.page, options.possibleResults);
    return handleLoginResult(loginResult, msg => this.notify(msg));
  }

  async fetchData() {
    this.notify('you must override fetchData()');
  }

  notify(msg) {
    this.notifier.notify(this.options, msg);
  }
}

export { BaseScraper, LOGIN_RESULT };

@@ -929,254 +929,131 @@
     
-case NAVIGATION_ERRORS.TIMEOUT:%0A      notifyAction('timeout during login');%0A      return %7B%0A        success: false,%0A        errorType: loginResult,%0A      %7D;%0A    case NAVIGATION_ERRORS.GENERIC:%0A      notifyAction('generic error during login');%0A    
+default:%0A      throw new Error(%60unexpected login result %22$%7BloginResult%7D%22%60);%0A  %7D%0A%7D%0A%0Afunction createErrorResult(errorType) %7B%0A
   re
@@ -1051,36 +1051,32 @@
 e) %7B%0A  return %7B%0A
-    
     success: fal
@@ -1075,36 +1075,32 @@
 ess: false,%0A    
- 
-   
 errorType: login
@@ -1096,385 +1096,209 @@
 Type
-: loginResult,%0A      %7D;%0A    default:%0A      throw new Error(%60unexpected logi
+,%0A  %7D;%0A%7D%0A%0Afunction createTimeoutError() %7B%0A  retur
 n 
+c
 re
-sult %22$%7BloginResult%7D%22%60);%0A  %7D%0A%7D%0A%0Aasync function analyzeLogin(page, possibleResults) %7B%0A  let loginResult;%0A  try %7B%0A    loginResult = await waitForUrls(page, possibleResults);%0A  %7D catch (e) %7B%0A    loginResult = e.timeout ? NAVIGATION_ERRORS.TIMEOUT : NAVIGATION_ERRORS.GENERIC;%0A  %7D%0A%0A  return loginResult
+ateErrorResult(NAVIGATION_ERRORS.TIMEOUT);%0A%7D%0A%0Afunction createGenericNavigationError() %7B%0A  return createErrorResult(NAVIGATION_ERRORS.GENERAL_ERROR)
 ;%0A%7D%0A
@@ -1343,45 +1343,26 @@
 urn 
-%7B%0A    success: false,%0A    errorType: 
+createErrorResult(
 GENE
@@ -1370,21 +1370,17 @@
 AL_ERROR
-,%0A  %7D
+)
 ;%0A%7D%0A%0Acla
@@ -1869,25 +1869,53 @@
 tials);%0A
+%0A
     
-const
+let loginResult;%0A    try %7B%0A     
  loginRe
@@ -1948,24 +1948,135 @@
 ginOptions);
+%0A    %7D catch (e) %7B%0A      loginResult = e.timeout ? createTimeoutError() : createGenericNavigationError();%0A    %7D
 %0A%0A    let sc
@@ -2114,24 +2114,38 @@
 .success) %7B%0A
+      try %7B%0A  
       scrape
@@ -2173,24 +2173,142 @@
 etchData();%0A
+      %7D catch (e) %7B%0A        scrapeResult = e.timeout ? createTimeoutError() : createGenericNavigationError();%0A      %7D%0A
     %7D else %7B
@@ -3012,20 +3012,19 @@
 ait 
-analyzeLogin
+waitForUrls
 (thi

handle nav errors in all places