import React, { ChangeEvent } from 'react';
import { QRCode } from 'react-qr-svg';

import { SessionModal } from './session/SessionModal';
import { SessionButton } from './session/SessionButton';
import { SessionSpinner } from './session/SessionSpinner';

interface Props {
  onClose: any;
}

interface State {
  currentPubKey: string | null;
  accepted: boolean;
  pubKeyRequests: Array<any>;
  currentView: 'filterRequestView' | 'qrcodeView';
  errors: any;
  loading: boolean;
  deviceAlias: string | null;
}

export class DevicePairingDialog extends React.Component<Props, State> {
  constructor(props: any) {
    super(props);

    this.closeDialog = this.closeDialog.bind(this);
    this.onKeyUp = this.onKeyUp.bind(this);
    this.stopReceivingRequests = this.stopReceivingRequests.bind(this);
    this.startReceivingRequests = this.startReceivingRequests.bind(this);
    this.getPubkeyName = this.getPubkeyName.bind(this);
    this.skipDevice = this.skipDevice.bind(this);
    this.allowDevice = this.allowDevice.bind(this);
    this.validateSecondaryDevice = this.validateSecondaryDevice.bind(this);
    this.handleUpdateDeviceAlias = this.handleUpdateDeviceAlias.bind(this);

    this.state = {
      currentPubKey: null,
      accepted: false,
      pubKeyRequests: Array(),
      currentView: 'qrcodeView',
      loading: false,
      errors: undefined,
      deviceAlias: null,
    };
  }

  public componentWillMount() {
    this.startReceivingRequests();
  }

  public componentWillUnmount() {
    this.closeDialog();
  }

  /*
  dialog.on('deviceUnpairingRequested', pubKey =>
    Whisper.events.trigger('deviceUnpairingRequested', pubKey)
  );*/

  public renderFilterRequestsView() {
    const { currentPubKey, accepted, deviceAlias } = this.state;
    const secretWords = window.mnemonic.pubkey_to_secret_words(currentPubKey);
    const deviceAliasPlaceholder = this.getPubkeyName(currentPubKey);
    const deviceName = deviceAliasPlaceholder.deviceAlias;

    if (accepted) {
      return (
        <SessionModal
          title={window.i18n('provideDeviceAlias')}
          onOk={() => null}
          onClose={this.closeDialog}
        >
          <div className="session-modal__centered">
            <input onChange={this.handleUpdateDeviceAlias}>{deviceName}</input>
            <div className="session-modal__button-group">
              <SessionButton
                text={window.i18n('ok')}
                onClick={this.validateSecondaryDevice}
                disabled={!deviceAlias}
              />
            </div>
            <SessionSpinner loading={this.state.loading} />
          </div>
        </SessionModal>
      );
    }

    return (
      <SessionModal
        title={window.i18n('allowPairingWithDevice')}
        onOk={() => null}
        onClose={this.closeDialog}
      >
        <div className="session-modal__centered">
          <label>{window.i18n('secretWords')}</label>
          <div className="text-subtle">{secretWords}</div>
          <div className="session-modal__button-group">
            <SessionButton
              text={window.i18n('skip')}
              onClick={this.skipDevice}
            />
            <SessionButton
              text={window.i18n('allowPairing')}
              onClick={this.allowDevice}
            />
          </div>
        </div>
      </SessionModal>
    );
  }

  public renderQrCodeView() {
    const theme = window.Events.getThemeSetting();
    const requestReceived = this.hasReceivedRequests();
    const title = window.i18n('pairingDevice');

    // Foreground equivalent to .session-modal background color
    const bgColor = 'rgba(0, 0, 0, 0)';
    const fgColor = theme === 'dark' ? '#FFFFFF' : '#1B1B1B';

    return (
      <SessionModal title={title} onOk={() => null} onClose={this.closeDialog}>
        <div className="session-modal__centered">
          <h4>{window.i18n('waitingForDeviceToRegister')}</h4>
          <small className="text-subtle">
            {window.i18n('pairNewDevicePrompt')}
          </small>
          <div className="spacer-lg" />

          <div id="qr">
            <QRCode
              value={window.textsecure.storage.user.getNumber()}
              bgColor={bgColor}
              fgColor={fgColor}
              level="L"
            />
          </div>

          <div className="spacer-lg" />
          <div className="session-modal__button-group__center">
            {!requestReceived ? (
              <SessionButton
                text={window.i18n('cancel')}
                onClick={this.closeDialog}
              />
            ) : (
              <div className="session-modal__button-group">
                <SessionButton
                  text={window.i18n('filterReceivedRequests')}
                  onClick={this.stopReceivingRequests}
                />
              </div>
            )}
          </div>
        </div>
      </SessionModal>
    );
  }

