import PropTypes from 'prop-types';
import React from 'react';
import styled from 'styled-components';
import { bindActionCreators } from 'redux';
import { FormattedMessage, intlShape } from 'react-intl';
import { createStructuredSelector } from 'reselect';
import {
  CapRadioCard,
  CapSpin,
  CapHeading,
  CapHeader,
  CapButton,
  CapIcon,
  CapLink,
  CapRow,
  CapColumn,
  CapInput,
  CapLabel,
  CapTag,
  CapTreeSelect,
  CapDivider,
  CapTooltip,
} from '@capillarytech/cap-ui-library';
import { FONT_SIZE_L } from '@capillarytech/cap-ui-library/styled/variables';
import _, { find, cloneDeep, findIndex, isEmpty, isEqual, filter, replace } from 'lodash';
import * as actions from './actions';
import { makeSelectFTP, makeSelectMetaEntities } from './selectors';
import { makeSelectLoyaltyPromotionDisplay, setInjectedTags } from '../Cap/selectors';
import withCreatives from '../../hoc/withCreatives';
import messages from './messages';
import './_FTP.scss';
import * as globalActions from '../Cap/actions';
import { TagList } from '../TagList';
import { NO_COMMUNICATION, CREATE, EDIT, PREVIEW } from '../App/constants';
import { getTreeStructuredTags } from '../../utils/common';
import injectSaga from '../../utils/injectSaga';
import injectReducer from '../../utils/injectReducer';

import { v2FTPSagas } from './sagas';
import v2FTPReducer from './reducer';

const UIView = {
  serverSelection: 'serverSelection',
  configureMessage: 'configureMessage',
};
const { CapLabelInline } = CapLabel;

const StyledCapIcon = styled(CapIcon)`
  font-size: ${FONT_SIZE_L};
`;

export class FTP extends React.Component {
  constructor(props) {
    super(props);
    this.state = this.getInitialState();
  }
  getInitialState = () => {
    const mode = this.props.mode || CREATE;
    let stateObj = {};
    if (mode === CREATE) {
      const { formatMessage } = this.props.intl;
      const headerTagList = [{
        header: formatMessage(messages.customerID),
        tag: '{{user_id}}',
      }];
      stateObj = {
        mode,
        selectedServer: null,
        viewMode: UIView.serverSelection,
        headerTagList,
        messageContent: '',
        tagsTree: [],
      };
    } else {
      const { messageContent = {} } = this.props.messageDetails || {};
      const { headerTagList = [], messageBody = '' } = messageContent.message_content_id_1 || {};
      stateObj = {
        mode,
        selectedServer: null,
        viewMode: UIView.configureMessage,
        headerTagList,
        messageContent: messageBody,
        tagsTree: [],
      };
    }
    return stateObj;
  };

  setFTPServerInState = (FTPServers) => {
    const { deliverySetting = { channelSetting: {} }} = this.props.messageDetails;
    const configId = (deliverySetting.channelSetting[NO_COMMUNICATION].endPointConfigs || {}).ftpConfigId;
    const selectedServer = find(FTPServers, {id: configId});
    this.setState({ selectedServer });
  };

  componentDidMount() {
    this.props.actions.getFTPServers();
    const query = {
      layout: 'SMS',
      type: 'TAG',
      context: 'outbound',
      embedded: 'embedded',
    };
    //This call is to fetch tags that are populated in csv columns.
    this.props.globalActions.fetchSchemaForEntity(query);
  }

  getTreeStructuredInjectedTags = (tags) => {
    const finalTags = [];
    let temp = {};
    _.forEach(tags, (val, key) => {
      temp = {
        title: !val['tag-header'] ? val.desc : val.name,
        value: `{{${key}}}`,
      };
      if (!val.subtags) {
        finalTags.push(temp);
      } else {
        temp.title = temp.title || val.name;
        temp.selectable = false;
        temp.children = this.getTreeStructuredInjectedTags(val.subtags);
        finalTags.push(temp);
      }
    });
    return finalTags;
  }

