/**
 * Copyright 2018-present Facebook.
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 * @format
 */
import {
  FlexColumn,
  Button,
  styled,
  colors,
  Text,
  LoadingIndicator,
  FlexRow,
  Spacer,
} from 'flipper';
import React, {Component} from 'react';
import {setExportStatusComponent, unsetShare} from '../reducers/application';
import {reportPlatformFailures} from '../utils/metrics';
import CancellableExportStatus from './CancellableExportStatus';
import {performance} from 'perf_hooks';
import {Logger} from '../fb-interfaces/Logger';
import {Idler} from '../utils/Idler';
import {
  exportStoreToFile,
  EXPORT_FLIPPER_TRACE_EVENT,
} from '../utils/exportData';
import PropTypes from 'prop-types';
import ShareSheetErrorList from './ShareSheetErrorList';

const Container = styled(FlexColumn)({
  padding: 20,
  width: 500,
});

const Center = styled(FlexColumn)({
  alignItems: 'center',
  paddingTop: 50,
  paddingBottom: 50,
});

const Uploading = styled(Text)({
  marginTop: 15,
});

const ErrorMessage = styled(Text)({
  display: 'block',
  marginTop: 6,
  wordBreak: 'break-all',
  whiteSpace: 'pre-line',
  lineHeight: 1.35,
});

const Title = styled(Text)({
  marginBottom: 6,
});

const InfoText = styled(Text)({
  lineHeight: 1.35,
  marginBottom: 15,
});

type Props = {
  onHide: () => any;
  file: string | null | undefined;
  logger: Logger;
};

type State = {
  errorArray: Array<Error>;
  result:
    | {
        success: boolean;
        error: Error | null | undefined;
      }
    | null
    | undefined;
  statusUpdate: string | null | undefined;
  runInBackground: boolean;
};

export default class ShareSheetExportFile extends Component<Props, State> {
  static contextTypes = {
    store: PropTypes.object.isRequired,
  };

  state: State = {
    errorArray: [],
    result: null,
    statusUpdate: null,
    runInBackground: false,
  };

  idler = new Idler();

  dispatchAndUpdateToolBarStatus(msg: string) {
    this.context.store.dispatch(
      setExportStatusComponent(
        <CancellableExportStatus
          msg={msg}
          onCancel={() => {
            this.idler.cancel();
            this.context.store.dispatch(unsetShare());
          }}
        />,
      ),
    );
  }

  async componentDidMount() {
    const mark = 'shareSheetExportFile';
    performance.mark(mark);
    try {
      if (!this.props.file) {
        return;
      }
      const {errorArray} = await reportPlatformFailures(
        exportStoreToFile(
          this.props.file,
          this.context.store,
          this.idler,
          (msg: string) => {
            if (this.state.runInBackground) {
              this.dispatchAndUpdateToolBarStatus(msg);
            } else {
              this.setState({statusUpdate: msg});
            }
          },
        ),
        `${EXPORT_FLIPPER_TRACE_EVENT}:UI_FILE`,
      );
      this.context.store.dispatch(unsetShare());
      if (this.state.runInBackground) {
        new Notification('Sharable Flipper trace created', {
          body: `Flipper trace exported to the ${this.props.file}`,
          requireInteraction: true,
        });
        return;
      }
      this.setState({errorArray, result: {success: true, error: null}});
      this.props.logger.trackTimeSince(mark, 'export:file-success');
    } catch (err) {
      if (!this.state.runInBackground) {
        this.setState({errorArray: [], result: {success: false, error: err}});
      }
      this.props.logger.trackTimeSince(mark, 'export:file-error');
    }
  }

  render() {
    const onHide = () => {
      this.context.store.dispatch(unsetShare());
      this.props.onHide();
      this.idler.cancel();
    };

    if (!this.props.file) {
      return this.renderNoFileError(onHide);
    }
    const {result, statusUpdate} = this.state;
    if (result) {
      const {success, error} = result;
      if (success) {
        return (
          <Container>
            <FlexColumn>
              <Title bold>Data Exported Successfully</Title>
              <InfoText>
                When sharing your Flipper data, consider that the captured data
                might contain sensitive information like access tokens used in
                network requests.
              </InfoText>
              <ShareSheetErrorList errors={this.state.errorArray} />
            </FlexColumn>
            <FlexRow>
              <Spacer />
              <Button compact padded onClick={onHide}>
                Close
              </Button>
            </FlexRow>
          </Container>
        );
      }
      if (error) {
        return (
          <Container>
            <Title bold>Error</Title>
            <ErrorMessage code>
              {(error && error.message) || 'File could not be saved.'}
            </ErrorMessage>
            <FlexRow>
              <Spacer />
              <Button compact padded onClick={onHide}>
                Close
              </Button>
            </FlexRow>
          </Container>
        );
      }
      return null;
    } else {
      return (
        <Container>
          <Center>
            <LoadingIndicator size={30} />
            {statusUpdate && statusUpdate.length > 0 ? (
              <Uploading bold color={colors.macOSTitleBarIcon}>
                {statusUpdate}
              </Uploading>
            ) : (
              <Uploading bold color={colors.macOSTitleBarIcon}>
                Exporting Flipper trace...
              </Uploading>
            )}
          </Center>
          <FlexRow>
            <Spacer />
            <Button
              compact
              padded
              onClick={() => {
                this.setState({runInBackground: true});
                const {statusUpdate} = this.state;
                if (statusUpdate) {
                  this.dispatchAndUpdateToolBarStatus(statusUpdate);
                }
                this.props.onHide();
              }}>
              Run In Background
            </Button>
            <Button compact padded onClick={onHide}>
              Cancel
            </Button>
          </FlexRow>
        </Container>
      );
    }
  }

  renderNoFileError(onHide: () => void) {
    return (
      <Container>
        <Center>
          <Title bold>No file selected</Title>
        </Center>
        <FlexRow>
          <Spacer />
          <Button compact padded onClick={onHide}>
            Close
          </Button>
        </FlexRow>
      </Container>
    );
  }
}
