import { test } from 'ember-qunit';
import wait from 'ember-test-helpers/wait';
import hbs from 'htmlbars-inline-precompile';
import $ from 'jquery';
import moduleForComponent from 'web-client/tests/helpers/module-for-component';

import { FILE_SIZE_LIMIT_BYTES } from 'web-client/components/dashboards/subscription-dash/badges/upload-form/component';
import { BadgesPage } from 'web-client/tests/pages/dashboards/subscription-dash';

const WIDTH_REQUIREMENT = 5;
const HEIGHT_REQUIREMENT = 5;
const INPUT_NAME = 'test_input_name';
const MIME_TYPE = 'image/png';
// Base64-encoded PNG: RGB: 000000; opacity: 1; width: 5; height: 5:
const VALID_BASE64 = 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAQAAAAnZu5uAAAAEElEQVR42mNk+M8ABYwkMAGbQQUBEvGWBAAAAABJRU5ErkJggg';
// Badly base64-encoded PNG:
const INVALID_BASE64 = 'iVBORw0K+GgoAAAAN+SUhEUgAA==';

function newMockFile(dataURI, mimeString) {
  let byteCharacters = atob(dataURI);
  let byteNumbers = new Array(byteCharacters.length);

  for (let i = 0; i < byteCharacters.length; i++) {
    byteNumbers[i] = byteCharacters.charCodeAt(i);
  }

  let byteArray = new Uint8Array(byteNumbers); // eslint-disable-line no-undef

  return new Blob([byteArray], { type: mimeString });
}

function triggerFileInputEvent($input, file) {
  let event = $.Event('change', {
    target: {
      files: [file]
    }
  });

  $input.trigger(event);
}

moduleForComponent('dashboards/subscription-dash/badges/badge-image-picker', 'Integration | Component | dashboards/subscription-dash/badges/badge image picker', {
  integration: true,

  beforeEach() {
    this.set('fileSizeLimitBytes', FILE_SIZE_LIMIT_BYTES);
    this.set('imageHeightRequirement', HEIGHT_REQUIREMENT);
    this.set('imageWidthRequirement', WIDTH_REQUIREMENT);
    this.set('inputName', INPUT_NAME);
    this.set('updateFile', () => {});

    this.page = BadgesPage.create();
  }
});

test('it renders with the correct tags, classes, attributes, and caption', function (assert) {
  assert.expect(5);

  this.render(hbs`
    {{dashboards/subscription-dash/badges/badge-image-picker
      fileSizeLimitBytes=fileSizeLimitBytes
      imageHeightRequirement=imageHeightRequirement
      imageWidthRequirement=imageWidthRequirement
      inputName=inputName
      updateFile=updateFile}}
  `);

  assert.ok(this.$('label').hasClass('balloon-wrapper'), 'component <label> tag has the appropriate class');

  let $input = this.$(this.page.subBadgeImagePickerInput());

  assert.equal($input.attr('accept'), MIME_TYPE, '<input> accepts PNG');
  assert.equal($input.attr('name'), INPUT_NAME, '<input> "name" attribute is set to passed-in "inputName"');
  assert.ok($input.prop('required'), '<input> is required');

  assert.elementText(
    this.$('figcaption.sub-text'),
    `${WIDTH_REQUIREMENT} x ${HEIGHT_REQUIREMENT}px`,
    '<figcaption> has the passed-in image width and height requirements'
  );
});