  componentWillReceiveProps(nextProps) {
    const { metaEntities = {}, selectedOfferDetails = [], mode, FTP: nextFTP = {}, injectedTags } = nextProps;
    const { metaEntities: thisMetaEntities = {}, FTP: thisFTP = {}, injectedTags: thisInjectedTags } = this.props;
    const { tags: thisTags = {} } = thisMetaEntities;
    if (
      (
        !isEmpty((metaEntities.tags || {}).standard) &&
        !isEqual((metaEntities.tags || {}).standard, thisTags.standard)
      ) || !isEqual(injectedTags, thisInjectedTags)
    ) {
      const tags = _.concat(
        getTreeStructuredTags({
          tagsList: ((metaEntities.tags || {}).standard || []),
          offerDetails: selectedOfferDetails,
          disableTagsDetails: this.props.disableTagsDetails,
        }),
        this.getTreeStructuredInjectedTags(injectedTags)
      );
      this.setState({ tagsTree: tags}, () => this.disableUsedColumns());
    }

    if (
      mode === EDIT &&
      nextFTP.FTPServers.length &&
      !isEqual(nextFTP.FTPServers, thisFTP.FTPServers)
    ) {
      this.setFTPServerInState(nextFTP.FTPServers);
    }
  }

  onChangeFTPServer = (e) => {
    const { value } = e.target;
    const { FTPServers = [] } = this.props.FTP || {};
    const selectedServer = find(FTPServers, {id: value});
    this.setState({ selectedServer, viewMode: UIView.configureMessage});
  };

  onTagSelect = (data) => {
    this.insertAtCursor(`{{${data}}}`);
  };

  setInputRef = (textarea) => {
    this.textArea = textarea;
  };

  getServerSelectionContent = () => {
    const { getFTPServersInProgress } = this.props.FTP || {};
    const { formatMessage } = this.props.intl;
    const serverOptions = this.getServerOptions();
    const { selectedServer } = this.state;
    return (
      <CapSpin spinning={getFTPServersInProgress}>
        <CapHeading
          type="h3"
          className="select-account-header"
        >
          {formatMessage(messages.ftpServer)}
        </CapHeading>
        {!!serverOptions.length && <CapHeading type="h6" className="manage-ftp-setting-div">
          {formatMessage(messages.ftpServerDesc)}
          <CapLink
            title={formatMessage(messages.manageFTPSettings)}
            className="select-server"
            onClick={this.handleManageFTPSettings}
          />
        </CapHeading>}
        {serverOptions.length ? (
          <CapRadioCard
            className="select-account"
            onChange={this.onChangeFTPServer}
            panes={serverOptions}
            cardHeight="48px"
            cardWidth="276px"
            size="small"
            defaultValue={selectedServer}
            selected={selectedServer}
          />
        ) : (
          <>
            {!getFTPServersInProgress && (
              <CapHeading type="h6" className="configure-ftp-setting-div">
                {formatMessage(messages.selectFtpServerDesc)}
                <CapLink
                  title={formatMessage(messages.manageFTPSettings)}
                  className="select-server"
                  onClick={this.handleManageFTPSettings}
                />
              </CapHeading>
            )}
          </>
        )}
      </CapSpin>
    );
  };

  getServerOptions = () => {
    const { FTPServers = [] } = this.props.FTP || {};
    return (FTPServers).map(({
      // eslint-disable-next-line camelcase
      ftp_tag = '',
      id,
    }) => ({
      key: ftp_tag,
      value: id,
      title: ftp_tag,
      icon: <CapIcon.CapIconAvatar text={ftp_tag[0]} width="auto" height="2rem"/>,
    }));
  };

  indexOfEnd(targetString, string) {
    const io = targetString.indexOf(string);
    return io == -1 ? -1 : io + string.length;
  }

