import * as React from 'react';
import classNames from 'classnames/bind';
import TextItem from 'components/items/TextItem';
import DateItem from 'components/items/DateItem';
import FormItemLabel from 'components/items/FormItemLabel';

import find from 'lodash/find';
import get from 'lodash/get';
import filter from 'lodash/filter';

import { config } from 'config/general';
import qs from 'query-string';
import { Form } from 'react-final-form';

import { NavLink, Link } from 'react-router-dom';
import ModuleButton from 'components/items/ModuleButton';
import { getTextFromObject, getValueFromObject } from 'store/utils/helper';
import NumberFormat from 'react-number-format';
import { options } from 'config/root';
import CellRowForm from 'components/forms/CellRowForm/CellRowForm';
import { getPageInfo } from 'store/utils/adminHelpers';
import unflatten from 'unflatten';

import styleIdentifiers from './genericValue.scss';

const styles = classNames.bind(styleIdentifiers);
const DAYS = ['Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi', 'Dimanche'];

export interface GenericValueProps {
  value: any;
  item: object;
  apiPayload?: object;
  loadApi?: Function;
  apiDataKey?: string;
  dataKey: string;
  data?: object;
  dialogShow: Function;
}

interface GenericValueState {
  localeValue: string;
  localeObj: object | null;
}

export default class GenericValue extends React.Component<GenericValueProps, GenericValueState> {
  constructor(props: GenericValueProps) {
    super(props);

    const { lg } = this.props;

    const firstDiff = find(config.languages, it => {
      const l = it.short || it.value;
      return l !== lg;
    });

    this.state = {
      localeValue: '',
      localeObj: null,
      displayAllLanguages: false,
      mainLanguage: lg,
      currentLg: firstDiff ? firstDiff.short || firstDiff.value : lg,
    };
  }

  componentDidMount() {
    this.loadLinkedData();
  }

  loadLinkedData = () => {
    const { value, apiPayload, loadApi, apiDataKey } = this.props;

    // special wrapper to load linked data
    // apiPayload : payload data
    // apiDataKey : path in the response used for display
    // Load Api : load One element
    // value : value of the field (usually an id)
    if (apiPayload && value && loadApi && apiDataKey) {
      console.log('LOAD LINKED DATA', apiPayload);
      const payload = {
        noLoading: true,
        noFeedback: true,
        noReset: true,
        ...apiPayload,
        id: value,
        callback: result => {
          const val = get(result, apiDataKey);
          if (val) {
            this.setState({
              localeObj: result,
              localeValue: val,
            });
          }
        },
      };
      loadApi(payload);
    }
  };

  preview = (value: string) => {
    const { dialogShow } = this.props;

    dialogShow({
      id: 'preview',
      img: value,
      large: true,
    });
  };

  renderGoodLink = (path: string): void => {
    const {
      match: { params },
    } = this.props;
    const newPath = [];
    const arr = path.split('/');
    arr.map(item => {
      if (item.charAt(0) === ':') {
        const value = get(params, item.substr(1));
        newPath.push(value);
      } else {
        newPath.push(item);
      }
      return newPath;
    });

    return newPath.join('/');
  };

  // Rendering of the text
  renderText = (value, item?: object) => {
    const { localeObj, localeValue } = this.state;
    const { lg, content, itemsValue } = this.props;

    if (localeObj && localeValue) return localeValue;

    // All other case
    if (item.modelValues) {
      let itemValue = value;
      if (itemsValue && typeof value === 'string') {
        itemValue = find(itemsValue, it => {
          const val = getValueFromObject(it, item.modelValues, item.valueKey);
          return val === value;
        });
      }
      const text = getTextFromObject(itemValue, item.modelValues, content, lg);

      if (text) return <TextItem path={text} />;
    }
    if (item && item.list) {
      return <TextItem list={item.list} item={value} />;
    }
    if (item && item.values) {
      return <TextItem path={item.values} item={value} />;
    }

    if (typeof value === 'string' && value.indexOf('.') >= 0 && !item.noTextItem)
      return <TextItem path={value} />;
    if (typeof value === 'string') return value;

    if (item.transform) {
      return <TextItem path={item.transform(value)} />;
    }

    return <TextItem path={value} />;
  };

  getLink = (item, id) => {
    const { transformedData } = this.props;
    let resource = item.resource;
    if (item.dynamic) {
      resource = transformedData[resource];
    }

    let link = `/${config.resourcePath}/${resource}`;

    if (item.resourceType !== 'listing') {
      if (id) link += `/${id}/${item.resourceType || 'detail'}`;
    }
    if (item.searchParameters) {
      link += `?${qs.stringify(item.searchParameters)}`;
    }
    return link;
  };

