import React from 'react'
import NestedPagePartial from '../nested-page-partial'
import Admin from '../../admin'
import truncate from 'lodash/truncate'
import capitalize from 'lodash/capitalize'
import sortBy from 'lodash/sortBy'
import { COLORS } from '../helpers/colors'

const truncateTo90 = (str) => truncate(str, { length: 93 });

const Instance = {

  fetchAdditionalNeededData(args) {
    let neededData = {};
    const fieldTypes = new Set(this.props.fields.map(f => f.fieldType));
    if (fieldTypes.has('content_download')) {
      neededData['contentDownloads'] = true;
      this.props.fetchEntities({
        url: '/admin/api/content_downloads'
      }).then(() => {
        let state = Instance.newStateAfterFetchingAddtionalData.call(this, { type: 'content_download', typeNamePlural: 'contentDownloads', addingEntity: args.addingEntity });
        this.setState(state);
        neededData['contentDownloads'] = false;
      });
    }
    if (fieldTypes.has('event_series')) {
      neededData['eventSeries'] = true;
      this.props.fetchEntities({
        url: '/admin/api/event_series'
      }).then(() => {
        let state = Instance.newStateAfterFetchingAddtionalData.call(this, { type: 'event_series', typeNamePlural: 'eventSeries', addingEntity: args.addingEntity });
        this.setState(state);
        neededData['eventSeries'] = false;
      });
    }
    if (fieldTypes.has('transcription')) {
      neededData['transcripts'] = true;
      this.props.fetchEntities({
        url: '/admin/api/transcripts'
      }).then(() => {
        let state = Instance.newStateAfterFetchingAddtionalData.call(this, { type: 'transcription', typeNamePlural: 'transcripts', addingEntity: args.addingEntity });
        this.setState(state);
        neededData['transcripts'] = false;
      });
    }
    if (Object.values(neededData).indexOf(true) > -1) {
      this.setState({
        fetching: true
      });
      let interval = setInterval(() => {
        if (Object.values(neededData).indexOf(true) === -1) {
          clearInterval(interval);
          this.setState({
            fetching: false
          }, () => {
            Admin.setUpNiceSelect({
              selector: 'select',
              func: Admin.changeField.bind(this, this.changeFieldArgs()),
              adjustWidthSelector: '.instance-fields .nice-select'
            });
          });
        }
      }, 250);
    } else {
      Admin.setUpNiceSelect({
        selector: 'select',
        func: Admin.changeField.bind(this, this.changeFieldArgs()),
        adjustWidthSelector: '.instance-fields .nice-select'
      });
    }
  },

  newStateAfterFetchingAddtionalData(args) {
    const { type, typeNamePlural, entityName } = args;
    let state = { [typeNamePlural]: this.props[typeNamePlural] };
    if (args.addingEntity) {
      let entity = this.state[entityName];
      this.state.fields.forEach((field) => {
        if (field.fieldType === type) {
          entity[field.id] = this.props[typeNamePlural][0].id;
        }
      });
      state[entityName] = entity;
    }
    return state;
  },

  renderFields(args) {
    return(
      <div className="instance-fields">
        { args.fields.map((field, index) => {
          return Instance.renderField.call(this, {
            field,
            index,
            entityName: args.entityName
          });
        }) }
      </div>
    );
  },

  renderField(args) {
    const field = args.field;
    switch (field.fieldType) {
      case 'text':
        return Instance.renderTextField.call(this, args);
      case 'markdown':
        return Instance.renderTextField.call(this, Object.assign(args, { markdown: true }));
      case 'number':
        return Instance.renderNumberField.call(this, args);
      case 'image':
        return Instance.renderImageField.call(this, args);
      case 'dropdown':
        return Instance.renderDropDownField.call(this, args);
      case 'color':
        return Instance.renderColorField.call(this, args);
      case 'content_download':
        return Instance.renderContentDownloadField.call(this, args);
      case 'event_series':
        return Instance.renderEventSeriesField.call(this, args);
      case 'transcription':
        return Instance.renderTranscriptField.call(this, args);
      case 'file':
        return Instance.renderFileField.call(this, args);
      case 'object':
        return Instance.renderObjectField.call(this, args);
      case 'header':
        return Instance.renderHeaderField.call(this, args);
      case 'webinar':
        return Instance.renderWebinarField.call(this, args);
      case 'page-partial':
        return Instance.renderPagePartialField.call(this, args);
      default:
        return null;
    }
  },

  renderTextField(args) {
    const { field, index, entityName, markdown } = args;
    const fieldWidth = Admin.calculateFieldWidth(field);
    const potentialErrors = [
      `${field.name} is mandatory`
    ];
    let newArgs = {
      field,
      fieldWidth,
      potentialErrors,
      entityName,
      markdown
    };
    return(
      <div key={ index } className="row">
        <div className="col-xs-12">
          <h2>{ field.name }</h2>
          { Instance.renderAnnotation.call(this, newArgs) }
          { field.charLimit < 1000 ? Instance.renderInput.call(this, newArgs) : Instance.renderTextArea.call(this, newArgs) }
          { Admin.renderFieldError(this.state.errors, potentialErrors) }
        </div>
      </div>
    );
  },

  renderNumberField(args) {
    const { field, index } = args;
    const potentialErrors = [
      `${capitalize(field.name)} is mandatory`,
      ` ${field.name} is not a number`
    ];
    return(
      <div key={ index } className="row">
        <div className="col-xs-12">
          <h2>{ field.name }</h2>
          { Instance.renderAnnotation.call(this, args) }
          <input
            className={ `textfield-100 ${Admin.errorClass(this.state.errors, potentialErrors)}` }
            onChange={ Admin.changeField.bind(this, this.changeFieldArgs()) }
            value={ this.state[args.entityName][field.id] }
            data-field={ field.id }
            data-fieldname={ field.name }
            type="text"
            maxLength={ 10 }
            />
          { Admin.renderFieldError(this.state.errors, potentialErrors) }
        </div>
      </div>
    );
  },

  renderImageField(args) {
    const { field, index, entityName } = args;
    const potentialErrors = [
      `${capitalize(field.name)} is mandatory`
    ];
    let image = this.state.images[field.id];
    let uploadId = this.state[args.entityName][field.id];
    return(
      <div key={ index } className="row">
        <div className="col-xs-12">
          <h2>{ field.name }</h2>
          { Instance.renderAnnotation.call(this, args) }
          <div className="vertically-align-contents">
            { Instance.renderImageOrEmptyDiv.call(this, image, potentialErrors) }
            { Instance.renderReuseImageButton.call(this, { field, image, uploadId }) }
          </div>
          <div className="buttons">
            <a className="change-upload" onClick={ Admin.clickUploadImage.bind(this, { entityName, property: field.id, field, createImageRecord: true, isInstance: true, imageCategory: 'PagePartial/Object' }, null) }>
              Upload Image
            </a>
            <a className="reusable-images" onClick={ Instance.clickReusableImages.bind(this, field.id) }>
              Reusable Images
            </a>
            <a className="remove-upload" onClick={ Instance.clickRemoveUpload.bind(this, { field, entityName: args.entityName, fieldType: 'image' }) }>
              Remove Image
            </a>
          </div>
          { Admin.renderFieldError(this.state.errors, potentialErrors) }
        </div>
      </div>
    );
  },

  renderImageOrEmptyDiv(image, potentialErrors) {
    if (image.url) {
      return(
        <img src={ image.url } />
      );
    } else {
      return(
        <div className={ `empty ${Admin.errorClass(this.state.errors, potentialErrors)}` }></div>
      );
    }
  },

  renderDropDownField(args) {
    const { field, index, entityName } = args;
    const potentialErrors = [
      `${capitalize(field.name)} is mandatory`
    ];
    return(
      <div key={ index } className="row">
        <div className="col-xs-12">
          <h2>{ field.name }</h2>
          { Instance.renderAnnotation.call(this, args) }
          <select onChange={ () => {} } data-field={ field.id } data-fieldname={ field.name } value={ this.state[entityName][field.id] }>
            { Instance.renderMenuOptions(field.menuOptions) }
          </select>
          { Admin.renderDropdownFieldError(this.state.errors, potentialErrors) }
        </div>
      </div>
    );
  },

  renderContentDownloadField(args) {
    const { field, index } = args;
    const potentialErrors = [
      `${capitalize(field.name)} is mandatory`
    ];
    return(
      <div key={ index } className="row">
        <div className="col-xs-12">
          <h2>{ field.name }</h2>
          { Instance.renderAnnotation.call(this, args) }
          <select onChange={ () => {} } data-field={ field.id } data-fieldname={ field.name } value={ this.state[args.entityName][field.id] }>
            { Instance.renderStateOptions(this.state.contentDownloads) }
          </select>
          { Admin.renderDropdownFieldError(this.state.errors, potentialErrors) }
        </div>
      </div>
    );
  },

  renderEventSeriesField(args) {
    const { field, index } = args;
    return(
      <div key={ index } className="row">
        <div className="col-xs-12">
          <h2>{ field.name }</h2>
          { Instance.renderAnnotation.call(this, args) }
          <select onChange={ () => {} } data-field={ field.id } data-fieldname={ field.name } value={ this.state[args.entityName][field.id] }>
            { Instance.renderStateOptions(this.state.eventSeries) }
          </select>
          { Admin.renderDropdownFieldError([], []) }
        </div>
      </div>
    );
  },

  renderTranscriptField(args) {
    const { field, index } = args;
    return(
      <div key={ index } className="row">
        <div className="col-xs-12">
          <h2>{ field.name }</h2>
          { Instance.renderAnnotation.call(this, args) }
          <select onChange={ () => {} } data-field={ field.id } data-fieldname={ field.name } value={ this.state[args.entityName][field.id] }>
            { Instance.renderStateOptions(this.state.transcripts) }
          </select>
          { Admin.renderDropdownFieldError([], []) }
        </div>
      </div>
    );
  },

  renderWebinarField(args) {
    const { field, index } = args;
    return(
      <div key={ index } className="row">
        <div className="col-xs-12">
          <h2>{ field.name }</h2>
          { Instance.renderAnnotation.call(this, args) }
          <select onChange={ () => {} } data-field={ field.id } data-fieldname={ field.name } value={ this.state[args.entityName][field.id] }>
            { Instance.renderStateOptions(this.state.webinars) }
          </select>
          { Admin.renderDropdownFieldError([], []) }
        </div>
      </div>
    );
  },

  renderHeaderField(args) {
    const { field, index, entityName } = args;
    const potentialErrors = [
      `${field.name} is mandatory`
    ];
    return(
      <div key={ index } className="row">
        <div className="col-xs-9">
          <h2>{ field.name }</h2>
          { Instance.renderAnnotation.call(this, args) }
          <input
            className={ `${Admin.errorClass(this.state.errors, potentialErrors)}` }
            onChange={ Admin.changeField.bind(this, this.changeFieldArgs()) }
            value={ this.state[entityName][field.id] }
            data-field={ field.id }
            data-fieldname={ field.name }
            type="text"
            maxLength={ field.charLimit }
            placeholder={ field.placeholder }
          />
          { Admin.renderFieldError([], []) }
        </div>
        <div className="col-xs-3">
          <h2>Header Type</h2>
          <select
            onChange={ () => {} }
            data-field={ field.id }
            data-fieldname={ field.name }
            data-headerstyle={ true }
            value={ this.state[entityName][`${field.id}_style`] }
          >
            { Instance.renderHeaderTypeOptions.call(this) }
          </select>
        </div>
      </div>
    );
  },

  renderColorField(args) {
    const { field, index } = args;
    return(
      <div key={ index } className="row">
        <div className="col-xs-12">
          <h2>{ field.name }</h2>
          { Instance.renderAnnotation.call(this, args) }
          <select onChange={ () => {} } data-field={ field.id } data-fieldname={ field.name } value={ this.state[args.entityName][field.id] }>
            { Instance.renderColors() }
          </select>
          { Admin.renderDropdownFieldError([], []) }
        </div>
      </div>
    );
  },

  renderFileField(args) {
    const { field, index } = args;
    const potentialErrors = [
      `${capitalize(field.name)} is mandatory`
    ];
    let file = this.state.files[field.id];
    return(
      <div key={ index } className="row">
        <div className="col-xs-12">
          <h2>{ field.name }</h2>
          { Instance.renderAnnotation.call(this, args) }
          <input readOnly className={ `textfield-1000 file-upload-field ${Admin.errorClass(this.state.errors, potentialErrors)}` } value={ file && file.url || "No File" } />
          <div className="buttons">
            <a className="change-upload" onClick={ Instance.clickUploadFile.bind(this, { entityName: args.entityName, property: field.id, field }) }>
              Upload File
            </a>
            <a className="remove-upload" onClick={ Instance.clickRemoveUpload.bind(this, { field, entityName: args.entityName, fieldType: 'file' }) }>
              Remove File
            </a>
          </div>
          { Admin.renderFieldError(this.state.errors, potentialErrors) }
        </div>
      </div>
    );
  },

  renderObjectField(args) {
    const { field, index } = args;
    const potentialErrors = [
      `${capitalize(field.name)} is mandatory`
    ];
    return(
      <div key={ index } className="row">
        <div className="col-xs-12">
          <h2>{ field.name }</h2>
          { Instance.renderAnnotation.call(this, args) }
          <select onChange={ () => {} } data-field={ field.id } data-fieldname={ field.name } value={ this.state[args.entityName][field.id] }>
            { Instance.renderObjectOptions.call(this, field) }
          </select>
          { Admin.renderDropdownFieldError(this.state.errors, potentialErrors) }
        </div>
      </div>
    );
  },

  renderPagePartialField(args) {
    const { field, index } = args;
    const potentialErrors = [
      `${capitalize(field.name)} is mandatory`
    ];
    if (this.state.changesToSave) {
      return(
        <div key={ index } className="row">
          <div className="col-xs-12">
            <h2>{ field.name }</h2>
            { Instance.renderAnnotation.call(this, args) }
            <p className="save-before-adding-nested">SAVE CHANGES TO ADD OR MODIFY NESTED PARTIAL</p>
            { Admin.renderFieldError(this.state.errors, potentialErrors) }
          </div>
        </div>
      );
    } else {
      const nestedPagePartialId = this.state.pagePartial[`${field.id}_page_partial_field_id`];
      const nestedPagePartial = nestedPagePartialId ? this.state.nestedPagePartials[nestedPagePartialId] : null;
      return(
        <div key={ index } className="row">
          <div className="col-xs-12">
            <h2>{ field.name }</h2>
            { Instance.renderAnnotation.call(this, args) }
            <NestedPagePartial
              pagePartial={ nestedPagePartial }
              partialFieldId={ field.id }
              addNestedPagePartial={ this.addNestedPagePartial.bind(this) }
              deleteNestedPagePartial={ this.deleteNestedPagePartial.bind(this) }
            />
            { Admin.renderFieldError(this.state.errors, potentialErrors) }
          </div>
        </div>
      );
    }
  },

  // Helper Methods:

  clickReusableImages(fieldId) {
    this.setState({
      reusableImagesModalOpen: true,
      reusableImagesFieldId: fieldId
    });
  },

  selectReusableImage(args) {
    const { uploadId, url, entityName } = args;
    let fieldId = this.state.reusableImagesFieldId;
    let entity = this.state[entityName];
    entity[fieldId] = uploadId;
    let images = this.state.images;
    let image = images[fieldId];
    let fieldName = this.state.fields.find(({ id }) => id === fieldId).name;
    Admin.removeDynamicFieldError(this.changeFieldArgs().errorsArray, fieldName);
    image['url'] = url;
    image['reusability'] = true;
    this.setState({
      [entityName]: entity,
      images
    }, () => {
      this.setState({
        changesToSave: this.checkForChanges()
      });
    });
  },

  clickRemoveUpload(args) {
    const { field, fieldType, entityName } = args;
    let entity = this.state[entityName];
    entity[field.id] = '';
    let uploads = this.state[`${fieldType}s`];
    uploads[field.id].url = '';
    uploads[field.id].reusability = null;
    this.setState({
      entityName: entity,
      [`${fieldType}s`]: uploads
    }, () => {
      if (this.checkForChanges) {
        this.setState({
          changesToSave: this.checkForChanges()
        });
      }
    });
  },

  clickUploadFile(args) {
    window.filepicker.pick({
      storeTo: {
        location: 's3'
      },
      customSourceContainer: 'cms-uploaded-files-bucket',
      services: ['COMPUTER'],
      debug: window.FILEPICKER_DEBUG
    }, (response) => {
      let entity = this.state[args.entityName];
      let files = this.state.files;
      files[args.property].url = response.url;
      this.setState({
        fetching: true
      });
      this.props.createEntity({
        directory: 'uploads',
        entityName: 'upload',
        entity: {
          uploadType: 'file',
          category: 'file',
          url: response.url,
          s3_url: response.key,
          uploadedBy: Admin.currentUser.id
        }
      }).then(() => {
        entity[args.property] = this.props.upload.id;
        let newArgs = Object.assign(args, { isInstance: true, isFile: true, entity, uploadsList: files });
        Admin.updateStateAfterImageOrFileUpload.call(this, newArgs);
      });
    });
  },

  renderInput(args) {
    const { field, fieldWidth, potentialErrors, entityName } = args;
    return(
      <div className="textarea-container">
        { Instance.renderMarkdownInfo.call(this, args) }
        <input
          className={ `textfield-${fieldWidth} ${Admin.errorClass(this.state.errors, potentialErrors)}` }
          onChange={ Admin.changeField.bind(this, this.changeFieldArgs()) }
          value={ this.state[entityName][field.id] }
          data-field={ field.id }
          data-fieldname={ field.name }
          type="text"
          maxLength={ field.charLimit }
          placeholder={ field.placeholder }
        />
      </div>
    );
  },

  renderTextArea(args) {
    const { field, fieldWidth, potentialErrors, entityName } = args;
    const rows = Math.ceil(fieldWidth / 2000);
    return(
      <div className="textarea-container">
        { Instance.renderMarkdownInfo.call(this, args) }
        <textarea
          className={ Admin.errorClass(this.state.errors, potentialErrors) }
          onChange={ Admin.changeField.bind(this, this.changeFieldArgs()) }
          value={ this.state[entityName][field.id] }
          data-field={ field.id }
          data-fieldname={ field.name }
          rows={ rows }
          >
        </textarea>
      </div>
    );
  },

  renderReuseImageButton(args) {
    let image = args.image;
    if (image.reusability) {
      return <p className="reused-image">Image is Reusable</p>;
    } else if (image.url) {
      return (
        <a className="make-reusable" onClick={ Admin.clickMakeReusable.bind(this, args) }>
          Make Reusable
        </a>
      );
    } else {
      return null;
    }
  },

  renderObjectOptions(field) {
    let options = this.state.instances[field.id];
    let alphabetizedOptions = sortBy(options, 'firstFieldValue');
    return alphabetizedOptions.map((option, index) => <option key={ index } value={ option.id }>{ truncateTo90(option.firstFieldValue) }</option>);
  },

  renderMenuOptions(options) {
    let alphabetizedOptions = sortBy(options, 'value');
    return alphabetizedOptions.map((option, index) => <option key={ index }>{ truncateTo90(option.value) }</option>);
  },

  renderStateOptions(obj) {
    return sortBy(obj, 'name')
      .map(({id, name}) => (<option key={ id } value={ id }>{ truncateTo90(name) }</option>))
  },

  renderColors() {
    return Object.keys(COLORS).map((color, index) => <option key={ index } value={ COLORS[color] }>{ color }</option>);
  },

  renderHeaderTypeOptions() {
    return this.state.headerTypes.map((type, i) => <option key={ i } value={ type.id }>{ type.name }</option>);
  },

  renderAnnotation(args) {
    if (args.field.description) {
      return(
        <p className="annotation">{ args.field.description }</p>
      );
    } else {
      return null;
    }
  },

  renderMarkdownInfo(args) {
    if (args.markdown) {
      return([
        <p key="1" className="markdown-notification">Accepts Markdown</p>,
        <img key="2" className="question-icon" src={ window.Images.questionIcon } onClick={ Admin.changeState.bind(this, 'markdownModalOpen', true) } />
      ]);
    } else {
      return null;
    }
  }
};

export default Instance;