  getMessageContent = () => {
    const { messageContent } = this.state;
    const { formatMessage } = this.props.intl;
    const { metaEntities, selectedOfferDetails, injectedTags } = this.props;
    const tagsRaw = metaEntities && metaEntities.tags ? metaEntities.tags.standard : [];
    return (
      <CapRow>
        <CapColumn span={11}>
          <div className="message-content-title">
            <TagList
              className="FTP-tagList"
              label={formatMessage(messages.addLabel)}
              tags={tagsRaw}
              onTagSelect={this.onTagSelect}
              selectedOfferDetails={selectedOfferDetails}
              injectedTags={injectedTags || {}}
            />
            <CapInput.TextArea
              setInputRef={this.setInputRef}
              label={formatMessage(messages.messageHeader)}
              onChange={this.updateMessageBody}
              value={messageContent}
            />
          </div>
        </CapColumn>
      </CapRow>
    );
  };

  getCsvColumns = () => {
    const { headerTagList = [], tagsTree = [] } = this.state;
    const { formatMessage } = this.props.intl;
    const tagsWithoutOptout = tagsTree.length && tagsTree.filter((tag) => tag.value !== '{{optout}}');
    const headerTagElements = headerTagList.map((item, index) => (
      index !== 0 && <div className="column-selection-container">
        <CapTooltip title={item.header}>
          <CapTreeSelect
            dropdownClassName="select-csv-col-dropdown"
            treeData={tagsWithoutOptout}
            className="select-csv-column"
            value={item.header}
            onChange={(option, label) => this.selectCsvColumn(option, label, index)}
            showSearch
            searchPlaceholder={
              <span className="search-placeholder-container">
                <StyledCapIcon type="search" className="csv-column-search-icon"/>
                {formatMessage(messages.search)}
              </span>
            }
            placeholder={formatMessage(messages.selectTag)}
          />
          <CapIcon
            type="close"
            size="m"
            onClick={() => this.removeCsvColumn(item)}
            className="remove-csv-column"
          />
        </CapTooltip>
      </div>
    ));
    headerTagElements.unshift(
      <CapLabelInline
        className="column-selection-container-cid"
        type="label2"
        style={{fontSize: 14, fontWeight: 500}}
      >
        <FormattedMessage {...messages.customerID}/>
      </CapLabelInline>
    );
    return headerTagElements;
  };

  getConfigureCsvContent = () => {
    const { formatMessage } = this.props.intl;
    return (
      <CapSpin spinning={!(this.state.tagsTree || []).length}>
        <CapRow className="configure-csv-container">
          <CapColumn span={8} className="configure-csv">
            <CapHeader
              title={formatMessage(messages.configureCsv)}
              size="regular"
              className="select-account-header"
            />
          </CapColumn>
          <CapColumn span={3} offset={13} className="add-column">
            <CapButton
              type="flat"
              isAddBtn
              onClick={this.addNewColumn}
            >
              <FormattedMessage {...messages.addColumn} />
            </CapButton>
          </CapColumn>
        </CapRow>
        {this.getCsvColumns()}
      </CapSpin>
    );
  }

  getConfigureMessageContent = () => {
    const { formatMessage } = this.props.intl;
    const { selectedServer = {}, headerTagList = [] } = this.state;
    const emptyHeaderTagList = filter(headerTagList, {tag: ''});
    const disableDoneButton = isEmpty(selectedServer) || emptyHeaderTagList.length;
    return (
      <CapSpin spinning={false}>
        <div className="scrollable-section">
          <CapHeader
            title={formatMessage(messages.ftpServer)}
            size="regular"
            className="select-account-header"
          />
          <div className="selected-server-name">
            <CapIcon.CapIconAvatar
              className="server-avtar"
              text={((selectedServer || {}).ftp_tag || [])[0]}
              width="auto"
              height="2rem"
            />
            <CapHeading
              type="h4"
              className="server-name"
            >
              {(selectedServer || {}).ftp_tag}
            </CapHeading>
            <CapLink
              title={formatMessage(messages.change)}
              className="server-change"
              onClick={this.showAccountSelection}
            />
          </div>
          <CapDivider />
          {this.getConfigureCsvContent()}
          <CapDivider />
          <div className="ftp-message-container">
            <CapHeader
              title={formatMessage(messages.messageContent)}
              description={formatMessage(messages.optional)}
              size="regular"
              inline
            />
            {this.getMessageContent()}
          </div>
        </div>
        <CapButton
          type="primary"
          disabled={disableDoneButton}
          onClick={this.saveFtpMessage}
        >
          <FormattedMessage {...messages.done} />
        </CapButton>
      </CapSpin>
    );
  };

