import * as React from 'react';
import classNames from 'classnames/bind';

import { options, models, transformFunctions } from 'config/root';
import qs from 'query-string';
import { config } from 'config/general';
import GenericForm from 'components/forms/GenericForm';
import DetailLegend from 'components/items/DetailLegend';
import { getPageInfo, getPageTitle } from 'store/utils/adminHelpers';
import { clone } from 'store/utils/helper';
import { Form } from 'react-final-form';
import arrayMutators from 'final-form-arrays';
import isEmpty from 'lodash/isEmpty';
import styleIdentifiers from './manage.scss';

const styles = classNames.bind(styleIdentifiers);

export interface StateProps {
  detail: Record<string, any>;
}

export interface DispatchProps {
  dialogShow: Function;
  load: Function;
  update: Function;
  add: Function;
  reset: Function;
  deleteItem: Function;
}

export interface OwnProps {}

type ManageProps = StateProps & DispatchProps & OwnProps;

interface ManageState {
  historySent: true;
}

export default class Manage extends React.Component<ManageProps, ManageState> {
  state = {
    historySent: false,
  };

  componentDidMount() {
    this.handleLocation();
  }

  componentDidUpdate(prevProps) {
    const { location, detail } = this.props;
    if (prevProps.location !== location || prevProps.detail !== detail) {
      this.historyToSend(this.handleLocation);
    } else {
      this.handleLocation();
    }
  }

  historyToSend = after => {
    this.setState(
      {
        historySent: false,
      },
      after,
    );
  };

  getLocationTitle = (modelOptions, data, edit) => {
    const { content, lg } = this.props;

    const title = getPageTitle(modelOptions, data, {
      content,
      lg,
      type: edit ? 'update' : 'create',
    });

    return title;
  };

  handleLocation = () => {
    const { pushHistory, location } = this.props;

    const { historySent } = this.state;

    if (historySent) return;

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

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

    const {
      type,
      subData,
      listData,
      index,
      form,
      allParams,
      data,
      modelOptions,
      dataLoading,
    } = pageInfo;

    const isEdit = (data && data[config.idKey]) || !isEmpty(data);

    if (dataLoading) return;

    if (isEdit && isEmpty(data)) return;

    let name = this.getLocationTitle(modelOptions, data, isEdit);
    let historyId = `${type}_detail`;

    if (form || subData || listData) {
      name = this.getLocationTitle(modelOptions, data, isEdit);
      historyId += `_sublevel`;
    }
    if (listData && index) {
      name = this.getLocationTitle(modelOptions, data, isEdit);
      historyId += `_detail`;
    }

    pushHistory({
      pathname: location.pathname,
      search: location.search,
      params: allParams,
      id: historyId,
      name,
    });

    this.setState({
      historySent: true,
    });
  };

  /**
   * Customized form actions
   */
  submitAction = values => {
    const { reset, history, location, genericAction, load } = this.props;

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

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

    const { type, parentType, parentId, id, actionOptions } = pageInfo;

    const data = (actionOptions.treatData && actionOptions.treatData(values)) || values;

    if (!actionOptions) {
      return;
    }

    genericAction({
      data,
      url: actionOptions.url,
      method: actionOptions.method,
      api: actionOptions.api,

      resourceType: type,
      resourceId: id,
      parentType,
      parentId,

      success: actionOptions.success,
      callback: (): void => {
        const redirect = actionOptions.redirect;

        if (redirect) {
          load({
            resourceType: type,
            resourceId: id,
            // can be undefined
            parentType,
            parentId,
          });

          history.push({
            pathname: `/${config.resourcePath}/${type}/${id}/${
              redirect === 'update' ? redirect : 'detail'
            }`,
            search: location.search,
          });
        }
      },
    });
  };

  /**
   * Add / Edit a sub object of the resource
   */
  submitSubData = values => {
    const {
      update,
      loadApollo,
      updateApollo,
      match: { params },
      history,
    } = this.props;

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

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

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

    if (!subDataOptions) return;

    // Update subdata from resource using rosource update api
    if (subDataOptions.apollo) {
      // gestion apollo
      updateApollo({
        options: subDataOptions,
        success: subDataOptions.successUpdate,
        data: {
          [subData]: values,
        },
        subResource: true,
        // send id if
        id: values[config.idKey],
        callback: (): void => {
          history.goBack();
          loadApollo({
            id,
            options: resourceOptions,
          });
        },
      });
      return;
    }

    update({
      data: {
        [subData]: values,
      },
      parentType,
      parentId,
      resourceId: params.id,
      resourceType: type,

      success: subDataOptions.successUpdate,
      callback: (): void => {
        // // reload
        // load({
        //   resourceType: type,
        //   id: params.id,
        // });
        history.goBack();
      },
    });
  };