  renderValue = (item: object, value) => {
    const { data, activeLgs } = this.props;

    const { displayAllLanguages, mainLanguage, currentLg } = this.state;
    const type = item.type;

    if (value === null || value === undefined || (value && value.length === 0) || value === '') {
      return <span>-</span>;
    }
    if (type === 'link') {
      const id = data && get(data, item.link || item.id || item.name);
      let link;
      if (item.externalLink) link = item.externalLink + id;
      else link = this.getLink(item, id);
      return (
        <NavLink to={link}>
          <div className={styles('link')}>{this.renderText(value, item)}</div>
        </NavLink>
      );
    }
    if (type === 'filestack') {
      const newItem = { ...item };
      const mimetype = value.mimetype;

      if (mimetype && mimetype.indexOf('image') >= 0) {
        newItem.type = 'image';
      } else if (mimetype && mimetype.indexOf('video') >= 0) {
        newItem.type = 'video';
      } else {
        newItem.type = 'file';
      }
      return this.renderValue(newItem, value);
    }
    if (
      type === 'input' ||
      type === 'string' ||
      type === 'value' ||
      type === 'link' ||
      type === 'textarea' ||
      type === 'text'
    ) {
      return this.renderText(value, item);
    }
    if (type === 'number') {
      return (
        <NumberFormat
          value={value}
          suffix={item.suffix || ''}
          decimalSeparator={item.decimalSeparator || '.'}
          decimalScale={item.decimalScale || 2}
          thousandSeparator={item.thousandSeparator || ' '}
          displayType="text"
        />
      );
    }
    if (type === 'code') {
      return (
        <div className={styles('code')}>
          <TextItem item={value} isHtml />
        </div>
      );
    }
    if (type === 'object' || type === 'json' || type === 'data') {
      return <div className={styles('object')}>{JSON.stringify(value, undefined, 4)}</div>;
    }
    if (type === 'select') {
      if (!item.multiple) {
        let linkId = '';
        if (item.modelValues) {
          linkId = get(value, item.modelValues.value);
        }
        if (!linkId) {
          linkId = value[item.resourceKey] || value[config.idKey] || value;
        }
        let link;
        if (item.externalLink) link = item.externalLink + linkId;
        else link = this.getLink(item, linkId);
        if (item.resource) {
          return (
            <div className={styles('select-item')}>
              <NavLink className={styles('link')} to={link}>
                {this.renderText(value, item)}
              </NavLink>
            </div>
          );
        }
        return <div className={styles('select-item')}>{this.renderText(value, item)}</div>;
      }
      if (item.return || item.resource) {
        // Array of links
        return value.map((it, index) => {
          let linkId = '';
          if (item.modelValues) {
            linkId = get(it, item.modelValues.value);
          }
          if (!linkId) {
            linkId = it[item.resourceKey] || it[config.idKey] || it;
          }

          const link = this.getLink(item, linkId);
          if (item.resource) {
            return (
              <div className={styles('select-item')} key={index}>
                <NavLink className={styles('link')} to={link} key={index}>
                  {this.renderText(it, item)}
                </NavLink>
              </div>
            );
          }
          return (
            <div className={styles('select-item')} key={index}>
              {this.renderText(it, item)}
            </div>
          );
        });
      }
      return value.map((it, index) => (
        <span className={styles('select-item')} key={index}>
          {it && index > 0 && ', '}
          {this.renderText(it, item)}
        </span>
      ));
    }
    if (type === 'geoloc') {
      if (!value.formattedAddress) return <span>error</span>;
      return <span>{value.formattedAddress}</span>;
    }
    if (type === 'price') {
      return <span>{value}</span>;
    }
    if (type === 'bool') {
      return <span>{value ? 'Yes' : 'No'}</span>;
    }
    if (type === 'switch') {
      return <span>{value ? 'Yes' : 'No'}</span>;
    }
    if (type === 'invertedBool') {
      return <span>{value ? 'No' : 'Yes'}</span>;
    }
    if (type === 'wysiwyg') {
      return <TextItem path={value} isHtml />;
    }
    if (type === 'date') {
      return <DateItem date={value} format={item.format} />;
    }
    if (type === 'datetime') {
      let format = item.dateFormat;
      if (item.timeFormat) format += ` ${item.timeFormat}`;
      return <DateItem date={value} format={format} />;
    }
    if (type === 'picture' || type === 'image' || (type === 'multiPictures' && item.oneFile)) {
      const previewUrl = value[item.displayValue] || value;
      return (
        <div className={styles('picture')} onClick={() => this.preview(previewUrl)}>
          <img src={previewUrl} alt="" />
        </div>
      );
    }
    if (type === 'multiPictures') {
      return (
        <div className={styles('pictures')}>
          {value?.map((it, index) => {
            const previewUrl = it[item.displayValue || 'large'];
            return (
              <div
                className={styles('picture')}
                key={index}
                onClick={() => this.preview(previewUrl)}
              >
                <img src={previewUrl} alt={it.title} />
              </div>
            );
          })}
        </div>
      );
    }
    if (type === 'tags') {
      return (
        <div>
          {value.map((el, key) => (
            <span key={key}>
              {key !== 0 && ', '}
              {`${el}`}
            </span>
          ))}
        </div>
      );
    }
    if (type === 'video') {
      return (
        <div className={styles('video')}>
          {/* eslint-disable-next-line */}
          <video width="100%" controls>
            <source src={(value && value[item.displayValue]) || value || ''} />
          </video>
        </div>
      );
    }
    if (type === 'file' || (type === 'multiFiles' && item.oneFile)) {
      const link = (value && (value[item.displayValue] || value.url)) || value;
      return (
        <a href={link} target="_blank" rel="noopener noreferrer">
          <div className={styles('file')}>
            {this.renderText(
              value[item.displayName] || value.filename || value.name || 'Open file',
              item,
            )}
          </div>
        </a>
      );
    }
    if (type === 'multiFiles') {
      return (
        <div className={styles('files')}>
          {value?.map((it, index) => {
            const link = it[item.displayValue || 'large'];
            return (
              <a href={link} key={index} target="_blank" rel="noopener noreferrer">
                <div className={styles('file')}>
                  {this.renderText(
                    value[item.displayName] || it.filename || it.name || 'Open file',
                    item,
                  )}
                </div>
              </a>
            );
          })}
        </div>
      );
    }
    if (type === 'schedule') {
      const week = Array.isArray(value) ? value : Object.values(value);
      return (
        <div className={styles('schedule')}>
          {week.map((day, index) => (
            <div className={styles('day')} key={index}>
              <span className={styles('day-label')}>{DAYS[index]}</span>
              {day?.map &&
                day.map((hour, indexHour) => (
                  <span className={styles('hour')} key={indexHour}>
                    {`${hour[0]}-${hour[1]}`}
                  </span>
                ))}
            </div>
          ))}
        </div>
      );
    }
    if (type === 'button') {
      const path = this.renderGoodLink(item.path);
      return (
        <Link to={path || '/'}>
          <ModuleButton label={item.buttonLabel || 'button'} />
        </Link>
      );
    }
    if (type === 'color') {
      return (
        <div className={styles('color')} style={{ backgroundColor: value }}>
          <TextItem path={value} />
        </div>
      );
    }

    /**
     * Multilingual component
     */

    const numLg = config.languages.length;

    let languages =
      numLg <= config.maxNumLanguagesDisplayed
        ? config.languages
        : filter(config.languages, lg => {
            const val = lg.short || lg.value;
            return [mainLanguage, currentLg].indexOf(val) >= 0;
          });

    if (displayAllLanguages) languages = config.languages;

    if (activeLgs) languages = languages?.filter(e => activeLgs.includes(e.value || e.short));

    if (type === 'multi' || type === 'multiarea') {
      return (
        <div className={styles('multi')}>
          {languages.map((lg, index) => (
            <div key={lg.value || lg.short || index} className={styles('language-wrapper')}>
              <div className={styles('language-label', type)}>{lg.value || lg.short}</div>
              <div className={styles('language-value')}>{value[lg.value || lg.short]}</div>
            </div>
          ))}
          {this.renderMoreLanguages(numLg)}
        </div>
      );
    }
    if (type === 'multicode') {
      return (
        <div className={styles('multi')}>
          {languages.map((lg, index) => {
            const text = value[lg.value || lg.short] || '';
            return (
              <div key={lg.value || lg.short || index} className={styles('language-wrapper')}>
                <div className={styles('language-label', type)}>{lg.value || lg.short}</div>
                <div className={styles('language-value', 'code')}>
                  <TextItem item={text} isHtml />
                </div>
              </div>
            );
          })}
          {this.renderMoreLanguages(numLg)}
        </div>
      );
    }
    if (type === 'multiwysiwyg') {
      return (
        <div className={styles('multi')}>
          {languages.map((lg, index) => {
            const text = value[lg.value || lg.short] || '';
            return (
              <div key={lg.value || lg.short || index} className={styles('language-wrapper')}>
                <div className={styles('language-label', type)}>{lg.value || lg.short}</div>
                <div className={styles('language-value', 'code')}>
                  <TextItem item={text} isHtml />
                </div>
              </div>
            );
          })}
          {this.renderMoreLanguages(numLg)}
        </div>
      );
    }

    return false;
  };

