/*
 * Copyright (c) 2021 Snowplow Analytics Ltd, 2010 Anthon Pang
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

import util from 'util';
import F from 'lodash/fp';
import { fetchResults, start, stop, DockerWrapper } from '../micro';

const dumpLog = (log: Array<unknown>) => console.log(util.inspect(log, true, null, true));

const isMatchWithCallback = F.isMatchWith((lt, rt) => (F.isFunction(rt) ? rt(lt) : undefined));

const SAFARI_EXPECTED_FIRST_NAME = 'Alex';
const SAFARI_EXPECTED_MESSAGE = 'Changed message';

describe('Auto tracking', () => {
  if (F.isMatch({ browserName: 'internet explorer', version: '9' }, browser.capabilities)) {
    fit('Skip IE9', () => {}); // Automated tests for IE autotracking features
  }

  let log: Array<unknown> = [];
  let docker: DockerWrapper;

  const logContains = (ev: unknown) => F.some(isMatchWithCallback(ev as object), log);

  const loadUrlAndWait = (url: string) => {
    browser.url(url);
    browser.waitUntil(() => $('#init').getText() === 'true', {
      timeout: 5000,
      timeoutMsg: 'expected init after 5s',
      interval: 250,
    });
  };

  beforeAll(() => {
    browser.call(() => {
      return start().then((container) => {
        docker = container;
      });
    });
    browser.url('/index.html');
    browser.setCookies({ name: 'container', value: docker.url });
  });

  afterAll(() => {
    browser.call(() => {
      return stop(docker.container);
    });
  });

  it('should send a link click events', () => {
    loadUrlAndWait('/link-tracking.html');
    $('#link-to-click').click();

    loadUrlAndWait('/link-tracking.html?filter=exclude');
    $('#link-to-not-track').click();
    $('#link-to-click').click();

    loadUrlAndWait('/link-tracking.html?filter=filter');
    $('#link-to-filter').click();
    $('#link-to-click').click();

    loadUrlAndWait('/link-tracking.html?filter=include');
    $('#link-to-filter').click();
    $('#link-to-click').click();

    // time for activity to register and request to arrive
    browser.pause(5000);
    browser.call(() =>
      fetchResults(docker.url).then((result) => {
        log = result;
      })
    );
  });

  it('should send a link click event', () => {
    expect(
      logContains({
        event: {
          event: 'unstruct',
          app_id: 'autotracking',
          page_url: 'http://snowplow-js-tracker.local:8080/link-tracking.html',
          unstruct_event: {
            data: {
              schema: 'iglu:com.snowplowanalytics.snowplow/link_click/jsonschema/1-0-1',
              data: {
                targetUrl: 'http://snowplow-js-tracker.local:8080/link-tracking.html#click',
                elementId: 'link-to-click',
                elementClasses: ['example'],
                elementTarget: '_self',
              },
            },
          },
          contexts: {
            data: [
              {
                schema: 'iglu:org.schema/WebPage/jsonschema/1-0-0',
                data: { keywords: ['tester'] },
              },
            ],
          },
        },
      })
    ).toBe(true);
  });

  it('should not send a blocked link click event', () => {
    expect(
      logContains({
        event: {
          event: 'unstruct',
          app_id: 'autotracking',
          unstruct_event: {
            data: {
              schema: 'iglu:com.snowplowanalytics.snowplow/link_click/jsonschema/1-0-1',
              data: {
                elementId: 'link-to-not-track',
              },
            },
          },
        },
      })
    ).toBe(false);

    expect(
      logContains({
        event: {
          event: 'unstruct',
          app_id: 'autotracking',
          unstruct_event: {
            data: {
              schema: 'iglu:com.snowplowanalytics.snowplow/link_click/jsonschema/1-0-1',
              data: {
                targetUrl: 'http://snowplow-js-tracker.local:8080/link-tracking.html?filter=exclude#click',
                elementId: 'link-to-click',
                elementClasses: ['example'],
                elementContent: 'Click here',
                elementTarget: '_self',
              },
            },
          },
        },
      })
    ).toBe(true);
  });

  it('should not send a filtered link click event', () => {
    expect(
      logContains({
        event: {
          event: 'unstruct',
          app_id: 'autotracking',
          unstruct_event: {
            data: {
              schema: 'iglu:com.snowplowanalytics.snowplow/link_click/jsonschema/1-0-1',
              data: {
                elementId: 'link-to-filter',
              },
            },
          },
        },
      })
    ).toBe(false);

    expect(
      logContains({
        event: {
          event: 'unstruct',
          app_id: 'autotracking',
          unstruct_event: {
            data: {
              schema: 'iglu:com.snowplowanalytics.snowplow/link_click/jsonschema/1-0-1',
              data: {
                targetUrl: 'http://snowplow-js-tracker.local:8080/link-tracking.html?filter=filter#click',
                elementId: 'link-to-click',
                elementClasses: ['example'],
                elementContent: 'Click here',
                elementTarget: '_self',
              },
            },
          },
        },
      })
    ).toBe(true);
  });

  it('should not send a non-included link click event', () => {
    expect(
      logContains({
        event: {
          event: 'unstruct',
          app_id: 'autotracking',
          unstruct_event: {
            data: {
              schema: 'iglu:com.snowplowanalytics.snowplow/link_click/jsonschema/1-0-1',
              data: {
                elementId: 'link-to-filter',
              },
            },
          },
        },
      })
    ).toBe(false);

    expect(
      logContains({
        event: {
          event: 'unstruct',
          app_id: 'autotracking',
          unstruct_event: {
            data: {
              schema: 'iglu:com.snowplowanalytics.snowplow/link_click/jsonschema/1-0-1',
              data: {
                targetUrl: 'http://snowplow-js-tracker.local:8080/link-tracking.html?filter=include#click',
                elementId: 'link-to-click',
                elementClasses: ['example'],
                elementContent: 'Click here',
                elementTarget: '_self',
              },
            },
          },
        },
      })
    ).toBe(true);
  });

  it('should send form events', () => {
    loadUrlAndWait('/form-tracking.html');
    $('#fname').click();

    // Safari 12.1 doesn't fire onchange events when clearing
    // However some browsers don't support setValue
    if (F.isMatch({ browserName: 'Safari', browserVersion: '12.1.1' }, browser.capabilities)) {
      $('#fname').setValue(SAFARI_EXPECTED_FIRST_NAME);
    } else {
      $('#fname').clearValue();
    }

    $('#lname').click();

    browser.pause(250);

    $('#bike').click();

    browser.pause(250);

    $('#cars').click();
    $('#cars').selectByAttribute('value', 'saab');
    $('#cars').click();

    browser.pause(250);

    $('#message').click();

    // Safari 12.1 doesn't fire onchange events when clearing
    // However some browsers don't support setValue
    if (F.isMatch({ browserName: 'Safari', browserVersion: '12.1.1' }, browser.capabilities)) {
      $('#message').setValue(SAFARI_EXPECTED_MESSAGE);
    } else {
      $('#message').clearValue();
    }

    browser.pause(250);

    $('#terms').click();

    browser.pause(2000);

    $('#submit').click();

    browser.pause(1000);

    loadUrlAndWait('/form-tracking.html?filter=exclude');

    $('#fname').click();
    $('#lname').click();

    browser.pause(1000);

    loadUrlAndWait('/form-tracking.html?filter=include');

    $('#lname').click();

    browser.pause(1000);

    loadUrlAndWait('/form-tracking.html?filter=filter');

    $('#fname').click();
    $('#lname').click();

    browser.pause(1000);

    loadUrlAndWait('/form-tracking.html?filter=excludedForm');

    $('#excluded-fname').click();
    $('#excluded-submit').click();

    // time for activity to register and request to arrive
    browser.pause(2500);

    browser.call(() =>
      fetchResults(docker.url).then((result) => {
        log = result;
      })
    );
  });

  it('should send focus_form and change_form on text input', () => {
    // Safari 12.1 doesn't fire onchange events when clearing
    // However some browsers don't support setValue
    let expectedFirstName = '';
    if (F.isMatch({ browserName: 'Safari', browserVersion: '12.1.1' }, browser.capabilities)) {
      expectedFirstName = SAFARI_EXPECTED_FIRST_NAME;
    }

    expect(
      logContains({
        event: {
          event: 'unstruct',
          app_id: 'autotracking',
          unstruct_event: {
            data: {
              schema: 'iglu:com.snowplowanalytics.snowplow/focus_form/jsonschema/1-0-0',
              data: {
                formId: 'myForm',
                elementId: 'fname',
                nodeName: 'INPUT',
                elementType: 'text',
                elementClasses: ['test'],
                value: 'John',
              },
            },
          },
        },
      })
    ).toBe(true);

    expect(
      logContains({
        event: {
          event: 'unstruct',
          app_id: 'autotracking',
          unstruct_event: {
            data: {
              schema: 'iglu:com.snowplowanalytics.snowplow/change_form/jsonschema/1-0-0',
              data: {
                formId: 'myForm',
                elementId: 'fname',
                nodeName: 'INPUT',
                type: 'text',
                elementClasses: ['test'],
                value: expectedFirstName,
              },
            },
          },
        },
      })
    ).toBe(true);

    expect(
      logContains({
        event: {
          event: 'unstruct',
          app_id: 'autotracking',
          unstruct_event: {
            data: {
              schema: 'iglu:com.snowplowanalytics.snowplow/focus_form/jsonschema/1-0-0',
              data: {
                formId: 'myForm',
                elementId: 'lname',
                nodeName: 'INPUT',
                elementType: 'text',
                elementClasses: [],
                value: 'Doe',
              },
            },
          },
        },
      })
    ).toBe(true);
  });

  it('should send change_form on radio input', () => {
    expect(
      logContains({
        event: {
          event: 'unstruct',
          app_id: 'autotracking',
          unstruct_event: {
            data: {
              schema: 'iglu:com.snowplowanalytics.snowplow/change_form/jsonschema/1-0-0',
              data: {
                formId: 'myForm',
                elementId: 'vehicle',
                nodeName: 'INPUT',
                type: 'radio',
                elementClasses: [],
                value: 'Bike',
              },
            },
          },
        },
      })
    ).toBe(true);
  });

  it('should send focus_form and change_form on select change', () => {
    // MS Edge <= 18 doesn't fire change events on select elements via Edge WebDriver
    if (F.isMatch({ browserName: 'MicrosoftEdge', browserVersion: '25.10586.0.0' }, browser.capabilities)) {
      return;
    }

    expect(
      logContains({
        event: {
          event: 'unstruct',
          app_id: 'autotracking',
          unstruct_event: {
            data: {
              schema: 'iglu:com.snowplowanalytics.snowplow/focus_form/jsonschema/1-0-0',
              data: {
                formId: 'myForm',
                elementId: 'cars',
                nodeName: 'SELECT',
                elementClasses: [],
                value: 'volvo',
              },
            },
          },
        },
      })
    ).toBe(true);

    expect(
      logContains({
        event: {
          event: 'unstruct',
          app_id: 'autotracking',
          unstruct_event: {
            data: {
              schema: 'iglu:com.snowplowanalytics.snowplow/change_form/jsonschema/1-0-0',
              data: {
                formId: 'myForm',
                elementId: 'cars',
                nodeName: 'SELECT',
                elementClasses: [],
                value: 'saab',
              },
            },
          },
        },
      })
    ).toBe(true);
  });

  it('should send focus_form and change_form on textarea input', () => {
    // Safari 12.1 doesn't fire onchange events when clearing
    // However some browsers don't support setValue
    let expectedMessage = '';
    if (F.isMatch({ browserName: 'Safari', browserVersion: '12.1.1' }, browser.capabilities)) {
      expectedMessage = SAFARI_EXPECTED_MESSAGE;
    }

    expect(
      logContains({
        event: {
          event: 'unstruct',
          app_id: 'autotracking',
          unstruct_event: {
            data: {
              schema: 'iglu:com.snowplowanalytics.snowplow/focus_form/jsonschema/1-0-0',
              data: {
                formId: 'myForm',
                elementId: 'message',
                nodeName: 'TEXTAREA',
                elementClasses: [],
                value: 'This is a message',
              },
            },
          },
        },
      })
    ).toBe(true);

    expect(
      logContains({
        event: {
          event: 'unstruct',
          app_id: 'autotracking',
          unstruct_event: {
            data: {
              schema: 'iglu:com.snowplowanalytics.snowplow/change_form/jsonschema/1-0-0',
              data: {
                formId: 'myForm',
                elementId: 'message',
                nodeName: 'TEXTAREA',
                elementClasses: [],
                value: expectedMessage,
              },
            },
          },
        },
      })
    ).toBe(true);
  });

  it('should send change_form on checkbox', () => {
    expect(
      logContains({
        event: {
          event: 'unstruct',
          app_id: 'autotracking',
          unstruct_event: {
            data: {
              schema: 'iglu:com.snowplowanalytics.snowplow/change_form/jsonschema/1-0-0',
              data: {
                formId: 'myForm',
                elementId: 'terms',
                nodeName: 'INPUT',
                type: 'checkbox',
                elementClasses: [],
                value: 'agree',
              },
            },
          },
        },
      })
    ).toBe(true);
  });

  it('should send submit_form on form submission', () => {
    // Safari 12.1 doesn't fire onchange events when clearing
    // However some browsers don't support setValue
    let expectedFirstName = '',
      expectedMessage = '';
    if (F.isMatch({ browserName: 'Safari', browserVersion: '12.1.1' }, browser.capabilities)) {
      expectedFirstName = SAFARI_EXPECTED_FIRST_NAME;
      expectedMessage = SAFARI_EXPECTED_MESSAGE;
    }

    expect(
      logContains({
        event: {
          event: 'unstruct',
          app_id: 'autotracking',
          unstruct_event: {
            data: {
              schema: 'iglu:com.snowplowanalytics.snowplow/submit_form/jsonschema/1-0-0',
              data: {
                formId: 'myForm',
                formClasses: ['formy-mcformface'],
                elements: [
                  {
                    name: 'message',
                    value: expectedMessage,
                    nodeName: 'TEXTAREA',
                  },
                  {
                    name: 'fname',
                    value: expectedFirstName,
                    nodeName: 'INPUT',
                    type: 'text',
                  },
                  {
                    name: 'lname',
                    value: 'Doe',
                    nodeName: 'INPUT',
                    type: 'text',
                  },
                  {
                    name: 'vehicle',
                    value: 'Bike',
                    nodeName: 'INPUT',
                    type: 'radio',
                  },
                  {
                    name: 'terms',
                    value: 'agree',
                    nodeName: 'INPUT',
                    type: 'checkbox',
                  },
                  {
                    name: 'cars',
                    value: 'saab',
                    nodeName: 'SELECT',
                  },
                ],
              },
            },
          },
          contexts: {
            data: [
              {
                schema: 'iglu:org.schema/WebPage/jsonschema/1-0-0',
                data: { keywords: ['tester'] },
              },
            ],
          },
        },
      })
    ).toBe(true);
  });

  it('should not send focus_form on excluded element', () => {
    expect(
      logContains({
        event: {
          event: 'unstruct',
          app_id: 'autotracking',
          page_url: 'http://snowplow-js-tracker.local:8080/form-tracking.html?filter=exclude',
          unstruct_event: {
            data: {
              schema: 'iglu:com.snowplowanalytics.snowplow/focus_form/jsonschema/1-0-0',
              data: {
                elementId: 'fname',
              },
            },
          },
        },
      })
    ).toBe(false);

    expect(
      logContains({
        event: {
          event: 'unstruct',
          app_id: 'autotracking',
          page_url: 'http://snowplow-js-tracker.local:8080/form-tracking.html?filter=exclude',
          unstruct_event: {
            data: {
              schema: 'iglu:com.snowplowanalytics.snowplow/focus_form/jsonschema/1-0-0',
              data: {
                elementId: 'lname',
              },
            },
          },
        },
      })
    ).toBe(true);
  });

  it('should send focus_form on included element', () => {
    expect(
      logContains({
        event: {
          event: 'unstruct',
          app_id: 'autotracking',
          page_url: 'http://snowplow-js-tracker.local:8080/form-tracking.html?filter=include',
          unstruct_event: {
            data: {
              schema: 'iglu:com.snowplowanalytics.snowplow/focus_form/jsonschema/1-0-0',
              data: {
                elementId: 'lname',
              },
            },
          },
        },
      })
    ).toBe(true);
  });

  it('should send focus_form on element included by filter', () => {
    expect(
      logContains({
        event: {
          event: 'unstruct',
          app_id: 'autotracking',
          page_url: 'http://snowplow-js-tracker.local:8080/form-tracking.html?filter=filter',
          unstruct_event: {
            data: {
              schema: 'iglu:com.snowplowanalytics.snowplow/focus_form/jsonschema/1-0-0',
              data: {
                elementId: 'fname',
              },
            },
          },
        },
      })
    ).toBe(true);

    expect(
      logContains({
        event: {
          event: 'unstruct',
          app_id: 'autotracking',
          page_url: 'http://snowplow-js-tracker.local:8080/form-tracking.html?filter=filter',
          unstruct_event: {
            data: {
              schema: 'iglu:com.snowplowanalytics.snowplow/focus_form/jsonschema/1-0-0',
              data: {
                elementId: 'lname',
              },
            },
          },
        },
      })
    ).toBe(false);
  });

  it('should not send focus_form on elements', () => {
    expect(
      logContains({
        event: {
          event: 'unstruct',
          app_id: 'autotracking',
          unstruct_event: {
            data: {
              schema: 'iglu:com.snowplowanalytics.snowplow/focus_form/jsonschema/1-0-0',
              data: {
                elementId: 'excluded-fname',
              },
            },
          },
        },
      })
    ).toBe(false);
  });

  it('should not send submit_form', () => {
    expect(
      logContains({
        event: {
          event: 'unstruct',
          app_id: 'autotracking',
          unstruct_event: {
            data: {
              schema: 'iglu:com.snowplowanalytics.snowplow/submit_form/jsonschema/1-0-0',
              data: {
                formId: 'excludedForm',
                formClasses: ['excluded-form'],
              },
            },
          },
        },
      })
    ).toBe(false);
  });
});