test('it rejects a file over file size limit', function (assert) {
  assert.expect(9);

  let newFileSizeLimitBytes = 0;
  let expectedFileSizeKilobytes = Math.floor(newFileSizeLimitBytes / 1000);
  let overSizeLimitFile = newMockFile(VALID_BASE64, MIME_TYPE);

  this.set('fileSizeLimitBytes', newFileSizeLimitBytes);

  this.render(hbs`
    {{dashboards/subscription-dash/badges/badge-image-picker
      fileSizeLimitBytes=fileSizeLimitBytes
      imageHeightRequirement=imageHeightRequirement
      imageWidthRequirement=imageWidthRequirement
      inputName=inputName
      updateFile=updateFile}}
  `);

  assert.stringIncludes(
    this.$(this.page.subBadgeImagePicker()).text(),
    'Upload Image',
    'default paragraph text is "Upload Image" without a file'
  );

  assert.elementText(
    this.$(this.page.subBadgeImagePickerTooltip()),
    'Choose a PNG to upload',
    'default balloon text is "Choose a PNG to upload" without a file'
  );

  assert.elementCount(
    this.$(this.page.subBadgeImagePickerAlert()),
    0,
    'alert balloon is NOT rendered without an error'
  );

  return wait().then(() => {
    triggerFileInputEvent(this.$(this.page.subBadgeImagePickerInput()), overSizeLimitFile);

    return wait();
  }).then(() => {
    assert.stringIncludes(
      this.$(this.page.subBadgeImagePicker()).text(),
      'Upload Image',
      'default paragraph text is still "Upload Image" without a valid file'
    );

    let $alertBalloon = this.$(this.page.subBadgeImagePickerAlert());

    assert.ok($alertBalloon.length, 'alert balloon is rendered with an error');
    assert.ok($alertBalloon.hasClass('show'), 'alert balloon is shown');

    assert.elementText(
      $alertBalloon,
      `Max file size is ${expectedFileSizeKilobytes} KB`,
      'alert balloon errors on file size limit with passed-in limit in bytes converted to kilobytes'
    );

    assert.elementCount(
      this.$(this.page.subBadgeImagePickerTooltip()),
      0,
      'default balloon is NOT rendered when there is an error'
    );

    this.$().click();

    assert.elementCount(this.$(this.page.subBadgeImagePickerAlert()), 0, 'alert balloon is removed on click outside');

    return wait();
  });
});

test('it rejects a bad image file', function (assert) {
  assert.expect(9);

  let badFile = newMockFile(INVALID_BASE64, MIME_TYPE);

  this.render(hbs`
    {{dashboards/subscription-dash/badges/badge-image-picker
      fileSizeLimitBytes=fileSizeLimitBytes
      imageHeightRequirement=imageHeightRequirement
      imageWidthRequirement=imageWidthRequirement
      inputName=inputName
      updateFile=updateFile}}
  `);

  assert.stringIncludes(
    this.$(this.page.subBadgeImagePicker()).text(),
    'Upload Image',
    'default paragraph text is "Upload Image" without a file'
  );

  assert.elementText(
    this.$(this.page.subBadgeImagePickerTooltip()),
    'Choose a PNG to upload',
    'default balloon text is "Choose a PNG to upload" without a file'
  );

  assert.elementCount(
    this.$(this.page.subBadgeImagePickerAlert()),
    0,
    'alert balloon is NOT rendered without an error'
  );

  return wait().then(() => {
    triggerFileInputEvent(this.$(this.page.subBadgeImagePickerInput()), badFile);

    return wait();
  }).then(() => {
    let $alertBalloon = this.$(this.page.subBadgeImagePickerAlert());

    assert.stringIncludes(
      this.$(this.page.subBadgeImagePicker()).text(),
      'Upload Image',
      'default paragraph text is still "Upload Image" without a valid file'
    );

    assert.ok($alertBalloon.length, 'alert balloon is rendered with an error');
    assert.ok($alertBalloon.hasClass('show'), 'alert balloon is shown');
    assert.elementText($alertBalloon, 'Bad PNG file', 'alert balloon errors on bad PNG file');

    assert.elementCount(
      this.$(this.page.subBadgeImagePickerTooltip()),
      0,
      'default balloon is NOT rendered when there is an error'
    );

    this.$().click();

    assert.elementCount(this.$(this.page.subBadgeImagePickerAlert()), 0, 'alert balloon is removed on click outside');

    return wait();
  });
});

test('it rejects an image file not matching dimension requirements', function (assert) {
  assert.expect(9);

  let newHeighRequirement = HEIGHT_REQUIREMENT * 10;
  let invalidDimensionFile = newMockFile(VALID_BASE64, MIME_TYPE);

  this.set('imageHeightRequirement', newHeighRequirement);

  this.render(hbs`
    {{dashboards/subscription-dash/badges/badge-image-picker
      fileSizeLimitBytes=fileSizeLimitBytes
      imageHeightRequirement=imageHeightRequirement
      imageWidthRequirement=imageWidthRequirement
      inputName=inputName
      updateFile=updateFile}}
  `);

  assert.stringIncludes(
    this.$(this.page.subBadgeImagePicker()).text(),
    'Upload Image',
    'default paragraph text is "Upload Image" without a file'
  );

  assert.elementText(
    this.$(this.page.subBadgeImagePickerTooltip()),
    'Choose a PNG to upload',
    'default balloon text is "Choose a PNG to upload" without a file'
  );

  assert.notOk(this.$(this.page.subBadgeImagePickerAlert()).length, 'alert balloon is NOT rendered without an error');

  return wait().then(() => {
    triggerFileInputEvent(this.$(this.page.subBadgeImagePickerInput()), invalidDimensionFile);

    return wait();
  }).then(() => {
    let $alertBalloon = this.$(this.page.subBadgeImagePickerAlert());

    assert.stringIncludes(
      this.$(this.page.subBadgeImagePicker()).text(),
      'Upload Image',
      'default paragraph text is still "Upload Image" without a valid file'
    );

    assert.ok($alertBalloon.length, 'alert balloon is rendered with an error');
    assert.ok($alertBalloon.hasClass('show'), 'alert balloon is shown');

    assert.elementText(
      $alertBalloon,
      `PNG must be ${WIDTH_REQUIREMENT} x ${newHeighRequirement}px`,
      'alert balloon errors on incorrect PNG image width and height'
    );

    assert.elementCount(
      this.$(this.page.subBadgeImagePickerTooltip()),
      0,
      'default balloon is NOT rendered when there is an error'
    );

    this.$().click();

    assert.elementCount(this.$(this.page.subBadgeImagePickerAlert()), 0, 'alert balloon is removed on click outside');

    return wait();
  });
});