  getColumnsHeaderPreview = (headerTagList) => (
    <div className="ftp-preview-data-col-container">
      { headerTagList.map((tag) => (
        <CapTag type="static" className="ftp-preview-data-col-tag">
          {tag.header}
        </CapTag>
      ))}
    </div>
  );

  getPreviewModeContents = () => {
    const { contents = [] } = this.props;
    const { messageBody = '', headerTagList = [] } = contents[0] || {};
    const columns = cloneDeep(headerTagList);
    if (messageBody) {
      columns.push({
        header: <FormattedMessage {...messages.message}/>,
      });
    }
    return (
      <div className="ftp-preview-container">
        <CapHeading className="ftp-preview-data-title" type="h3">
          <FormattedMessage {...messages.dataPreview}/>
        </CapHeading>
        {this.getColumnsHeaderPreview(columns)}
        {messageBody && (
          <>
            <CapHeading className="ftp-preview-message-title" type="h3">
              <FormattedMessage {...messages.messageContent}/>
            </CapHeading>
            <CapLabel className="ftp-content" type="label1">{messageBody}</CapLabel>
          </>
        )}
      </div>
    );
  };

  showAccountSelection = () => {
    this.setState({ viewMode: UIView.serverSelection, selectedServer: null});
  };

  selectCsvColumn = (option, label, index) => {
    const { headerTagList } = this.state;
    const clonedHeaderTagList = cloneDeep(headerTagList);
    const optionValue = option.substring(this.indexOfEnd(option, '{{'), option.indexOf('}}'));
    let  readableOptionValue = replace(optionValue, /_/g, ' ');
    readableOptionValue = readableOptionValue[0].toUpperCase() + readableOptionValue.slice(1);
    const obj = {
      header: readableOptionValue,
      tag: option,
    };
    clonedHeaderTagList[index] = obj;
    this.setState({ headerTagList: clonedHeaderTagList}, () => this.disableUsedColumns());
  };

  disableUsedColumns = () => {
    const { headerTagList = [] } = this.state;
    const { selectedOfferDetails = [], metaEntities = {}, injectedTags, disableTagsDetails } = this.props;
    const tags = _.concat(
      getTreeStructuredTags({
        tagsList: ((metaEntities.tags || {}).standard || []),
        offerDetails: selectedOfferDetails,
        disableTagsDetails,
      }),
      this.getTreeStructuredInjectedTags(injectedTags)
    );
    const clonnedTagsTree = cloneDeep(tags);
    headerTagList.map((headerTag) => {
      clonnedTagsTree.map((tag, index) => {
        if (!isEmpty(tag.children)) { //if we have subtags it will first resolve subtag values.
          const subTags = this.disableUsedSubTag(tag.children, headerTag);
          clonnedTagsTree[index] = {
            ...clonnedTagsTree[index],
            children: subTags,
          };
        } else { // If first level itself is leaf node then I will match the usedtag value and disable it accordingly.
          const pos = findIndex(clonnedTagsTree, {value: headerTag.tag});
          if (pos > -1) {
            clonnedTagsTree[pos] = {
              disabled: true,
              ...clonnedTagsTree[pos],
            };
          }
        }
      });
    });
    this.setState({tagsTree: clonnedTagsTree});
  };

  disableUsedSubTag = (subTags, headerTag) => {
    const clonnedSubTags = cloneDeep(subTags);
    clonnedSubTags.map((tag, index) => {
      if (!isEmpty(tag.children)) { //Using recursion here to dig deep into further level if they have subtags.
        //if we still have subtags, it will call same function to resolved deepest subtag value first.
        const subTags1 = this.disableUsedSubTag(tag.children, headerTag);
        clonnedSubTags[index] = {
          ...clonnedSubTags[index],
          children: subTags1,
        };
      } else if (isEqual(tag.value, headerTag.tag)) { // when Leaf node, it will match usedtag value and disable usedtags from tags tree.
        clonnedSubTags[index] = {
          disabled: true,
          ...clonnedSubTags[index],
        };
      }
      return null;
    });
    return clonnedSubTags;
  }