  public render() {
    const { currentView } = this.state;
    const renderQrCodeView = currentView === 'qrcodeView';
    const renderFilterRequestView = currentView === 'filterRequestView';

    return (
      <>
        {renderQrCodeView && this.renderQrCodeView()}
        {renderFilterRequestView && this.renderFilterRequestsView()}
      </>
    );
  }

  private getPubkeyName(pubKey: string | null) {
    if (!pubKey) {
      return {};
    }

    const secretWords = window.mnemonic.pubkey_to_secret_words(pubKey);
    const conv = window.ConversationController.get(this.state.currentPubKey);
    const deviceAlias = conv ? conv.getNickname() : 'Unnamed Device';

    return { deviceAlias, secretWords };
  }

  private reset() {
    this.setState({
      currentPubKey: null,
      accepted: false,
      pubKeyRequests: Array(),
      currentView: 'filterRequestView',
      deviceAlias: null,
    });
  }

  private startReceivingRequests() {
    this.reset();
    window.Whisper.events.on(
      'devicePairingRequestReceived',
      (pubKey: string) => {
        this.requestReceived(pubKey);
      }
    );
    this.setState({ currentView: 'qrcodeView' });
  }

  private stopReceivingRequests() {
    this.setState({ currentView: 'filterRequestView' });
    window.Whisper.events.off('devicePairingRequestReceived');
  }

  private requestReceived(secondaryDevicePubKey: string | EventHandlerNonNull) {
    // FIFO: push at the front of the array with unshift()
    this.state.pubKeyRequests.unshift(secondaryDevicePubKey);
    window.pushToast({
      title: window.i18n('gotPairingRequest'),
      description: `${window.shortenPubkey(
        secondaryDevicePubKey
      )} ${window.i18n(
        'showPairingWordsTitle'
      )}: ${window.mnemonic.pubkey_to_secret_words(secondaryDevicePubKey)}`,
    });
    if (!this.state.currentPubKey) {
      this.nextPubKey();
    }
  }

  private allowDevice() {
    this.setState({
      accepted: true,
    });
  }

  private transmissionCB(errors: any) {
    if (!errors) {
      this.setState({
        errors: null,
      });
      this.closeDialog();
      window.pushToast({
        title: window.i18n('devicePairedSuccessfully'),
      });
      const conv = window.ConversationController.get(this.state.currentPubKey);
      if (conv) {
        conv.setNickname(this.state.deviceAlias);
      }

      // FIXME display error somewhere
      // FIXME display list of linked device
      // FIXME do not show linked device in list of contacts

      return;
    }
    /* this.$('.transmissionStatus').text(errors);
    this.$('.requestAcceptedView .ok').show();*/

    this.setState({
      errors: errors,
    });
  }

  private skipDevice() {
    window.Whisper.events.trigger(
      'devicePairingRequestRejected',
      this.state.currentPubKey
    );

    const hasNext = this.state.pubKeyRequests.length > 0;
    this.nextPubKey();
    if (!hasNext) {
      this.startReceivingRequests();
    }
    this.setState({
      currentView: hasNext ? 'filterRequestView' : 'qrcodeView',
    });
  }

  private nextPubKey() {
    // FIFO: pop at the back of the array using pop()
    this.setState({
      currentPubKey: this.state.pubKeyRequests.pop(),
    });
  }

  private onKeyUp(event: any) {
    switch (event.key) {
      case 'Esc':
      case 'Escape':
        this.closeDialog();
        break;
      default:
    }
  }

  private validateSecondaryDevice() {
    this.setState({ loading: true });
    window.Whisper.events.trigger(
      'devicePairingRequestAccepted',
      this.state.currentPubKey,
      (errors: any) => {
        this.transmissionCB(errors);

        return true;
      }
    );
  }

  private hasReceivedRequests() {
    return this.state.currentPubKey || this.state.pubKeyRequests.length > 0;
  }

  private closeDialog() {
    window.removeEventListener('keyup', this.onKeyUp);
    this.stopReceivingRequests();
    window.Whisper.events.off('devicePairingRequestReceived');
    if (this.state.currentPubKey && !this.state.accepted) {
      window.Whisper.events.trigger(
        'devicePairingRequestRejected',
        this.state.currentPubKey
      );
    }
    this.props.onClose();
  }

  private handleUpdateDeviceAlias(value: ChangeEvent<HTMLInputElement>) {
    const trimmed = value.target.value.trim();
    if (!!trimmed) {
      this.setState({ deviceAlias: trimmed });
    } else {
      this.setState({ deviceAlias: null });
    }
  }
}