test('it loads a valid PNG image file', function (assert) {
  assert.expect(9);

  let validPNG = newMockFile(VALID_BASE64, MIME_TYPE);

  this.set('file', null);

  this.set('updateFile', (file) => {
    assert.ok(true, 'closure action "updateFile" is invoked');
    assert.equal(file.type, MIME_TYPE, 'file type is correct');

    this.set('file', 'some-file.png');
  });

  this.render(hbs`
    {{dashboards/subscription-dash/badges/badge-image-picker
      fileSizeLimitBytes=fileSizeLimitBytes
      imageHeightRequirement=imageHeightRequirement
      imageWidthRequirement=imageWidthRequirement
      inputName=inputName
      file=file
      updateFile=updateFile}}
  `);

  assert.stringIncludes(
    this.$(this.page.subBadgeImagePicker()).text(),
    'Upload Image',
    'default paragraph text is "Upload Image" without a file'
  );

  assert.elementText(
    this.$(this.page.subBadgeImagePickerTooltip()),
    'Choose a PNG to upload',
    'default balloon text is "Choose a PNG to upload" without a file'
  );

  assert.elementCount(
    this.$(this.page.subBadgeImagePickerAlert()),
    0,
    'alert balloon is NOT rendered without an error'
  );

  return wait().then(() => {
    triggerFileInputEvent(this.$(this.page.subBadgeImagePickerInput()), validPNG);

    return wait();
  }).then(() => {
    assert.notStringIncludes(
      this.$(this.page.subBadgeImagePicker()).text(),
      'Upload Image',
      'default "Upload Image" text is NOT rendered'
    );

    assert.elementCount(this.$(this.page.subBadgeImagePickerTooltip()), 0, 'balloon tooltip is NOT rendered');
    assert.elementCount(this.$(this.page.subBadgeImagePickerAlert()), 0, 'alert balloon is NOT rendered');

    assert.equal(
      this.$(this.page.subBadgeImagePickerImage()).attr('src'),
      `data:${MIME_TYPE};base64,${VALID_BASE64}==`,
      '<img> "src" attribute is set to the file content'
    );
  });
});

test('it resets file input when the passed-in "file" property is nullified', function (assert) {
  assert.expect(3);

  let validPNG = newMockFile(VALID_BASE64, MIME_TYPE);

  this.set('file', null);

  this.set('updateFile', () => {
    this.set('file', 'some-file.png');
  });

  this.render(hbs`
    {{dashboards/subscription-dash/badges/badge-image-picker
      fileSizeLimitBytes=fileSizeLimitBytes
      imageHeightRequirement=imageHeightRequirement
      imageWidthRequirement=imageWidthRequirement
      inputName=inputName
      file=file
      updateFile=updateFile}}
  `);

  assert.notOk(this.$(this.page.subBadgeImagePickerImage()).attr('src'), '<img> "src" attribute is NOT set');

  return wait().then(() => {
    triggerFileInputEvent(this.$(this.page.subBadgeImagePickerInput()), validPNG);

    return wait();
  }).then(() => {
    assert.ok(
      this.$(this.page.subBadgeImagePickerImage()).attr('src'),
      '<img> "src" attribute is set to the file content'
    );

    this.set('file', null);

    assert.notOk(
      this.$(this.page.subBadgeImagePickerImage()).attr('src'),
      '<img> "src" attribute is once again NOT set'
    );
  });
});