  handleManageFTPSettings = () => {
    const path = `/exports/configurations`;
    window.open(path, '_blank');
  };

  removeCsvColumn = (item) => {
    const { headerTagList } = this.state;
    const clonedHeaderTagList = cloneDeep(headerTagList);
    const index = findIndex(headerTagList, (val) => val.tag === item.tag);
    clonedHeaderTagList.splice(index, 1);
    this.setState({ headerTagList: clonedHeaderTagList}, () => this.disableUsedColumns());
  };

  addNewColumn = () => {
    const { headerTagList } = this.state;
    const clonedHeaderTagList = cloneDeep(headerTagList);
    const newCsvObj = {
      header: '',
      tag: '',
    };
    clonedHeaderTagList.push(newCsvObj);
    this.setState({ headerTagList: clonedHeaderTagList});
  };

  insertAtCursor = (myValue) => {
    this.textArea.focus();
    const textAreaRef = this.textArea.textAreaRef;
    let pos = textAreaRef.selectionEnd + myValue.length;
    //IE support
    let newMessage = textAreaRef.value + myValue;
    if (document.selection) {
      this.textArea.focus();
      const sel = document.selection.createRange();
      sel.text = myValue;
    } else if (textAreaRef.selectionStart || textAreaRef.selectionStart === '0') { //MOZILLA and others
      const startPos = textAreaRef.selectionStart;
      const endPos = textAreaRef.selectionEnd;
      newMessage = textAreaRef.value.substring(0, startPos) + myValue + textAreaRef.value.substring(endPos, textAreaRef.value.length);
      pos = startPos + myValue.length;
    }
    this.setState({
      messageContent: newMessage,
    }, () => {
      textAreaRef.selectionStart = pos;
      textAreaRef.selectionEnd = pos;
    });
  };

  updateMessageBody = (e) => {
    this.setState({
      messageContent: e.target.value,
    });
  };

  saveFtpMessage = () => {
    const { onFTPSubmit } = this.props;
    const { FTPServers = [] } = this.props.FTP || {};
    const { selectedServer = {}, headerTagList = [], messageContent = '' } = this.state;
    onFTPSubmit({
      channel: "FTP",
      endPointConfigs: {
        endPointType: "FTP",
        ftpConfigId: selectedServer.id,
      },
      headerTagList,
      messageContent,
      FTPServers,
    });
  };

  render() {
    const { viewMode } = this.state;
    const { serverSelection } = UIView;
    const { mode } = this.props;
    return (
      mode === PREVIEW ?
        this.getPreviewModeContents() :
        (viewMode === serverSelection ?
          this.getServerSelectionContent() :
          this.getConfigureMessageContent()
        )
    );
  }
}

FTP.propTypes = {
  actions: PropTypes.object.isRequired,
  FTP: PropTypes.object,
  intl: intlShape.isRequired,
  globalActions: PropTypes.object,
  onFTPSubmit: PropTypes.func,
  messageDetails: PropTypes.object,
  mode: PropTypes.string,
  contents: PropTypes.array,
  metaEntities: PropTypes.object,
  selectedOfferDetails: PropTypes.array,
  disableTagsDetails: PropTypes.func,
};


function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(actions, dispatch),
    globalActions: bindActionCreators(globalActions, dispatch),
  };
}

const mapStateToProps = createStructuredSelector({
  FTP: makeSelectFTP(),
  metaEntities: makeSelectMetaEntities(),
  injectedTags: setInjectedTags(),
  disableTagsDetails: makeSelectLoyaltyPromotionDisplay(),
});


const withSaga = injectSaga({ key: 'FTP', saga: v2FTPSagas });
const withReducer = injectReducer({ key: 'FTP', reducer: v2FTPReducer });

export default withCreatives({
  WrappedComponent: FTP,
  mapStateToProps,
  mapDispatchToProps,
  userAuth: true,
  sagas: [withSaga],
  reducers: [withReducer],
});
