// dtp-app.js
// Copyright (C) 2022 DTP Technologies, LLC
// License: Apache-2.0

'use strict';

import { Upload } from 'upload';

import DtpLog from './dtp-log';
import DtpSocket from './dtp-socket';
import DtpDisplayEngine from './dtp-display-engine';

export default class DtpApp {

  constructor (component, user) {
    this.user = user;
    this.log = new DtpLog(component);

    this.log.debug('constructor', 'creating DisplayEngine instance');
    this.displayEngine = new DtpDisplayEngine();
  }

  async connect (options) {
    try {
      this.log.debug('connect', 'creating WebSocket interface');
      this.socket = new DtpSocket();

      this.log.debug('connect', 'connecting WebSocket platform');
      await this.socket.connect(options);

      this.log.info('connect', 'DTP framework online');
    } catch (error) {
      this.log.error('connect', 'failed to connect', { error });
      UIkit.modal.alert(`Failed to connect to server: ${error.message}`);
    }
  }

  async showForm (event, formUrl) {
    event.preventDefault();
    event.stopPropagation();
    try {
      const response = await fetch(formUrl, { method: 'GET' });
      const html = await response.text();
      this.currentModal = UIkit.modal.dialog(html);
    } catch (error) {
      UIkit.modal.alert(`Failed to display form: ${error.message}`);
    }
    return true;
  }

  async submitForm (event, userAction) {
    event.preventDefault();
    event.stopPropagation();

    try {
      const formElement = event.currentTarget || event.target;
      const form = new FormData(formElement);

      // include the submitter if we have one and it presents all required data
      if (event.submitter && event.submitter.name && event.submitter.value) {
        form.append(event.submitter.name, event.submitter.value);
      }

      this.log.info('submitForm', userAction, { event, action: formElement.action });
      const response = await fetch(formElement.action, {
        method: formElement.method,
        body: form,
      });

      if (!response.ok) {
        let json;
        try {
          json = await response.json();
        } catch (error) {
          throw new Error('Server error');
        }
        throw new Error(json.message || 'Server error');
      }

      await this.processResponse(response);
    } catch (error) {
      UIkit.modal.alert(`Failed to ${userAction}: ${error.message}`);
    } finally {
      if (this.currentModal) {
        this.currentModal.hide();
        delete this.currentModal;
      }
    }

    return;
  }

  async submitFormWithProgress (event) {
    event.preventDefault();
    event.stopPropagation();

    const formElement = event.currentTarget || event.target;
    const form = new FormData(formElement);

    const progressDialog = document.querySelector(formElement.getAttribute('data-progress-dialog') || '#upload-progress-dialog');
    const progressBar = document.querySelector(formElement.getAttribute('data-progress-element') || 'progress#upload-progress');
    const progressPrompt = document.querySelector(formElement.getAttribute('data-progress-prompt') || '#upload-progress-prompt');

    const upload = new Upload({
      url: formElement.getAttribute('action'),
      form,
    });

    upload.on('progress', (progress) => {
      this.log.info('submitSemitism', 'upload progress', { progress });
      progressBar.value = Math.round(progress * 100.0);
    });

    progressDialog.removeAttribute('hidden');
    const response = await upload.upload();

    progressPrompt.textContent = 'Upload complete...';
    this.log.info('submitSemitism', 'upload response', { response });

    const json = JSON.parse(response.data);
    await this.processResponseJSON(json);

    return true;
  }

  async processResponse (response) {
    const json = await response.json();
    return this.processResponseJSON(json);
  }

  async processResponseJSON (json) {
    if (!json.success) {
      this.log.error('processResponse', json.message);
      throw new Error(json.message);
    }
    if (json.displayList) {
      this.displayEngine.executeDisplayList(json.displayList);
    }
  }

  async validateTextElement (element, elementName, options) {
    const DEFAULT_MAX_CHARS = 1000;
    const DEFAULT_OPTIONS = {
      maxLength: DEFAULT_MAX_CHARS,
      minLength: 1,
      warnLengths: [80, 150],
      feedback: {
        textEmpty: `The ${elementName} can't be empty`,
        textTooShort: "Keep going...",
        textValid: "You're off to a great start!",
        textLengthWarn: [
          "This is getting a little crazy",
          `Only ${Math.max(0, options.maxLength - element.value.length)} characters left`,
        ],
        textMaxLength: `The ${elementName} is as long as it can be.`,
        textTooLong: `${elementName} is too long by ${element.value.length - options.maxLength} characters`,
      }
    };
    options = Object.assign(DEFAULT_OPTIONS, options);

    if (options.prompt) {
      options.prompt.textContent = `${element.value.length} of ${options.maxLength} max`;
    }

    let isValid = true;

    if (element.value.length === 0 || element.value.length > options.maxLength) {
      element.classList.remove('uk-form-success');
      element.classList.remove('uk-form-warning');
      element.classList.add('uk-form-danger');
      isValid = false;
    } else if (element.value.length > options.warnLengths[1]) {
      element.classList.remove('uk-form-success');
      element.classList.remove('uk-form-danger');
      element.classList.add('uk-form-warning');
    } else {
      element.classList.remove('uk-form-danger');
      element.classList.remove('uk-form-warning');
      element.classList.remove('uk-form-success');
    }

    if (element.value.length === 0) {
      window.dtp.submit.prompt.textContent = options.feedback.textEmpty;
      isValid = false;
    } else if (options.minLength && (element.value.length < options.minLength)) {
      window.dtp.submit.prompt.textContent = options.feedback.textTooShort;
      isValid = false;
    } else if (element.value.length < options.warnLengths[0]) {
      window.dtp.submit.prompt.textContent = options.feedback.textValid;
    } else if (element.value.length < options.warnLengths[1]) {
      window.dtp.submit.prompt.textContent = options.feedback.textLengthWarn[0];
    } else if (element.value.length < options.maxLength) {
      window.dtp.submit.prompt.textContent = options.feedback.textLengthWarn[1];
      window.dtp.submit.prompt.classList.add('uk-text-bold');
    } else if (element.value.length === options.maxLength) {
      window.dtp.submit.prompt.textContent = options.feedback.textMaxLength;
      window.dtp.submit.prompt.classList.add('uk-text-bold');
    } else {
      window.dtp.submit.prompt.textContent = options.feedback.textTooLong;
      window.dtp.submit.prompt.classList.add('uk-text-bold');
      isValid = false;
    }

    return isValid;
  }
}