  /**
   * Add / Edit an element of a list of the resource
   */
  submitListData = values => {
    const {
      detail,
      add,
      update,
      load,
      match: { params },
      addApollo,
      updateApollo,
      loadApollo,
      history,
    } = this.props;

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

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

    const {
      index,
      type,
      id,
      parentType,
      resourceOptions,
      parentId,
      listData,
      data,
      listDataOptions,
    } = pageInfo;

    if (!listDataOptions) return;

    const isEdit = values[config.idKey] || !isEmpty(data);

    console.log('listDataOptions', listDataOptions, resourceOptions);
    if (listDataOptions.apollo) {
      if (listDataOptions.separatedApi) {
        if (isEdit) {
          updateApollo({
            options: listDataOptions,
            success: listDataOptions.successUpdate,
            data: values,
            subResource: true,
            // send id if
            id: values[config.idKey],
            inputs: {
              [listDataOptions.parent]: id,
            },
            callback: (): void => {
              history.goBack();
              loadApollo({
                id,
                options: resourceOptions,
              });
            },
          });
          return;
        }
        addApollo({
          options: listDataOptions,
          success: listDataOptions.successCreate,
          data: values,
          subResource: true,
          inputs: {
            [listDataOptions.parent]: id,
          },
          callback: (): void => {
            history.goBack();
            loadApollo({
              id,
              options: resourceOptions,
            });
          },
        });
      }
      return;
    }
    if (resourceOptions.apollo) {
      // Case direct addition
      const resourceData = detail && detail.data;
      let toSend = clone(resourceData[listData]);

      if (index >= 0 && toSend) toSend[index] = values;
      else if (!index) {
        toSend = toSend || [];
        toSend.push(values);
      }

      updateApollo({
        options: resourceOptions,
        success: resourceOptions.successUpdate,
        id,
        data: {
          ...resourceData,
          [listData]: toSend,
        },
        callback: (): void => {
          history.goBack();
        },
      });
      return;
    }

    if (listDataOptions.separatedApi) {
      const goListing = () => {
        if (listDataOptions.separated) {
          load({
            resourceType: listDataOptions.resourceName || type,
            resourceId: id,
            parentType,
            parentId,
          });
        } else {
          load({
            resourceType: type,
            resourceId: id,
          });
        }
        history.push({
          pathname: `/${config.resourcePath}/${type}/${id}/listData/${listData}`,
          search: location.search,
        });
      };

      if (values[config.idKey]) {
        update({
          parentType: type,
          parentId: params.id,
          resourceType: listDataOptions.resourceName || listData,
          resourceId: values[config.idKey],
          data: values,
          callback: goListing,
        });
      } else {
        add({
          parentType: type,
          parentId: params.id,
          resourceType: listDataOptions.resourceName || listData,
          data: values,
          callback: goListing,
        });
      }
      return;
    }

    // Case direct addition
    const resourceData = detail && detail.data;
    let toSend = clone(resourceData[listData]);

    if (index >= 0 && toSend) toSend[index] = values;
    else if (!index) {
      toSend = toSend || [];
      toSend.push(values);
    }
    update({
      data: {
        ...resourceData,
        [listData]: toSend,
      },
      resourceId: params.id,
      resourceType: type,
      parentType,
      parentId,
      success: listDataOptions.successUpdate,
      callback: (): void => {
        history.goBack();
      },
    });
  };

  submit = (values: Record<string, any>): void => {
    const {
      add,
      addApollo,
      resetList,
      match: { params },
      dialogShow,
      history,
    } = this.props;

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

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

    const {
      type,
      actionOptions,
      listDataOptions,
      subDataOptions,
      resourceOptions,
      parentType,
      parentId,
    } = pageInfo;

    if (actionOptions) {
      this.submitAction(values);
      return;
    }
    if (subDataOptions) {
      this.submitSubData(values);
      return;
    }
    if (listDataOptions) {
      this.submitListData(values);
      return;
    }

    if (!resourceOptions) return;

    // Add of the resource
    const data = (resourceOptions.treatData && resourceOptions.treatData(values)) || values;

    const resourceId = params.id;
    const resourceType = type;

    const handleAdd = () => {
      if (resourceOptions.apollo) {
        addApollo({
          options: resourceOptions,
          success: resourceOptions.successCreate,
          data,
          callback: (): void => {
            resetList();
            history.goBack();
          },
        });
        return;
      }

      add({
        resourceType,
        resourceId,
        parentType,
        parentId,
        success: resourceOptions.successCreate,
        data,
        callback: (): void => {
          resetList();
          history.goBack();
        },
      });
    };

    if (resourceOptions.confirmOnSave) {
      dialogShow({
        id: 'confirm',
        title: 'Save item',
        messages: 'Do you confirm you want to add this element ?',
        action: {
          value: handleAdd,
        },
      });
    } else {
      handleAdd();
    }
  };

  render(): JSX {
    const {
      match: { params },
      location,
    } = this.props;

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

    if (typeof pageInfo === 'string') return <span>{pageInfo}</span>;

    const {
      modelOptions,
      data,
      subData,
      listData,
      resourceData,
      transformedData,
      dataOptions,
      fields,
    } = pageInfo;

    // Use for initialValues for no edit mode
    const search = qs.parse(location.search);
    let initialValues = search;
    if (modelOptions && modelOptions.parseParams) initialValues = modelOptions.parseParams(search);
    if (location?.state?.initialValues) initialValues = location?.state?.initialValues;

    // Page title
    let isEdit = (data && data[config.idKey]) || !isEmpty(data);
    if (location?.state?.isEdit !== undefined) isEdit = location?.state?.isEdit;

    const pageTitle = this.getLocationTitle(modelOptions, data, isEdit);

    return (
      <div className={styles('Manage')}>
        {(!params.id || data) && (
          <Form
            component={GenericForm}
            legend={
              !dataOptions &&
              modelOptions?.legend && (
                <DetailLegend legend={modelOptions.legend} data={transformedData} />
              )
            }
            mutators={{ ...arrayMutators }}
            noHeader={params.id && !subData && !listData}
            displayId={modelOptions && modelOptions.displayId}
            title={pageTitle}
            dataOptions={dataOptions}
            allOpened={isEmpty(data)}
            initialValues={params.id ? data : initialValues}
            fields={fields}
            resourceData={resourceData}
            onSubmit={this.submit}
          />
        )}
      </div>
    );
  }
}