  renderMoreLanguages = numLg => {
    const { displayAllLanguages } = this.state;

    if (numLg <= config.maxNumLanguagesDisplayed) return false;

    return (
      <div
        className={styles('more')}
        onClick={() =>
          this.setState({
            displayAllLanguages: !displayAllLanguages,
          })
        }
      >
        <TextItem path={displayAllLanguages ? 'Show less' : 'Show more'} />
      </div>
    );
  };

  handleSubmit = values => {
    const { update, loadApollo, data, updateApollo, load } = this.props;

    const pageInfo = getPageInfo(this.props, options);

    if (typeof pageInfo === 'string') return;

    const {
      id,
      type,
      resourceOptions,
      parentType,
      parentId,
      modelOptions,
      listData,
      subData,
    } = pageInfo;

    const toSend = (modelOptions.treatData && modelOptions.treatData(values)) || values;
    // Check if subresource
    const subResource = listData || subData;
    // parent or current resource id
    const resourceId = id;
    const resourceType = type;

    const inputs = {};
    // modelOptions if the current model
    if (modelOptions.parent) {
      inputs[modelOptions.parent] = id;
    }

    if (modelOptions.apollo) {
      updateApollo({
        options: modelOptions,
        // ID of the list data (data[config.idKey] or id the parent (id)
        id: data[config.idKey] || id,
        data: toSend,
        // For list data or subdata
        inputs,
        noLoading: true,
        // Avoid to reset the parent detail
        subResource,
        success: modelOptions.successUpdate,
        callback: (): void => {
          this.setState({
            loading: false,
          });
          // If subresource => reload parent
          if (subResource) {
            // reload
            loadApollo({
              id: resourceId,
              options: resourceOptions,
            });
          }
        },
        callbackError: (): void => {
          this.setState({
            loading: false,
          });
        },
      });
    } else {
      update({
        resourceId,
        resourceType,
        parentType,
        parentId,
        noLoading: true,
        success: modelOptions.successUpdate,
        data: toSend,
        callback: (): void => {
          load({
            resourceType,
            parentType,
            parentId,
            resourceId,
            callback: () => {
              this.setState({
                loading: false,
              });
            },
            callbackError: (): void => {
              this.setState({
                loading: false,
              });
            },
          });
        },
      });
    }

    this.setState({ editState: false, loading: true });
  };

  canUpdate = () => {
    const { item, noUpdate } = this.props;
    const pageInfo = getPageInfo(this.props, options);

    if (config.noFieldUpdate) return false;

    if (typeof pageInfo === 'string') return false;

    const { modelOptions } = pageInfo;

    if (modelOptions.noUpdate) return false;

    if (noUpdate) return false;

    if (item.noUpdate) return false;

    if (item.type === 'text' || item.type === 'link') return false;

    return true;
  };

  handleUpdate = () => {
    if (!this.canUpdate()) return;

    this.setState({ editState: true });
  };

  render() {
    const { item, dataKey, value, noLabel } = this.props;

    const { editState, loading } = this.state;

    const inputName = item.name || dataKey;

    const init = unflatten({
      [inputName]: value,
    });
    const type = item.updateType || item.type;

    return (
      item && (
        <div
          className={styles(
            'GenericValue',
            (item.first || item.noMargin) && 'no-margin',
            this.canUpdate() && 'updatable',
          )}
          onDoubleClick={this.handleUpdate}
        >
          {!noLabel && <FormItemLabel {...this.props} label={item.label || dataKey} />}
          {loading && <div className={styles('value')}>...</div>}
          {!loading && <div className={styles('value')}>{this.renderValue(item, value)}</div>}
          {/* {editState && (
            <Form
              initialValues={init}
              smallMargin
              inputName={inputName}
              component={CellRowForm}
              onClickOutside={() => this.setState({ editState: false })}
              field={{
                ...item,
                name: inputName,
                // removing label
                label: type !== 'bool' ? null : item.label,
                type: item.updateType || item.type,
              }}
              onSubmit={val => this.handleSubmit(val)}
            />
          )} */}
        </div>
      )
    );
  }
}
